From 76e25bad2380fc2763531e7012b648d0cc119779 Mon Sep 17 00:00:00 2001 From: yiliang114 <1204183885@qq.com> Date: Thu, 3 Aug 2023 22:58:20 +0800 Subject: [PATCH 001/223] fix: Close #189556, gulp task do not display task name --- build/lib/compilation.js | 14 ++++++++++---- build/lib/compilation.ts | 14 +++++++++++--- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/build/lib/compilation.js b/build/lib/compilation.js index 2270e054ba5..d15f91891a9 100644 --- a/build/lib/compilation.js +++ b/build/lib/compilation.js @@ -82,17 +82,19 @@ function createCompile(src, build, emitError, transpileOnly) { return pipeline; } function transpileTask(src, out, swc) { - return function () { + const task = () => { const transpile = createCompile(src, false, true, { swc }); const srcPipe = gulp.src(`${src}/**`, { base: `${src}` }); return srcPipe .pipe(transpile()) .pipe(gulp.dest(out)); }; + task.taskName = `transpile-${path.basename(src)}`; + return task; } exports.transpileTask = transpileTask; function compileTask(src, out, build, options = {}) { - return function () { + const task = () => { if (os.totalmem() < 4000000000) { throw new Error('compilation requires 4GB of RAM'); } @@ -128,10 +130,12 @@ function compileTask(src, out, build, options = {}) { .pipe(compile()) .pipe(gulp.dest(out)); }; + task.taskName = `compile-${path.basename(src)}`; + return task; } exports.compileTask = compileTask; function watchTask(out, build) { - return function () { + const task = () => { const compile = createCompile('src', build, false, false); const src = gulp.src('src/**', { base: 'src' }); const watchSrc = watch('src/**', { base: 'src', readDelay: 200 }); @@ -142,6 +146,8 @@ function watchTask(out, build) { .pipe(util.incremental(compile, src, true)) .pipe(gulp.dest(out)); }; + task.taskName = `watch-${path.basename(out)}`; + return task; } exports.watchTask = watchTask; const REPO_SRC_FOLDER = path.join(__dirname, '../../src'); @@ -281,4 +287,4 @@ exports.watchApiProposalNamesTask = task.define('watch-api-proposal-names', () = .pipe(util.debounce(task)) .pipe(gulp.dest('src')); }); -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tcGlsYXRpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJjb21waWxhdGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztnR0FHZ0c7OztBQUVoRyxtQ0FBbUM7QUFDbkMseUJBQXlCO0FBQ3pCLDZCQUE2QjtBQUM3Qiw2QkFBNkI7QUFDN0IsMENBQTBDO0FBQzFDLDZCQUE2QjtBQUM3Qix5Q0FBNEM7QUFDNUMsK0JBQStCO0FBQy9CLHNDQUFzQztBQUN0QywwQ0FBMEM7QUFDMUMseUJBQXlCO0FBQ3pCLGlDQUFrQztBQUNsQyw4QkFBOEI7QUFDOUIsK0JBQStCO0FBQy9CLDBDQUF5QztBQUV6QyxNQUFNLEtBQUssR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUM7QUFHakMsdUVBQXVFO0FBRXZFLE1BQU0sUUFBUSxHQUFHLElBQUEseUJBQWMsR0FBRSxDQUFDO0FBRWxDLFNBQVMsNEJBQTRCLENBQUMsR0FBVztJQUNoRCxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxTQUFTLEdBQUcsRUFBRSxDQUFDLENBQUM7SUFDckQsTUFBTSxPQUFPLEdBQXVCLEVBQUUsQ0FBQztJQUN2QyxPQUFPLENBQUMsT0FBTyxHQUFHLEtBQUssQ0FBQztJQUN4QixPQUFPLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQztJQUN6QixJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMscUJBQXFCLENBQUMsRUFBRSxFQUFFLHNDQUFzQztRQUMvRSxPQUFPLENBQUMsU0FBUyxHQUFHLEtBQUssQ0FBQztLQUMxQjtJQUNELE9BQU8sQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO0lBQzFCLE9BQU8sQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO0lBQzFCLE9BQU8sQ0FBQyxVQUFVLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUM3QyxPQUFPLENBQUMsT0FBTyxHQUFHLE1BQU0sQ0FBQyxJQUFJ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 \ No newline at end of file +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tcGlsYXRpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJjb21waWxhdGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztnR0FHZ0c7OztBQUVoRyxtQ0FBbUM7QUFDbkMseUJBQXlCO0FBQ3pCLDZCQUE2QjtBQUM3Qiw2QkFBNkI7QUFDN0IsMENBQTBDO0FBQzFDLDZCQUE2QjtBQUM3Qix5Q0FBNEM7QUFDNUMsK0JBQStCO0FBQy9CLHNDQUFzQztBQUN0QywwQ0FBMEM7QUFDMUMseUJBQXlCO0FBQ3pCLGlDQUFrQztBQUNsQyw4QkFBOEI7QUFDOUIsK0JBQStCO0FBQy9CLDBDQUF5QztBQUV6QyxNQUFNLEtBQUssR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUM7QUFHakMsdUVBQXVFO0FBRXZFLE1BQU0sUUFBUSxHQUFHLElBQUEseUJBQWMsR0FBRSxDQUFDO0FBRWxDLFNBQVMsNEJBQTRCLENBQUMsR0FBVztJQUNoRCxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxTQUFTLEdBQUcsRUFBRSxDQUFDLENBQUM7SUFDckQsTUFBTSxPQUFPLEdBQXVCLEVBQUUsQ0FBQztJQUN2QyxPQUFPLENBQUMsT0FBTyxHQUFHLEtBQUssQ0FBQztJQUN4QixPQUFPLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQztJQUN6QixJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMscUJBQXFCLENBQUMsRUFBRSxFQUFFLHNDQUFzQztRQUMvRSxPQUFPLENBQUMsU0FBUyxHQUFHLEtBQUssQ0FBQztLQUMxQjtJQUNELE9BQU8sQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO0lBQzFCLE9BQU8sQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO0lBQzFCLE9BQU8sQ0FBQyxVQUFVLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUM3QyxPQUFPLENBQUMsT0FBTyxHQUFHLE1BQU0sQ0FBQyxJQUFJ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 \ No newline at end of file diff --git a/build/lib/compilation.ts b/build/lib/compilation.ts index d5da3f1cd89..ac2fe1bbf5a 100644 --- a/build/lib/compilation.ts +++ b/build/lib/compilation.ts @@ -95,7 +95,7 @@ function createCompile(src: string, build: boolean, emitError: boolean, transpil export function transpileTask(src: string, out: string, swc: boolean): () => NodeJS.ReadWriteStream { - return function () { + const task = () => { const transpile = createCompile(src, false, true, { swc }); const srcPipe = gulp.src(`${src}/**`, { base: `${src}` }); @@ -104,11 +104,14 @@ export function transpileTask(src: string, out: string, swc: boolean): () => Nod .pipe(transpile()) .pipe(gulp.dest(out)); }; + + task.taskName = `transpile-${path.basename(src)}`; + return task; } export function compileTask(src: string, out: string, build: boolean, options: { disableMangle?: boolean } = {}): () => NodeJS.ReadWriteStream { - return function () { + const task = () => { if (os.totalmem() < 4_000_000_000) { throw new Error('compilation requires 4GB of RAM'); @@ -150,11 +153,14 @@ export function compileTask(src: string, out: string, build: boolean, options: { .pipe(compile()) .pipe(gulp.dest(out)); }; + + task.taskName = `compile-${path.basename(src)}`; + return task; } export function watchTask(out: string, build: boolean): () => NodeJS.ReadWriteStream { - return function () { + const task = () => { const compile = createCompile('src', build, false, false); const src = gulp.src('src/**', { base: 'src' }); @@ -168,6 +174,8 @@ export function watchTask(out: string, build: boolean): () => NodeJS.ReadWriteSt .pipe(util.incremental(compile, src, true)) .pipe(gulp.dest(out)); }; + task.taskName = `watch-${path.basename(out)}`; + return task; } const REPO_SRC_FOLDER = path.join(__dirname, '../../src'); From 818ae5029a3792e31804be7ad83e30c42f063eb0 Mon Sep 17 00:00:00 2001 From: yiliang114 <1204183885@qq.com> Date: Tue, 8 Aug 2023 14:01:32 +0800 Subject: [PATCH 002/223] fix: Close #189556, gulp task do not display task name --- build/lib/compilation.js | 2 +- build/lib/compilation.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/lib/compilation.js b/build/lib/compilation.js index d15f91891a9..c4b6dd98af5 100644 --- a/build/lib/compilation.js +++ b/build/lib/compilation.js @@ -287,4 +287,4 @@ exports.watchApiProposalNamesTask = task.define('watch-api-proposal-names', () = .pipe(util.debounce(task)) .pipe(gulp.dest('src')); }); -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tcGlsYXRpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJjb21waWxhdGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztnR0FHZ0c7OztBQUVoRyxtQ0FBbUM7QUFDbkMseUJBQXlCO0FBQ3pCLDZCQUE2QjtBQUM3Qiw2QkFBNkI7QUFDN0IsMENBQTBDO0FBQzFDLDZCQUE2QjtBQUM3Qix5Q0FBNEM7QUFDNUMsK0JBQStCO0FBQy9CLHNDQUFzQztBQUN0QywwQ0FBMEM7QUFDMUMseUJBQXlCO0FBQ3pCLGlDQUFrQztBQUNsQyw4QkFBOEI7QUFDOUIsK0JBQStCO0FBQy9CLDBDQUF5QztBQUV6QyxNQUFNLEtBQUssR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUM7QUFHakMsdUVBQXVFO0FBRXZFLE1BQU0sUUFBUSxHQUFHLElBQUEseUJBQWMsR0FBRSxDQUFDO0FBRWxDLFNBQVMsNEJBQTRCLENBQUMsR0FBVztJQUNoRCxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxTQUFTLEdBQUcsRUFBRSxDQUFDLENBQUM7SUFDckQsTUFBTSxPQUFPLEdBQXVCLEVBQUUsQ0FBQztJQUN2QyxPQUFPLENBQUMsT0FBTyxHQUFHLEtBQUssQ0FBQztJQUN4QixPQUFPLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQztJQUN6QixJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMscUJBQXFCLENBQUMsRUFBRSxFQUFFLHNDQUFzQztRQUMvRSxPQUFPLENBQUMsU0FBUyxHQUFHLEtBQUssQ0FBQztLQUMxQjtJQUNELE9BQU8sQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO0lBQzFCLE9BQU8sQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO0lBQzFCLE9BQU8sQ0FBQyxVQUFVLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUM3QyxPQUFPLENBQUMsT0FBTyxHQUFHLE1BQU0sQ0FBQyxJQUFJ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 \ No newline at end of file +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tcGlsYXRpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJjb21waWxhdGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztnR0FHZ0c7OztBQUVoRyxtQ0FBbUM7QUFDbkMseUJBQXlCO0FBQ3pCLDZCQUE2QjtBQUM3Qiw2QkFBNkI7QUFDN0IsMENBQTBDO0FBQzFDLDZCQUE2QjtBQUM3Qix5Q0FBNEM7QUFDNUMsK0JBQStCO0FBQy9CLHNDQUFzQztBQUN0QywwQ0FBMEM7QUFDMUMseUJBQXlCO0FBQ3pCLGlDQUFrQztBQUNsQyw4QkFBOEI7QUFDOUIsK0JBQStCO0FBQy9CLDBDQUF5QztBQUV6QyxNQUFNLEtBQUssR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUM7QUFFakMsdUVBQXVFO0FBRXZFLE1BQU0sUUFBUSxHQUFHLElBQUEseUJBQWMsR0FBRSxDQUFDO0FBRWxDLFNBQVMsNEJBQTRCLENBQUMsR0FBVztJQUNoRCxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxTQUFTLEdBQUcsRUFBRSxDQUFDLENBQUM7SUFDckQsTUFBTSxPQUFPLEdBQXVCLEVBQUUsQ0FBQztJQUN2QyxPQUFPLENBQUMsT0FBTyxHQUFHLEtBQUssQ0FBQztJQUN4QixPQUFPLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQztJQUN6QixJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMscUJBQXFCLENBQUMsRUFBRSxFQUFFLHNDQUFzQztRQUMvRSxPQUFPLENBQUMsU0FBUyxHQUFHLEtBQUssQ0FBQztLQUMxQjtJQUNELE9BQU8sQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO0lBQzFCLE9BQU8sQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO0lBQzFCLE9BQU8sQ0FBQyxVQUFVLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUM3QyxPQUFPLENBQUMsT0FBTyxHQUFHLE1BQU0sQ0FBQyxJQUFJ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 \ No newline at end of file diff --git a/build/lib/compilation.ts b/build/lib/compilation.ts index ac2fe1bbf5a..cf2ab921f1c 100644 --- a/build/lib/compilation.ts +++ b/build/lib/compilation.ts @@ -93,7 +93,7 @@ function createCompile(src: string, build: boolean, emitError: boolean, transpil return pipeline; } -export function transpileTask(src: string, out: string, swc: boolean): () => NodeJS.ReadWriteStream { +export function transpileTask(src: string, out: string, swc: boolean): task.StreamTask { const task = () => { @@ -109,7 +109,7 @@ export function transpileTask(src: string, out: string, swc: boolean): () => Nod return task; } -export function compileTask(src: string, out: string, build: boolean, options: { disableMangle?: boolean } = {}): () => NodeJS.ReadWriteStream { +export function compileTask(src: string, out: string, build: boolean, options: { disableMangle?: boolean } = {}): task.StreamTask { const task = () => { @@ -158,7 +158,7 @@ export function compileTask(src: string, out: string, build: boolean, options: { return task; } -export function watchTask(out: string, build: boolean): () => NodeJS.ReadWriteStream { +export function watchTask(out: string, build: boolean): task.StreamTask { const task = () => { const compile = createCompile('src', build, false, false); From a4a254bd5de8cc68301c5ed8967269072f2e4c07 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Thu, 10 Aug 2023 20:35:56 -0600 Subject: [PATCH 003/223] Add basic intellisense for chat variables --- .../browser/contrib/chatInputEditorContrib.ts | 51 +++++++++++++++++++ .../common/extensionsApiProposals.ts | 1 + .../vscode.proposed.chatVariables.d.ts | 21 ++++++++ .../vscode.proposed.interactive.d.ts | 9 +++- 4 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 src/vscode-dts/vscode.proposed.chatVariables.d.ts diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts index 801519bd549..829ebe5b2be 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts @@ -25,6 +25,20 @@ import { IChatService } from 'vs/workbench/contrib/chat/common/chatService'; import { SlashCommandContentWidget } from 'vs/workbench/contrib/chat/browser/chatSlashCommandContentWidget'; import { SubmitAction } from 'vs/workbench/contrib/chat/browser/actions/chatExecuteActions'; + +const variables = [ + { name: 'selection', description: `The current editor's selection` }, + { name: 'editor', description: 'The current editor' }, + { name: 'debugConsole', description: 'The output in the debug console' }, + { name: 'vscodeAPI', description: 'The docs for the vscode extension API' }, + { name: 'git', description: 'The git details for your workspace' }, + { name: 'problems', description: 'The problems detected in your workspace' }, + { name: 'terminal', description: 'The current terminal buffer' }, + { name: 'terminalSelection', description: 'The current selection in the terminal' }, + { name: 'workspace', description: 'Details of your workspace' }, + { name: 'vscode', description: 'Commands and settings in vscode' }, +]; + const decorationDescription = 'chat'; const slashCommandPlaceholderDecorationType = 'chat-session-detail'; const slashCommandTextDecorationType = 'chat-session-text'; @@ -242,3 +256,40 @@ class SlashCommandCompletions extends Disposable { } Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(SlashCommandCompletions, LifecyclePhase.Eventually); + +class VariableCompletions extends Disposable { + constructor( + @ILanguageFeaturesService private readonly languageFeaturesService: ILanguageFeaturesService, + @IChatWidgetService private readonly chatWidgetService: IChatWidgetService, + ) { + super(); + + this._register(this.languageFeaturesService.completionProvider.register({ scheme: ChatInputPart.INPUT_SCHEME, hasAccessToAllModels: true }, { + _debugDisplayName: 'chatVariables', + triggerCharacters: ['@'], + provideCompletionItems: async (model: ITextModel, position: Position, _context: CompletionContext, _token: CancellationToken) => { + // TODO Why does this get invoked on every keypress, not just after @? + + const widget = this.chatWidgetService.getWidgetByInputUri(model.uri); + if (!widget) { + return null; + } + + return { + suggestions: variables.map(v => { + const withAt = `@${v.name}`; + return { + label: withAt, + range: new Range(position.lineNumber, position.column - 1, position.lineNumber, position.column - 1), + insertText: withAt + ' ', + detail: v.description, + kind: CompletionItemKind.Text, // The icons are disabled here anyway, + }; + }) + }; + } + })); + } +} + +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(VariableCompletions, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 19fe88c4ea3..de730088c46 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -13,6 +13,7 @@ export const allApiProposals = Object.freeze({ chatProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatProvider.d.ts', chatRequestAccess: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatRequestAccess.d.ts', chatSlashCommands: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatSlashCommands.d.ts', + chatVariables: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatVariables.d.ts', codiconDecoration: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.codiconDecoration.d.ts', commentsDraftState: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.commentsDraftState.d.ts', contribCommentEditorActionsMenu: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribCommentEditorActionsMenu.d.ts', diff --git a/src/vscode-dts/vscode.proposed.chatVariables.d.ts b/src/vscode-dts/vscode.proposed.chatVariables.d.ts new file mode 100644 index 00000000000..bb3380301e3 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.chatVariables.d.ts @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + interface ResolvedVariable { + content: string; + } + + interface ChatVariableResolver { + resolve(name: string): ResolvedVariable; + } + + // Could be provider/resolver pattern, but how dynamic are they? + export namespace chat { + // name: selection + export function registerVariable(name: string, description: string, resolver: ChatVariableResolver): void; + } +} diff --git a/src/vscode-dts/vscode.proposed.interactive.d.ts b/src/vscode-dts/vscode.proposed.interactive.d.ts index a294597ec6a..31017b88217 100644 --- a/src/vscode-dts/vscode.proposed.interactive.d.ts +++ b/src/vscode-dts/vscode.proposed.interactive.d.ts @@ -105,9 +105,16 @@ declare module 'vscode' { args: any; } + export interface ResolvedVariableInRequest { + name: string; + content: string; + } + + export type ParsedRequest = (string | ResolvedVariableInRequest)[]; + export interface InteractiveRequest { session: InteractiveSession; - message: string | InteractiveSessionReplyFollowup; + message: ParsedRequest | InteractiveSessionReplyFollowup; } export interface InteractiveResponseErrorDetails { From 4c9d0b8ff79e9489b6fd91885c43db13f1dfcde6 Mon Sep 17 00:00:00 2001 From: Johannes Date: Fri, 11 Aug 2023 09:41:53 +0200 Subject: [PATCH 004/223] when invoking the VariableCompletions provider explicitly check for `@word` at position --- .../browser/contrib/chatInputEditorContrib.ts | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts index 829ebe5b2be..10dbc27e91a 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts @@ -24,6 +24,7 @@ import { ChatInputPart } from 'vs/workbench/contrib/chat/browser/chatInputPart'; import { IChatService } from 'vs/workbench/contrib/chat/common/chatService'; import { SlashCommandContentWidget } from 'vs/workbench/contrib/chat/browser/chatSlashCommandContentWidget'; import { SubmitAction } from 'vs/workbench/contrib/chat/browser/actions/chatExecuteActions'; +import { getWordAtText } from 'vs/editor/common/core/wordHelper'; const variables = [ @@ -258,6 +259,9 @@ class SlashCommandCompletions extends Disposable { Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(SlashCommandCompletions, LifecyclePhase.Eventually); class VariableCompletions extends Disposable { + + private static readonly VariableNameDef = /@\w*/g; // MUST be using `g`-flag + constructor( @ILanguageFeaturesService private readonly languageFeaturesService: ILanguageFeaturesService, @IChatWidgetService private readonly chatWidgetService: IChatWidgetService, @@ -268,19 +272,33 @@ class VariableCompletions extends Disposable { _debugDisplayName: 'chatVariables', triggerCharacters: ['@'], provideCompletionItems: async (model: ITextModel, position: Position, _context: CompletionContext, _token: CancellationToken) => { - // TODO Why does this get invoked on every keypress, not just after @? const widget = this.chatWidgetService.getWidgetByInputUri(model.uri); if (!widget) { return null; } + const varWord = getWordAtText(position.column, VariableCompletions.VariableNameDef, model.getLineContent(position.lineNumber), 0); + if (!varWord && model.getWordUntilPosition(position).word) { + // inside a "normal" word + return null; + } + + let insert: Range; + let replace: Range; + if (!varWord) { + insert = replace = Range.fromPositions(position); + } else { + insert = new Range(position.lineNumber, varWord.startColumn, position.lineNumber, position.column); + replace = new Range(position.lineNumber, varWord.startColumn, position.lineNumber, varWord.endColumn); + } + return { suggestions: variables.map(v => { const withAt = `@${v.name}`; return { label: withAt, - range: new Range(position.lineNumber, position.column - 1, position.lineNumber, position.column - 1), + range: { insert, replace }, insertText: withAt + ' ', detail: v.description, kind: CompletionItemKind.Text, // The icons are disabled here anyway, From 552ff617e2620b90ddb1f45b0bb478b09bf82961 Mon Sep 17 00:00:00 2001 From: Johannes Date: Fri, 11 Aug 2023 10:12:34 +0200 Subject: [PATCH 005/223] add chat variables service and hook up with completions --- .../contrib/chat/browser/chat.contribution.ts | 2 + .../browser/contrib/chatInputEditorContrib.ts | 17 +--- .../contrib/chat/common/chatVariables.ts | 84 +++++++++++++++++++ 3 files changed, 89 insertions(+), 14 deletions(-) create mode 100644 src/vs/workbench/contrib/chat/common/chatVariables.ts diff --git a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts index 6d78034a68c..ccbda802d19 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts @@ -49,6 +49,7 @@ import { IMarkdownString } from 'vs/base/common/htmlContent'; import { ChatProviderService, IChatProviderService } from 'vs/workbench/contrib/chat/common/chatProvider'; import { ChatSlashCommandService, IChatSlashCommandService } from 'vs/workbench/contrib/chat/common/chatSlashCommands'; import { ICommandService } from 'vs/platform/commands/common/commands'; +import { ChatVariablesService, IChatVariablesService } from 'vs/workbench/contrib/chat/common/chatVariables'; // Register configuration const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); @@ -258,3 +259,4 @@ registerSingleton(IChatAccessibilityService, ChatAccessibilityService, Instantia registerSingleton(IChatWidgetHistoryService, ChatWidgetHistoryService, InstantiationType.Delayed); registerSingleton(IChatProviderService, ChatProviderService, InstantiationType.Delayed); registerSingleton(IChatSlashCommandService, ChatSlashCommandService, InstantiationType.Delayed); +registerSingleton(IChatVariablesService, ChatVariablesService, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts index 10dbc27e91a..39daad0d0af 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts @@ -25,21 +25,9 @@ import { IChatService } from 'vs/workbench/contrib/chat/common/chatService'; import { SlashCommandContentWidget } from 'vs/workbench/contrib/chat/browser/chatSlashCommandContentWidget'; import { SubmitAction } from 'vs/workbench/contrib/chat/browser/actions/chatExecuteActions'; import { getWordAtText } from 'vs/editor/common/core/wordHelper'; +import { IChatVariablesService } from 'vs/workbench/contrib/chat/common/chatVariables'; -const variables = [ - { name: 'selection', description: `The current editor's selection` }, - { name: 'editor', description: 'The current editor' }, - { name: 'debugConsole', description: 'The output in the debug console' }, - { name: 'vscodeAPI', description: 'The docs for the vscode extension API' }, - { name: 'git', description: 'The git details for your workspace' }, - { name: 'problems', description: 'The problems detected in your workspace' }, - { name: 'terminal', description: 'The current terminal buffer' }, - { name: 'terminalSelection', description: 'The current selection in the terminal' }, - { name: 'workspace', description: 'Details of your workspace' }, - { name: 'vscode', description: 'Commands and settings in vscode' }, -]; - const decorationDescription = 'chat'; const slashCommandPlaceholderDecorationType = 'chat-session-detail'; const slashCommandTextDecorationType = 'chat-session-text'; @@ -265,6 +253,7 @@ class VariableCompletions extends Disposable { constructor( @ILanguageFeaturesService private readonly languageFeaturesService: ILanguageFeaturesService, @IChatWidgetService private readonly chatWidgetService: IChatWidgetService, + @IChatVariablesService private readonly chatVariablesService: IChatVariablesService, ) { super(); @@ -294,7 +283,7 @@ class VariableCompletions extends Disposable { } return { - suggestions: variables.map(v => { + suggestions: Array.from(this.chatVariablesService.getVariables()).map(v => { const withAt = `@${v.name}`; return { label: withAt, diff --git a/src/vs/workbench/contrib/chat/common/chatVariables.ts b/src/vs/workbench/contrib/chat/common/chatVariables.ts new file mode 100644 index 00000000000..7da1ebafcdb --- /dev/null +++ b/src/vs/workbench/contrib/chat/common/chatVariables.ts @@ -0,0 +1,84 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CancellationToken } from 'vs/base/common/cancellation'; +import { Iterable } from 'vs/base/common/iterator'; +import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +export interface IChatVariableData { + name: string; + description: string; +} + +export interface IChatResolvedVariable { + // TODO should we allow for multiple levels + value: string; +} + +export interface IChatVariableResolver { + // TODO should we spec "zoom level" + (token: CancellationToken): IChatResolvedVariable; +} + +export const IChatVariablesService = createDecorator('IChatVariablesService'); + +export interface IChatVariablesService { + _serviceBrand: undefined; + registerChatVariable(data: IChatVariableData, resolver: IChatVariableResolver): IDisposable; + resolveVariable(name: string, token: CancellationToken): Promise; + getVariables(): Iterable>; +} + +type ChatData = [data: IChatVariableData, resolver: IChatVariableResolver]; + +export class ChatVariablesService implements IChatVariablesService { + + declare _serviceBrand: undefined; + + private _resolver = new Map(); + + constructor() { + [ + { name: 'selection', description: `The current editor's selection` }, + { name: 'editor', description: 'The current editor' }, + { name: 'debugConsole', description: 'The output in the debug console' }, + { name: 'vscodeAPI', description: 'The docs for the vscode extension API' }, + { name: 'git', description: 'The git details for your workspace' }, + { name: 'problems', description: 'The problems detected in your workspace' }, + { name: 'terminal', description: 'The current terminal buffer' }, + { name: 'terminalSelection', description: 'The current selection in the terminal' }, + { name: 'workspace', description: 'Details of your workspace' }, + { name: 'vscode', description: 'Commands and settings in vscode' }, + ].forEach(item => { + this.registerChatVariable(item, () => ({ value: item.name })); + }); + } + + getVariables(): Iterable> { + return Iterable.map(this._resolver.values(), data => data[0]); + } + + registerChatVariable(data: IChatVariableData, resolver: IChatVariableResolver): IDisposable { + const key = data.name.toLowerCase(); + if (this._resolver.has(key)) { + throw new Error(`A chat variable with the name '${data.name}' already exists.`); + } + this._resolver.set(key, [data, resolver]); + return toDisposable(() => { + this._resolver.delete(key); + }); + } + + resolveVariable(name: string, token: CancellationToken): Promise { + const key = name.toLowerCase(); + const chatData = this._resolver.get(key); + if (!chatData) { + throw new Error(`A chat variable with the name '${name}' does not exist.`); + } + const [, resolver] = chatData; + return Promise.resolve(resolver(token)); + } +} From 2cb4a66f87faa57335b8edafeb968daa4ab57019 Mon Sep 17 00:00:00 2001 From: Johannes Date: Fri, 11 Aug 2023 11:15:57 +0200 Subject: [PATCH 006/223] implement and wire-up chatVariables proposal --- .../api/browser/extensionHost.contribution.ts | 1 + .../api/browser/mainThreadChatVariables.ts | 38 +++++++++++++++ .../workbench/api/common/extHost.api.impl.ts | 6 +++ .../workbench/api/common/extHost.protocol.ts | 12 +++++ .../api/common/extHostChatVariables.ts | 48 +++++++++++++++++++ .../contrib/chat/common/chatVariables.ts | 10 ++-- .../vscode.proposed.chatVariables.d.ts | 8 ++-- 7 files changed, 114 insertions(+), 9 deletions(-) create mode 100644 src/vs/workbench/api/browser/mainThreadChatVariables.ts create mode 100644 src/vs/workbench/api/common/extHostChatVariables.ts diff --git a/src/vs/workbench/api/browser/extensionHost.contribution.ts b/src/vs/workbench/api/browser/extensionHost.contribution.ts index 6328cfd2df9..774cc0cc158 100644 --- a/src/vs/workbench/api/browser/extensionHost.contribution.ts +++ b/src/vs/workbench/api/browser/extensionHost.contribution.ts @@ -21,6 +21,7 @@ import './mainThreadLocalization'; import './mainThreadBulkEdits'; import './mainThreadChatProvider'; import './mainThreadChatSlashCommands'; +import './mainThreadChatVariables'; import './mainThreadCodeInsets'; import './mainThreadCLICommands'; import './mainThreadClipboard'; diff --git a/src/vs/workbench/api/browser/mainThreadChatVariables.ts b/src/vs/workbench/api/browser/mainThreadChatVariables.ts new file mode 100644 index 00000000000..d2d8902ccbd --- /dev/null +++ b/src/vs/workbench/api/browser/mainThreadChatVariables.ts @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { DisposableMap } from 'vs/base/common/lifecycle'; +import { ExtHostChatVariablesShape, ExtHostContext, MainContext, MainThreadChatVariablesShape } from 'vs/workbench/api/common/extHost.protocol'; +import { IChatVariableData, IChatVariablesService } from 'vs/workbench/contrib/chat/common/chatVariables'; +import { IExtHostContext, extHostNamedCustomer } from 'vs/workbench/services/extensions/common/extHostCustomers'; + +@extHostNamedCustomer(MainContext.MainThreadChatVariables) +export class MainThreadChatSlashCommands implements MainThreadChatVariablesShape { + + private readonly _proxy: ExtHostChatVariablesShape; + private readonly _variables = new DisposableMap(); + + constructor( + extHostContext: IExtHostContext, + @IChatVariablesService private readonly _chatVariablesService: IChatVariablesService, + ) { + this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostChatVariables); + } + + dispose(): void { + this._variables.clearAndDisposeAll(); + } + + $registerVariable(handle: number, data: IChatVariableData): void { + const registration = this._chatVariablesService.registerChatVariable(data, token => { + return this._proxy.$resolveVariable(handle, token); + }); + this._variables.set(handle, registration); + } + + $unregisterVariable(handle: number): void { + this._variables.deleteAndDispose(handle); + } +} diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 383e886c8c1..9328804fda1 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -106,6 +106,7 @@ import { IExtHostManagedSockets } from 'vs/workbench/api/common/extHostManagedSo import { ExtHostShare } from 'vs/workbench/api/common/extHostShare'; import { ExtHostChatProvider } from 'vs/workbench/api/common/extHostChatProvider'; import { ExtHostChatSlashCommands } from 'vs/workbench/api/common/extHostChatSlashCommand'; +import { ExtHostChatVariables } from 'vs/workbench/api/common/extHostChatVariables'; export interface IExtensionRegistries { mine: ExtensionDescriptionRegistry; @@ -207,6 +208,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostInteractiveEditor = rpcProtocol.set(ExtHostContext.ExtHostInlineChat, new ExtHostInteractiveEditor(rpcProtocol, extHostCommands, extHostDocuments, extHostLogService)); const extHostChatProvider = rpcProtocol.set(ExtHostContext.ExtHostChatProvider, new ExtHostChatProvider(rpcProtocol, extHostLogService)); const extHostChatSlashCommands = rpcProtocol.set(ExtHostContext.ExtHostChatSlashCommands, new ExtHostChatSlashCommands(rpcProtocol, extHostChatProvider, extHostLogService)); + const extHostChatVariables = rpcProtocol.set(ExtHostContext.ExtHostChatVariables, new ExtHostChatVariables(rpcProtocol)); const extHostChat = rpcProtocol.set(ExtHostContext.ExtHostChat, new ExtHostChat(rpcProtocol, extHostLogService)); const extHostSemanticSimilarity = rpcProtocol.set(ExtHostContext.ExtHostSemanticSimilarity, new ExtHostSemanticSimilarity(rpcProtocol)); const extHostIssueReporter = rpcProtocol.set(ExtHostContext.ExtHostIssueReporter, new ExtHostIssueReporter(rpcProtocol)); @@ -1345,6 +1347,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I requestChatAccess(id: string) { checkProposedApiEnabled(extension, 'chatRequestAccess'); return extHostChatProvider.requestChatResponseProvider(extension.identifier, id); + }, + registerVariable(name, description, resolver) { + checkProposedApiEnabled(extension, 'chatVariables'); + return extHostChatVariables.registerVariableResolver(extension, name, description, resolver); } }; diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 76565ab6f14..49356b070ca 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -76,6 +76,7 @@ import { ISaveProfileResult } from 'vs/workbench/services/userDataProfile/common import { TerminalCommandMatchResult, TerminalQuickFixCommand, TerminalQuickFixOpener } from 'vscode'; import { IChatMessage, IChatResponseFragment, IChatResponseProviderMetadata } from 'vs/workbench/contrib/chat/common/chatProvider'; import { IChatSlashFragment } from 'vs/workbench/contrib/chat/common/chatSlashCommands'; +import { IChatResolvedVariable, IChatVariableData } from 'vs/workbench/contrib/chat/common/chatVariables'; export type TerminalQuickFix = TerminalQuickFixCommand | TerminalQuickFixOpener; @@ -1149,6 +1150,15 @@ export interface ExtHostChatSlashCommandsShape { $executeCommand(handle: number, requestId: number, prompt: string, context: { history: IChatMessage[] }, token: CancellationToken): Promise; } +export interface MainThreadChatVariablesShape extends IDisposable { + $registerVariable(handle: number, data: IChatVariableData): void; + $unregisterVariable(handle: number): void; +} + +export interface ExtHostChatVariablesShape { + $resolveVariable(handle: number, token: CancellationToken): Promise; +} + export interface MainThreadInlineChatShape extends IDisposable { $registerInteractiveEditorProvider(handle: number, label: string, debugName: string, supportsFeedback: boolean): Promise; $handleProgressChunk(requestId: string, chunk: { message?: string; edits?: languages.TextEdit[] }): Promise; @@ -2556,6 +2566,7 @@ export const MainContext = { MainThreadBulkEdits: createProxyIdentifier('MainThreadBulkEdits'), MainThreadChatProvider: createProxyIdentifier('MainThreadChatProvider'), MainThreadChatSlashCommands: createProxyIdentifier('MainThreadChatSlashCommands'), + MainThreadChatVariables: createProxyIdentifier('MainThreadChatVariables'), MainThreadClipboard: createProxyIdentifier('MainThreadClipboard'), MainThreadCommands: createProxyIdentifier('MainThreadCommands'), MainThreadComments: createProxyIdentifier('MainThreadComments'), @@ -2675,6 +2686,7 @@ export const ExtHostContext = { ExtHostInlineChat: createProxyIdentifier('ExtHostInlineChatShape'), ExtHostChat: createProxyIdentifier('ExtHostChat'), ExtHostChatSlashCommands: createProxyIdentifier('ExtHostChatSlashCommands'), + ExtHostChatVariables: createProxyIdentifier('ExtHostChatVariables'), ExtHostChatProvider: createProxyIdentifier('ExtHostChatProvider'), ExtHostSemanticSimilarity: createProxyIdentifier('ExtHostSemanticSimilarity'), ExtHostTheming: createProxyIdentifier('ExtHostTheming'), diff --git a/src/vs/workbench/api/common/extHostChatVariables.ts b/src/vs/workbench/api/common/extHostChatVariables.ts new file mode 100644 index 00000000000..ac11bd3515f --- /dev/null +++ b/src/vs/workbench/api/common/extHostChatVariables.ts @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import type * as vscode from 'vscode'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { ExtHostChatVariablesShape, IMainContext, MainContext, MainThreadChatVariablesShape } from 'vs/workbench/api/common/extHost.protocol'; +import { IChatResolvedVariable, IChatVariableData } from 'vs/workbench/contrib/chat/common/chatVariables'; +import { onUnexpectedExternalError } from 'vs/base/common/errors'; + +export class ExtHostChatVariables implements ExtHostChatVariablesShape { + + private static _idPool = 0; + + private readonly _resolver = new Map(); + private readonly _proxy: MainThreadChatVariablesShape; + + constructor(mainContext: IMainContext) { + this._proxy = mainContext.getProxy(MainContext.MainThreadChatVariables); + } + + async $resolveVariable(handle: number, token: CancellationToken): Promise { + const item = this._resolver.get(handle); + if (!item) { + return undefined; + } + try { + return (await item.resolver.resolve(item.data.name, token)) ?? undefined; + } catch (err) { + onUnexpectedExternalError(err); + return undefined; + } + } + + registerVariableResolver(extension: IExtensionDescription, name: string, description: string, resolver: vscode.ChatVariableResolver): IDisposable { + const handle = ExtHostChatVariables._idPool++; + this._resolver.set(handle, { extension: extension.identifier, data: { name, description }, resolver: resolver }); + this._proxy.$registerVariable(handle, { name, description }); + + return toDisposable(() => { + this._resolver.delete(handle); + this._proxy.$unregisterVariable(handle); + }); + } +} diff --git a/src/vs/workbench/contrib/chat/common/chatVariables.ts b/src/vs/workbench/contrib/chat/common/chatVariables.ts index 7da1ebafcdb..d8cde94c682 100644 --- a/src/vs/workbench/contrib/chat/common/chatVariables.ts +++ b/src/vs/workbench/contrib/chat/common/chatVariables.ts @@ -15,12 +15,12 @@ export interface IChatVariableData { export interface IChatResolvedVariable { // TODO should we allow for multiple levels - value: string; + content: string; } export interface IChatVariableResolver { // TODO should we spec "zoom level" - (token: CancellationToken): IChatResolvedVariable; + (token: CancellationToken): Promise; } export const IChatVariablesService = createDecorator('IChatVariablesService'); @@ -28,7 +28,7 @@ export const IChatVariablesService = createDecorator('ICh export interface IChatVariablesService { _serviceBrand: undefined; registerChatVariable(data: IChatVariableData, resolver: IChatVariableResolver): IDisposable; - resolveVariable(name: string, token: CancellationToken): Promise; + resolveVariable(name: string, token: CancellationToken): Promise; getVariables(): Iterable>; } @@ -53,7 +53,7 @@ export class ChatVariablesService implements IChatVariablesService { { name: 'workspace', description: 'Details of your workspace' }, { name: 'vscode', description: 'Commands and settings in vscode' }, ].forEach(item => { - this.registerChatVariable(item, () => ({ value: item.name })); + this.registerChatVariable(item, async () => ({ content: item.name })); }); } @@ -72,7 +72,7 @@ export class ChatVariablesService implements IChatVariablesService { }); } - resolveVariable(name: string, token: CancellationToken): Promise { + resolveVariable(name: string, token: CancellationToken): Promise { const key = name.toLowerCase(); const chatData = this._resolver.get(key); if (!chatData) { diff --git a/src/vscode-dts/vscode.proposed.chatVariables.d.ts b/src/vscode-dts/vscode.proposed.chatVariables.d.ts index bb3380301e3..38f6c1947dc 100644 --- a/src/vscode-dts/vscode.proposed.chatVariables.d.ts +++ b/src/vscode-dts/vscode.proposed.chatVariables.d.ts @@ -5,17 +5,17 @@ declare module 'vscode' { - interface ResolvedVariable { + export interface ChatVariableValue { content: string; } - interface ChatVariableResolver { - resolve(name: string): ResolvedVariable; + export interface ChatVariableResolver { + resolve(name: string, token: CancellationToken): ProviderResult; } // Could be provider/resolver pattern, but how dynamic are they? export namespace chat { // name: selection - export function registerVariable(name: string, description: string, resolver: ChatVariableResolver): void; + export function registerVariable(name: string, description: string, resolver: ChatVariableResolver): Disposable; } } From 1e8e7251a959a17e8ac253e55acd6f15df0df296 Mon Sep 17 00:00:00 2001 From: Johannes Date: Fri, 11 Aug 2023 15:03:06 +0200 Subject: [PATCH 007/223] add `resolveVariables` to resolve all variables of a prompt --- .../api/browser/mainThreadChatVariables.ts | 2 +- .../contrib/chat/common/chatVariables.ts | 38 +++++++++++-- .../chat/test/common/chatVariables.test.ts | 54 +++++++++++++++++++ 3 files changed, 88 insertions(+), 6 deletions(-) create mode 100644 src/vs/workbench/contrib/chat/test/common/chatVariables.test.ts diff --git a/src/vs/workbench/api/browser/mainThreadChatVariables.ts b/src/vs/workbench/api/browser/mainThreadChatVariables.ts index d2d8902ccbd..eaea216e529 100644 --- a/src/vs/workbench/api/browser/mainThreadChatVariables.ts +++ b/src/vs/workbench/api/browser/mainThreadChatVariables.ts @@ -26,7 +26,7 @@ export class MainThreadChatSlashCommands implements MainThreadChatVariablesShape } $registerVariable(handle: number, data: IChatVariableData): void { - const registration = this._chatVariablesService.registerChatVariable(data, token => { + const registration = this._chatVariablesService.registerVariable(data, token => { return this._proxy.$resolveVariable(handle, token); }); this._variables.set(handle, registration); diff --git a/src/vs/workbench/contrib/chat/common/chatVariables.ts b/src/vs/workbench/contrib/chat/common/chatVariables.ts index d8cde94c682..4320135a54e 100644 --- a/src/vs/workbench/contrib/chat/common/chatVariables.ts +++ b/src/vs/workbench/contrib/chat/common/chatVariables.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { CancellationToken } from 'vs/base/common/cancellation'; +import { onUnexpectedExternalError } from 'vs/base/common/errors'; import { Iterable } from 'vs/base/common/iterator'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; @@ -27,15 +28,21 @@ export const IChatVariablesService = createDecorator('ICh export interface IChatVariablesService { _serviceBrand: undefined; - registerChatVariable(data: IChatVariableData, resolver: IChatVariableResolver): IDisposable; - resolveVariable(name: string, token: CancellationToken): Promise; + registerVariable(data: IChatVariableData, resolver: IChatVariableResolver): IDisposable; getVariables(): Iterable>; + + /** + * Resolves all variables that occur in `prompt` + */ + resolveVariables(prompt: string, token: CancellationToken): Promise>; + + // TODO is this needed? + resolveVariable(name: string, token: CancellationToken): Promise; } type ChatData = [data: IChatVariableData, resolver: IChatVariableResolver]; export class ChatVariablesService implements IChatVariablesService { - declare _serviceBrand: undefined; private _resolver = new Map(); @@ -53,15 +60,36 @@ export class ChatVariablesService implements IChatVariablesService { { name: 'workspace', description: 'Details of your workspace' }, { name: 'vscode', description: 'Commands and settings in vscode' }, ].forEach(item => { - this.registerChatVariable(item, async () => ({ content: item.name })); + this.registerVariable(item, async () => ({ content: item.name })); }); } + async resolveVariables(prompt: string, token: CancellationToken): Promise> { + const resolvedVariables = new Map(); + const jobs: Promise[] = []; + + for (const [data, resolver] of this._resolver.values()) { + // (^|\s)@foo(\s|$) + const regex = new RegExp(`(^|\\s)@${data.name}(\\s|$)`, 'i'); + if (regex.test(prompt)) { + jobs.push(resolver(token).then(value => { + if (value) { + resolvedVariables.set(data.name, value); + } + }).catch(onUnexpectedExternalError)); + } + } + + await Promise.allSettled(jobs); + + return Promise.resolve(resolvedVariables); + } + getVariables(): Iterable> { return Iterable.map(this._resolver.values(), data => data[0]); } - registerChatVariable(data: IChatVariableData, resolver: IChatVariableResolver): IDisposable { + registerVariable(data: IChatVariableData, resolver: IChatVariableResolver): IDisposable { const key = data.name.toLowerCase(); if (this._resolver.has(key)) { throw new Error(`A chat variable with the name '${data.name}' already exists.`); diff --git a/src/vs/workbench/contrib/chat/test/common/chatVariables.test.ts b/src/vs/workbench/contrib/chat/test/common/chatVariables.test.ts new file mode 100644 index 00000000000..4cfa9defedf --- /dev/null +++ b/src/vs/workbench/contrib/chat/test/common/chatVariables.test.ts @@ -0,0 +1,54 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { ChatVariablesService } from 'vs/workbench/contrib/chat/common/chatVariables'; + +suite('ChatVariables', function () { + + let service: ChatVariablesService; + + setup(function () { + service = new ChatVariablesService(); + }); + + + test('ChatVariables - resolveVariables', async function () { + service.registerVariable({ name: 'foo', description: 'bar' }, async () => ({ content: 'farboo' })); + service.registerVariable({ name: 'far', description: 'boo' }, async () => ({ content: 'farboo' })); + + { + const data = await service.resolveVariables('Hello @foo and@far', CancellationToken.None); + assert.strictEqual(data.size, 1); + assert.deepEqual([...data.keys()].sort(), ['foo']); + } + { + const data = await service.resolveVariables('@foo Hello', CancellationToken.None); + assert.strictEqual(data.size, 1); + assert.deepEqual([...data.keys()].sort(), ['foo']); + } + { + const data = await service.resolveVariables('Hello @foo', CancellationToken.None); + assert.strictEqual(data.size, 1); + assert.deepEqual([...data.keys()].sort(), ['foo']); + } + { + const data = await service.resolveVariables('Hello @foo and@far @foo', CancellationToken.None); + assert.strictEqual(data.size, 1); + assert.deepEqual([...data.keys()].sort(), ['foo']); + } + { + const data = await service.resolveVariables('Hello @foo and @far @foo', CancellationToken.None); + assert.strictEqual(data.size, 2); + assert.deepEqual([...data.keys()].sort(), ['far', 'foo']); + } + { + const data = await service.resolveVariables('Hello @foo and @far @foo @unknown', CancellationToken.None); + assert.strictEqual(data.size, 2); + assert.deepEqual([...data.keys()].sort(), ['far', 'foo']); + } + }); +}); From 205b572538104f69ed1b794f840bae18b7461f2b Mon Sep 17 00:00:00 2001 From: Johannes Date: Fri, 11 Aug 2023 15:10:02 +0200 Subject: [PATCH 008/223] loop over @-word instead of registered variables --- .../contrib/chat/common/chatVariables.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/chat/common/chatVariables.ts b/src/vs/workbench/contrib/chat/common/chatVariables.ts index 4320135a54e..a59bac1bd63 100644 --- a/src/vs/workbench/contrib/chat/common/chatVariables.ts +++ b/src/vs/workbench/contrib/chat/common/chatVariables.ts @@ -68,13 +68,16 @@ export class ChatVariablesService implements IChatVariablesService { const resolvedVariables = new Map(); const jobs: Promise[] = []; - for (const [data, resolver] of this._resolver.values()) { - // (^|\s)@foo(\s|$) - const regex = new RegExp(`(^|\\s)@${data.name}(\\s|$)`, 'i'); - if (regex.test(prompt)) { - jobs.push(resolver(token).then(value => { + const regex = /(^|\s)@(\w+)(\s|$)/ig; + + let match: RegExpMatchArray | null; + while (match = regex.exec(prompt)) { + const candidate = match[2]; + const data = this._resolver.get(candidate); + if (data) { + jobs.push(data[1](token).then(value => { if (value) { - resolvedVariables.set(data.name, value); + resolvedVariables.set(candidate, value); } }).catch(onUnexpectedExternalError)); } From d4241aa6788c2605194de73e7b5fe349be1cfc72 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 11 Aug 2023 12:01:46 -0600 Subject: [PATCH 009/223] Add basic decorations for variables --- .../browser/contrib/chatInputEditorContrib.ts | 46 +++++++++++++++---- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts index 4215f77a5ae..8e84be5d439 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Disposable } from 'vs/base/common/lifecycle'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; +import { getWordAtText } from 'vs/editor/common/core/wordHelper'; import { IDecorationOptions } from 'vs/editor/common/editorCommon'; import { CompletionContext, CompletionItem, CompletionItemKind, CompletionList } from 'vs/editor/common/languages'; import { ITextModel } from 'vs/editor/common/model'; @@ -17,20 +17,22 @@ import { localize } from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { inputPlaceholderForeground } from 'vs/platform/theme/common/colorRegistry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { IChatWidget, IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat'; -import { ChatWidget } from 'vs/workbench/contrib/chat/browser/chatWidget'; -import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import { ChatInputPart } from 'vs/workbench/contrib/chat/browser/chatInputPart'; -import { IChatService } from 'vs/workbench/contrib/chat/common/chatService'; -import { SlashCommandContentWidget } from 'vs/workbench/contrib/chat/browser/chatSlashCommandContentWidget'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { SubmitAction } from 'vs/workbench/contrib/chat/browser/actions/chatExecuteActions'; -import { getWordAtText } from 'vs/editor/common/core/wordHelper'; +import { IChatWidget, IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat'; +import { ChatInputPart } from 'vs/workbench/contrib/chat/browser/chatInputPart'; +import { SlashCommandContentWidget } from 'vs/workbench/contrib/chat/browser/chatSlashCommandContentWidget'; +import { ChatWidget } from 'vs/workbench/contrib/chat/browser/chatWidget'; +import { chatSlashCommandBackground, chatSlashCommandForeground } from 'vs/workbench/contrib/chat/common/chatColors'; +import { IChatService } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatVariablesService } from 'vs/workbench/contrib/chat/common/chatVariables'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; const decorationDescription = 'chat'; const slashCommandPlaceholderDecorationType = 'chat-session-detail'; const slashCommandTextDecorationType = 'chat-session-text'; +const variableTextDecorationType = 'chat-variable-text'; class InputEditorDecorations extends Disposable { @@ -42,6 +44,7 @@ class InputEditorDecorations extends Disposable { @ICodeEditorService private readonly codeEditorService: ICodeEditorService, @IThemeService private readonly themeService: IThemeService, @IChatService private readonly chatService: IChatService, + // @IChatVariablesService private readonly chatVariablesService: IChatVariablesService, ) { super(); @@ -64,6 +67,7 @@ class InputEditorDecorations extends Disposable { } private updateRegisteredDecorationTypes() { + this.codeEditorService.removeDecorationType(variableTextDecorationType); this.codeEditorService.removeDecorationType(slashCommandTextDecorationType); this._slashCommandContentWidget?.hide(); this.codeEditorService.registerDecorationType(decorationDescription, slashCommandTextDecorationType, { @@ -72,6 +76,12 @@ class InputEditorDecorations extends Disposable { contentText: ' ', } }); + const theme = this.themeService.getColorTheme(); + this.codeEditorService.registerDecorationType(decorationDescription, variableTextDecorationType, { + color: theme.getColor(chatSlashCommandForeground)?.toString(), + backgroundColor: theme.getColor(chatSlashCommandBackground)?.toString(), + borderRadius: '3px' + }); this.updateInputEditorDecorations(); } @@ -165,6 +175,26 @@ class InputEditorDecorations extends Disposable { } else { this.widget.inputEditor.setDecorationsByType(decorationDescription, slashCommandTextDecorationType, []); } + + // const variables = this.chatVariablesService.getVariables(); + const variableReg = /(^|\s)@(\w+)(\s|$)/ig; + let match: RegExpMatchArray | null; + const varDecorations: IDecorationOptions[] = []; + while (match = variableReg.exec(inputValue)) { + // const candidate = match[2]; + // if (Iterable.find(variables, v => v.name === candidate)) + const atOffset = match[0].indexOf('@'); + varDecorations.push({ + range: { + startLineNumber: 1, + endLineNumber: 1, + startColumn: match.index! + atOffset + 1, + endColumn: match.index! + match[0].length + 1 + } + }); + } + + this.widget.inputEditor.setDecorationsByType(decorationDescription, variableTextDecorationType, varDecorations); } } From ea912c1b3986fd63dccec5c34ed36dcd2c718b2a Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 11 Aug 2023 12:33:41 -0700 Subject: [PATCH 010/223] start on acc xterm --- .../terminal/common/capabilities/capabilities.ts | 2 ++ .../common/capabilities/commandDetectionCapability.ts | 10 ++++++++++ .../contrib/terminal/browser/terminalInstance.ts | 8 ++++++++ 3 files changed, 20 insertions(+) diff --git a/src/vs/platform/terminal/common/capabilities/capabilities.ts b/src/vs/platform/terminal/common/capabilities/capabilities.ts index 552b1811639..624d986ad6d 100644 --- a/src/vs/platform/terminal/common/capabilities/capabilities.ts +++ b/src/vs/platform/terminal/common/capabilities/capabilities.ts @@ -157,6 +157,8 @@ export interface ICommandDetectionCapability { readonly onCommandExecuted: Event; readonly onCommandInvalidated: Event; readonly onCurrentCommandInvalidated: Event; + readonly onRequestWriteToTextArea: Event; + setCwd(value: string): void; setIsWindowsPty(value: boolean): void; setIsCommandStorageDisabled(): void; diff --git a/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts b/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts index c9005bff55f..d79ebc5718b 100644 --- a/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts +++ b/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts @@ -110,6 +110,8 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe readonly onCommandInvalidated = this._onCommandInvalidated.event; private readonly _onCurrentCommandInvalidated = this._register(new Emitter()); readonly onCurrentCommandInvalidated = this._onCurrentCommandInvalidated.event; + private readonly _onRequestWriteToTextArea = this._register(new Emitter()); + readonly onRequestWriteToTextArea = this._onRequestWriteToTextArea.event; constructor( private readonly _terminal: Terminal, @@ -145,6 +147,14 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe // // This is mostly a workaround for Windows but applies to all OS' because of the tsc watch // case. + let line = this._terminal.buffer.active.getLine(this._terminal.buffer.active.cursorY)?.translateToString(true); + if (this._currentCommand.commandStartX) { + line = line?.substring(this._currentCommand.commandStartX).trimEnd(); + } + if (line) { + console.log(line); + this._onRequestWriteToTextArea.fire(line); + } if (this._terminal.buffer.active === this._terminal.buffer.normal && this._currentCommand.commandStartMarker) { if (this._terminal.buffer.active.baseY + this._terminal.buffer.active.cursorY < this._currentCommand.commandStartMarker.line) { this._clearCommandsInViewport(); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index a699b24c929..fb875038aab 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -422,6 +422,14 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { }); } else if (e === TerminalCapability.CommandDetection) { const commandCapability = this.capabilities.get(TerminalCapability.CommandDetection); + commandCapability?.onRequestWriteToTextArea(text => { + const textArea = this.xterm?.raw.textarea; + if (textArea) { + textArea.textContent = text; + console.log('content is'); + console.log(textArea.textContent); + } + }); commandCapability?.onCommandFinished(e => { if (e.command.trim().length > 0) { this._scopedInstantiationService.invokeFunction(getCommandHistory)?.add(e.command, { shellType: this._shellType }); From bb9d98b82e310d368971430291ea5bf796ae2f35 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 11 Aug 2023 15:05:46 -0700 Subject: [PATCH 011/223] check if screen reader optimized, support for right prompts --- .../commandDetectionCapability.ts | 28 +++++++++++++------ .../common/xterm/shellIntegrationAddon.ts | 3 +- src/vs/platform/terminal/node/ptyService.ts | 2 +- .../terminal/browser/terminalInstance.ts | 4 +-- .../terminal/browser/xterm/xtermTerminal.ts | 4 ++- .../commandDetectionCapability.test.ts | 2 +- .../browser/xterm/decorationAddon.test.ts | 2 +- .../xterm/shellIntegrationAddon.test.ts | 2 +- .../test/browser/terminalLinkOpeners.test.ts | 2 +- .../test/browser/quickFixAddon.test.ts | 2 +- 10 files changed, 33 insertions(+), 18 deletions(-) diff --git a/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts b/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts index d79ebc5718b..64947a3769e 100644 --- a/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts +++ b/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts @@ -115,6 +115,7 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe constructor( private readonly _terminal: Terminal, + private readonly _isScreenReaderOptimized: boolean, private readonly _logService: ILogService ) { super(); @@ -137,6 +138,24 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe @debounce(500) private _handleCursorMove() { + // Sync the textarea with the terminal contents so screen readers can read it + if (this._isScreenReaderOptimized) { + const buffer = this._terminal.buffer.active; + const line = buffer.getLine(buffer.cursorY)?.translateToString(true); + let commandText: string | undefined; + if (line) { + if (this._currentCommand.commandStartX) { + // Left prompt + commandText = line.substring(this._currentCommand.commandStartX); + } else if (this._currentCommand.commandRightPromptStartX) { + // Right prompt + commandText = line.substring(0, this._currentCommand.commandRightPromptStartX).trimStart(); + } + if (commandText?.length) { + this._onRequestWriteToTextArea.fire(commandText); + } + } + } // Early versions of conpty do not have real support for an alt buffer, in addition certain // commands such as tsc watch will write to the top of the normal buffer. The following // checks when the cursor has moved while the normal buffer is empty and if it is above the @@ -147,14 +166,7 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe // // This is mostly a workaround for Windows but applies to all OS' because of the tsc watch // case. - let line = this._terminal.buffer.active.getLine(this._terminal.buffer.active.cursorY)?.translateToString(true); - if (this._currentCommand.commandStartX) { - line = line?.substring(this._currentCommand.commandStartX).trimEnd(); - } - if (line) { - console.log(line); - this._onRequestWriteToTextArea.fire(line); - } + if (this._terminal.buffer.active === this._terminal.buffer.normal && this._currentCommand.commandStartMarker) { if (this._terminal.buffer.active.baseY + this._terminal.buffer.active.cursorY < this._currentCommand.commandStartMarker.line) { this._clearCommandsInViewport(); diff --git a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts index dd97f30b522..409b5714060 100644 --- a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts +++ b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts @@ -207,6 +207,7 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati constructor( private _nonce: string, + private readonly _isScreenReaderOptimized: boolean, private readonly _disableTelemetry: boolean | undefined, private readonly _telemetryService: ITelemetryService | undefined, private readonly _logService: ILogService @@ -513,7 +514,7 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati protected _createOrGetCommandDetection(terminal: Terminal): ICommandDetectionCapability { let commandDetection = this.capabilities.get(TerminalCapability.CommandDetection); if (!commandDetection) { - commandDetection = this._register(new CommandDetectionCapability(terminal, this._logService)); + commandDetection = this._register(new CommandDetectionCapability(terminal, this._isScreenReaderOptimized, this._logService)); this.capabilities.add(TerminalCapability.CommandDetection, commandDetection); } return commandDetection; diff --git a/src/vs/platform/terminal/node/ptyService.ts b/src/vs/platform/terminal/node/ptyService.ts index 7e017ab5ccd..ffcafa82d80 100644 --- a/src/vs/platform/terminal/node/ptyService.ts +++ b/src/vs/platform/terminal/node/ptyService.ts @@ -1000,7 +1000,7 @@ class XtermSerializer implements ITerminalSerializer { this._xterm.writeln(reviveBufferWithRestoreMessage); } this.setUnicodeVersion(unicodeVersion); - this._shellIntegrationAddon = new ShellIntegrationAddon(shellIntegrationNonce, true, undefined, logService); + this._shellIntegrationAddon = new ShellIntegrationAddon(shellIntegrationNonce, false, true, undefined, logService); this._xterm.loadAddon(this._shellIntegrationAddon); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index fb875038aab..71e04e17147 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -425,9 +425,9 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { commandCapability?.onRequestWriteToTextArea(text => { const textArea = this.xterm?.raw.textarea; if (textArea) { + // Sync the textarea using shell integration so screen + // readers can read the command textArea.textContent = text; - console.log('content is'); - console.log(textArea.textContent); } }); commandCapability?.onCommandFinished(e => { diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index f3776f4eab8..c757e0b4281 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -43,6 +43,7 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService import { debounce } from 'vs/base/common/decorators'; import { MouseWheelClassifier } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { IMouseWheelEvent, StandardWheelEvent } from 'vs/base/browser/mouseEvent'; +import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; const enum RenderConstants { /** @@ -202,6 +203,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, ID @ITelemetryService private readonly _telemetryService: ITelemetryService, @IClipboardService private readonly _clipboardService: IClipboardService, @IContextKeyService contextKeyService: IContextKeyService, + @IAccessibilityService private readonly _accessiblityService: IAccessibilityService ) { super(); const font = this._configHelper.getFont(undefined, true); @@ -272,7 +274,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, ID this._decorationAddon = this._instantiationService.createInstance(DecorationAddon, this._capabilities); this._decorationAddon.onDidRequestRunCommand(e => this._onDidRequestRunCommand.fire(e)); this.raw.loadAddon(this._decorationAddon); - this._shellIntegrationAddon = new ShellIntegrationAddon(shellIntegrationNonce, disableShellIntegrationReporting, this._telemetryService, this._logService); + this._shellIntegrationAddon = new ShellIntegrationAddon(shellIntegrationNonce, this._accessiblityService.isScreenReaderOptimized(), disableShellIntegrationReporting, this._telemetryService, this._logService); this.raw.loadAddon(this._shellIntegrationAddon); this._anyTerminalFocusContextKey = TerminalContextKeys.focusInAny.bindTo(contextKeyService); diff --git a/src/vs/workbench/contrib/terminal/test/browser/capabilities/commandDetectionCapability.test.ts b/src/vs/workbench/contrib/terminal/test/browser/capabilities/commandDetectionCapability.test.ts index 8ce2b64a810..1b68224fd18 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/capabilities/commandDetectionCapability.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/capabilities/commandDetectionCapability.test.ts @@ -62,7 +62,7 @@ suite('CommandDetectionCapability', () => { xterm = new TerminalCtor({ allowProposedApi: true, cols: 80 }); instantiationService = new TestInstantiationService(); instantiationService.stub(IContextMenuService, { showContextMenu(delegate: IContextMenuDelegate): void { } } as Partial); - capability = new TestCommandDetectionCapability(xterm, new NullLogService()); + capability = new TestCommandDetectionCapability(xterm, false, new NullLogService()); addEvents = []; capability.onCommandFinished(e => addEvents.push(e)); assertCommands([]); diff --git a/src/vs/workbench/contrib/terminal/test/browser/xterm/decorationAddon.test.ts b/src/vs/workbench/contrib/terminal/test/browser/xterm/decorationAddon.test.ts index b9306c6c0a7..0800708f9f7 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/xterm/decorationAddon.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/xterm/decorationAddon.test.ts @@ -61,7 +61,7 @@ suite('DecorationAddon', () => { instantiationService.stub(IContextMenuService, instantiationService.createInstance(ContextMenuService)); instantiationService.stub(ILogService, NullLogService); const capabilities = new TerminalCapabilityStore(); - capabilities.add(TerminalCapability.CommandDetection, instantiationService.createInstance(CommandDetectionCapability, xterm)); + capabilities.add(TerminalCapability.CommandDetection, instantiationService.createInstance(CommandDetectionCapability, xterm, false)); instantiationService.stub(ILifecycleService, new TestLifecycleService()); decorationAddon = instantiationService.createInstance(DecorationAddon, capabilities); xterm.loadAddon(decorationAddon); diff --git a/src/vs/workbench/contrib/terminal/test/browser/xterm/shellIntegrationAddon.test.ts b/src/vs/workbench/contrib/terminal/test/browser/xterm/shellIntegrationAddon.test.ts index 5cba5252508..6b85f891774 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/xterm/shellIntegrationAddon.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/xterm/shellIntegrationAddon.test.ts @@ -34,7 +34,7 @@ suite('ShellIntegrationAddon', () => { const TerminalCtor = (await importAMDNodeModule('xterm', 'lib/xterm.js')).Terminal; xterm = new TerminalCtor({ allowProposedApi: true, cols: 80, rows: 30 }); - shellIntegrationAddon = new TestShellIntegrationAddon('', true, undefined, new NullLogService()); + shellIntegrationAddon = new TestShellIntegrationAddon('', false, true, undefined, new NullLogService()); xterm.loadAddon(shellIntegrationAddon); capabilities = shellIntegrationAddon.capabilities; }); diff --git a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkOpeners.test.ts b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkOpeners.test.ts index e9193758069..d355dbe5ed7 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkOpeners.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkOpeners.test.ts @@ -125,7 +125,7 @@ suite('Workbench - TerminalLinkOpeners', () => { setup(() => { capabilities = new TerminalCapabilityStore(); - commandDetection = instantiationService.createInstance(TestCommandDetectionCapability, xterm); + commandDetection = instantiationService.createInstance(TestCommandDetectionCapability, xterm, false); capabilities.add(TerminalCapability.CommandDetection, commandDetection); }); diff --git a/src/vs/workbench/contrib/terminalContrib/quickFix/test/browser/quickFixAddon.test.ts b/src/vs/workbench/contrib/terminalContrib/quickFix/test/browser/quickFixAddon.test.ts index 107304e632c..4b4e867c43b 100644 --- a/src/vs/workbench/contrib/terminalContrib/quickFix/test/browser/quickFixAddon.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/quickFix/test/browser/quickFixAddon.test.ts @@ -58,7 +58,7 @@ suite('QuickFixAddon', () => { instantiationService.stub(ILabelService, {} as Partial); const capabilities = new TerminalCapabilityStore(); instantiationService.stub(ILogService, new NullLogService()); - commandDetection = instantiationService.createInstance(CommandDetectionCapability, terminal); + commandDetection = instantiationService.createInstance(CommandDetectionCapability, terminal, false); capabilities.add(TerminalCapability.CommandDetection, commandDetection); instantiationService.stub(IContextMenuService, instantiationService.createInstance(ContextMenuService)); instantiationService.stub(IOpenerService, {} as Partial); From 436a8af787809184a6f6ee3575baf6db907b5084 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Fri, 11 Aug 2023 15:07:46 -0700 Subject: [PATCH 012/223] Update src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts --- .../terminal/common/capabilities/commandDetectionCapability.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts b/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts index 64947a3769e..48eb56492d8 100644 --- a/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts +++ b/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts @@ -166,7 +166,6 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe // // This is mostly a workaround for Windows but applies to all OS' because of the tsc watch // case. - if (this._terminal.buffer.active === this._terminal.buffer.normal && this._currentCommand.commandStartMarker) { if (this._terminal.buffer.active.baseY + this._terminal.buffer.active.cursorY < this._currentCommand.commandStartMarker.line) { this._clearCommandsInViewport(); From fffa5874cecc554a0b28ebd26a72fba717c29b55 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Fri, 11 Aug 2023 15:08:27 -0700 Subject: [PATCH 013/223] Update src/vs/workbench/contrib/terminal/browser/terminalInstance.ts --- src/vs/workbench/contrib/terminal/browser/terminalInstance.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 71e04e17147..499a8ba38de 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -426,7 +426,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { const textArea = this.xterm?.raw.textarea; if (textArea) { // Sync the textarea using shell integration so screen - // readers can read the command + // readers can review the command with left/ right arrow keys textArea.textContent = text; } }); From 9283cbe983e47bccc9e240260839f61ac211016d Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Sat, 12 Aug 2023 18:14:58 -0600 Subject: [PATCH 014/223] Fix issue with variable decorations --- .../contrib/chat/browser/contrib/chatInputEditorContrib.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts index 8e84be5d439..b80d3b020f0 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts @@ -177,18 +177,17 @@ class InputEditorDecorations extends Disposable { } // const variables = this.chatVariablesService.getVariables(); - const variableReg = /(^|\s)@(\w+)(\s|$)/ig; + const variableReg = /(^|\s)@(\w+)(?=(\s|$))/ig; let match: RegExpMatchArray | null; const varDecorations: IDecorationOptions[] = []; while (match = variableReg.exec(inputValue)) { // const candidate = match[2]; // if (Iterable.find(variables, v => v.name === candidate)) - const atOffset = match[0].indexOf('@'); varDecorations.push({ range: { startLineNumber: 1, endLineNumber: 1, - startColumn: match.index! + atOffset + 1, + startColumn: match.index! + match[1].length + 1, endColumn: match.index! + match[0].length + 1 } }); From 13b404d11bd54c4fde2d7a2329ff1cbea1dc9204 Mon Sep 17 00:00:00 2001 From: Johannes Date: Mon, 14 Aug 2023 14:18:29 +0200 Subject: [PATCH 015/223] expose resolved variables to chat request --- .../workbench/api/browser/mainThreadChat.ts | 1 + .../workbench/api/common/extHost.protocol.ts | 5 ++- src/vs/workbench/api/common/extHostChat.ts | 1 + .../api/common/extHostChatVariables.ts | 4 +- .../contrib/chat/common/chatService.ts | 2 + .../contrib/chat/common/chatServiceImpl.ts | 16 +++++++- .../contrib/chat/common/chatVariables.ts | 37 ++++++++----------- .../chat/test/common/chatService.test.ts | 4 +- .../chat/test/common/chatVariables.test.ts | 28 +++++++------- .../vscode.proposed.chatVariables.d.ts | 11 +++++- .../vscode.proposed.interactive.d.ts | 8 +--- 11 files changed, 65 insertions(+), 52 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadChat.ts b/src/vs/workbench/api/browser/mainThreadChat.ts index 4a6634dc248..b0621471f66 100644 --- a/src/vs/workbench/api/browser/mainThreadChat.ts +++ b/src/vs/workbench/api/browser/mainThreadChat.ts @@ -100,6 +100,7 @@ export class MainThreadChat extends Disposable implements MainThreadChatShape { try { const requestDto: IChatRequestDto = { message: request.message, + variables: request.variables }; const dto = await this._proxy.$provideReply(handle, request.session.id, requestDto, token); return { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 8247828cf95..b76cfe05ba4 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -75,7 +75,7 @@ import * as search from 'vs/workbench/services/search/common/search'; import { ISaveProfileResult } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; import { IChatMessage, IChatResponseFragment, IChatResponseProviderMetadata } from 'vs/workbench/contrib/chat/common/chatProvider'; import { IChatSlashFragment } from 'vs/workbench/contrib/chat/common/chatSlashCommands'; -import { IChatResolvedVariable, IChatVariableData } from 'vs/workbench/contrib/chat/common/chatVariables'; +import { IChatRequestVariableValue, IChatVariableData } from 'vs/workbench/contrib/chat/common/chatVariables'; export interface IWorkspaceData extends IStaticWorkspaceData { folders: { uri: UriComponents; name: string; index: number }[]; @@ -1152,7 +1152,7 @@ export interface MainThreadChatVariablesShape extends IDisposable { } export interface ExtHostChatVariablesShape { - $resolveVariable(handle: number, token: CancellationToken): Promise; + $resolveVariable(handle: number, token: CancellationToken): Promise; } export interface MainThreadInlineChatShape extends IDisposable { @@ -1187,6 +1187,7 @@ export interface IChatDto { export interface IChatRequestDto { message: string | IChatReplyFollowup; + variables?: Record; } export interface IChatResponseDto { diff --git a/src/vs/workbench/api/common/extHostChat.ts b/src/vs/workbench/api/common/extHostChat.ts index 674b557b889..5d1cbd4474a 100644 --- a/src/vs/workbench/api/common/extHostChat.ts +++ b/src/vs/workbench/api/common/extHostChat.ts @@ -200,6 +200,7 @@ export class ExtHostChat implements ExtHostChatShape { const requestObj: vscode.InteractiveRequest = { session: realSession, message: typeof request.message === 'string' ? request.message : typeConvert.ChatReplyFollowup.to(request.message), + variables: request.variables ?? {} }; const stopWatch = StopWatch.create(false); diff --git a/src/vs/workbench/api/common/extHostChatVariables.ts b/src/vs/workbench/api/common/extHostChatVariables.ts index ac11bd3515f..7225b148bd4 100644 --- a/src/vs/workbench/api/common/extHostChatVariables.ts +++ b/src/vs/workbench/api/common/extHostChatVariables.ts @@ -8,7 +8,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { ExtHostChatVariablesShape, IMainContext, MainContext, MainThreadChatVariablesShape } from 'vs/workbench/api/common/extHost.protocol'; -import { IChatResolvedVariable, IChatVariableData } from 'vs/workbench/contrib/chat/common/chatVariables'; +import { IChatRequestVariableValue, IChatVariableData } from 'vs/workbench/contrib/chat/common/chatVariables'; import { onUnexpectedExternalError } from 'vs/base/common/errors'; export class ExtHostChatVariables implements ExtHostChatVariablesShape { @@ -22,7 +22,7 @@ export class ExtHostChatVariables implements ExtHostChatVariablesShape { this._proxy = mainContext.getProxy(MainContext.MainThreadChatVariables); } - async $resolveVariable(handle: number, token: CancellationToken): Promise { + async $resolveVariable(handle: number, token: CancellationToken): Promise { const item = this._resolver.get(handle); if (!item) { return undefined; diff --git a/src/vs/workbench/contrib/chat/common/chatService.ts b/src/vs/workbench/contrib/chat/common/chatService.ts index 1bed4de4b2b..70f6c3f780f 100644 --- a/src/vs/workbench/contrib/chat/common/chatService.ts +++ b/src/vs/workbench/contrib/chat/common/chatService.ts @@ -10,6 +10,7 @@ import { URI } from 'vs/base/common/uri'; import { ProviderResult } from 'vs/editor/common/languages'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IChatModel, ChatModel, ISerializableChatData } from 'vs/workbench/contrib/chat/common/chatModel'; +import { IChatRequestVariableValue } from 'vs/workbench/contrib/chat/common/chatVariables'; export interface IChat { id: number; // TODO Maybe remove this and move to a subclass that only the provider knows about @@ -25,6 +26,7 @@ export interface IChat { export interface IChatRequest { session: IChat; message: string | IChatReplyFollowup; + variables: Record; } export interface IChatResponseErrorDetails { diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index a0a5c5f59b6..a8663c161df 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -24,8 +24,9 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { CONTEXT_PROVIDER_EXISTS } from 'vs/workbench/contrib/chat/common/chatContextKeys'; import { ChatModel, ChatWelcomeMessageModel, IChatModel, ISerializableChatData, ISerializableChatsData } from 'vs/workbench/contrib/chat/common/chatModel'; import { ChatMessageRole, IChatMessage } from 'vs/workbench/contrib/chat/common/chatProvider'; -import { IChat, IChatCompleteResponse, IChatDetail, IChatDynamicRequest, IChatProgress, IChatProvider, IChatProviderInfo, IChatReplyFollowup, IChatResponse, IChatService, IChatTransferredSessionData, IChatUserActionEvent, ISlashCommand, InteractiveSessionCopyKind, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChat, IChatCompleteResponse, IChatDetail, IChatDynamicRequest, IChatProgress, IChatProvider, IChatProviderInfo, IChatReplyFollowup, IChatRequest, IChatResponse, IChatService, IChatTransferredSessionData, IChatUserActionEvent, ISlashCommand, InteractiveSessionCopyKind, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatSlashCommandService, IChatSlashFragment } from 'vs/workbench/contrib/chat/common/chatSlashCommands'; +import { IChatVariablesService } from 'vs/workbench/contrib/chat/common/chatVariables'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; const serializedChatKey = 'interactive.sessions'; @@ -151,6 +152,7 @@ export class ChatService extends Disposable implements IChatService { @IContextKeyService private readonly contextKeyService: IContextKeyService, @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, @IChatSlashCommandService private readonly chatSlashCommandService: IChatSlashCommandService, + @IChatVariablesService private readonly chatVariablesService: IChatVariablesService ) { super(); @@ -496,7 +498,17 @@ export class ChatService extends Disposable implements IChatService { rawResponse = { session: model.session! }; } else { - rawResponse = await provider.provideReply({ session: model.session!, message: resolvedCommand }, progressCallback, token); + const request: IChatRequest = { + session: model.session!, + message: resolvedCommand, + variables: {} + }; + + if (typeof request.message === 'string') { + request.variables = await this.chatVariablesService.resolveVariables(request.message, token); + } + + rawResponse = await provider.provideReply(request, progressCallback, token); } if (token.isCancellationRequested) { diff --git a/src/vs/workbench/contrib/chat/common/chatVariables.ts b/src/vs/workbench/contrib/chat/common/chatVariables.ts index a59bac1bd63..1dc14e1de7b 100644 --- a/src/vs/workbench/contrib/chat/common/chatVariables.ts +++ b/src/vs/workbench/contrib/chat/common/chatVariables.ts @@ -14,14 +14,15 @@ export interface IChatVariableData { description: string; } -export interface IChatResolvedVariable { - // TODO should we allow for multiple levels - content: string; +export interface IChatRequestVariableValue { + level: 'short' | 'medium' | 'full'; + value: string; + description?: string; } export interface IChatVariableResolver { // TODO should we spec "zoom level" - (token: CancellationToken): Promise; + (token: CancellationToken): Promise; } export const IChatVariablesService = createDecorator('IChatVariablesService'); @@ -34,10 +35,7 @@ export interface IChatVariablesService { /** * Resolves all variables that occur in `prompt` */ - resolveVariables(prompt: string, token: CancellationToken): Promise>; - - // TODO is this needed? - resolveVariable(name: string, token: CancellationToken): Promise; + resolveVariables(prompt: string, token: CancellationToken): Promise>; } type ChatData = [data: IChatVariableData, resolver: IChatVariableResolver]; @@ -60,12 +58,17 @@ export class ChatVariablesService implements IChatVariablesService { { name: 'workspace', description: 'Details of your workspace' }, { name: 'vscode', description: 'Commands and settings in vscode' }, ].forEach(item => { - this.registerVariable(item, async () => ({ content: item.name })); + this.registerVariable(item, async () => { + return [{ + level: 'short', + value: item.name + }]; + }); }); } - async resolveVariables(prompt: string, token: CancellationToken): Promise> { - const resolvedVariables = new Map(); + async resolveVariables(prompt: string, token: CancellationToken): Promise> { + const resolvedVariables: Record = {}; const jobs: Promise[] = []; const regex = /(^|\s)@(\w+)(\s|$)/ig; @@ -77,7 +80,7 @@ export class ChatVariablesService implements IChatVariablesService { if (data) { jobs.push(data[1](token).then(value => { if (value) { - resolvedVariables.set(candidate, value); + resolvedVariables[candidate] = value; } }).catch(onUnexpectedExternalError)); } @@ -102,14 +105,4 @@ export class ChatVariablesService implements IChatVariablesService { this._resolver.delete(key); }); } - - resolveVariable(name: string, token: CancellationToken): Promise { - const key = name.toLowerCase(); - const chatData = this._resolver.get(key); - if (!chatData) { - throw new Error(`A chat variable with the name '${name}' does not exist.`); - } - const [, resolver] = chatData; - return Promise.resolve(resolver(token)); - } } diff --git a/src/vs/workbench/contrib/chat/test/common/chatService.test.ts b/src/vs/workbench/contrib/chat/test/common/chatService.test.ts index a2f01a9d9ed..ca968ebf1c5 100644 --- a/src/vs/workbench/contrib/chat/test/common/chatService.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/chatService.test.ts @@ -23,6 +23,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { ChatSlashCommandService, IChatSlashCommandService } from 'vs/workbench/contrib/chat/common/chatSlashCommands'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { ChatVariablesService, IChatVariablesService } from 'vs/workbench/contrib/chat/common/chatVariables'; class SimpleTestProvider extends Disposable implements IChatProvider { private static sessionId = 0; @@ -65,7 +66,8 @@ suite('Chat', () => { suiteSetup(async () => { instantiationService = new TestInstantiationService(new ServiceCollection( - [IChatSlashCommandService, new SyncDescriptor(ChatSlashCommandService)] + [IChatSlashCommandService, new SyncDescriptor(ChatSlashCommandService)], + [IChatVariablesService, new SyncDescriptor(ChatVariablesService)] )); instantiationService.stub(IStorageService, storageService = new TestStorageService()); instantiationService.stub(ILogService, new NullLogService()); diff --git a/src/vs/workbench/contrib/chat/test/common/chatVariables.test.ts b/src/vs/workbench/contrib/chat/test/common/chatVariables.test.ts index 4cfa9defedf..a24ab711a95 100644 --- a/src/vs/workbench/contrib/chat/test/common/chatVariables.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/chatVariables.test.ts @@ -17,38 +17,38 @@ suite('ChatVariables', function () { test('ChatVariables - resolveVariables', async function () { - service.registerVariable({ name: 'foo', description: 'bar' }, async () => ({ content: 'farboo' })); - service.registerVariable({ name: 'far', description: 'boo' }, async () => ({ content: 'farboo' })); + service.registerVariable({ name: 'foo', description: 'bar' }, async () => ([{ level: 'full', value: 'farboo' }])); + service.registerVariable({ name: 'far', description: 'boo' }, async () => ([{ level: 'full', value: 'farboo' }])); { const data = await service.resolveVariables('Hello @foo and@far', CancellationToken.None); - assert.strictEqual(data.size, 1); - assert.deepEqual([...data.keys()].sort(), ['foo']); + assert.strictEqual(Object.keys(data).length, 1); + assert.deepEqual(Object.keys(data).sort(), ['foo']); } { const data = await service.resolveVariables('@foo Hello', CancellationToken.None); - assert.strictEqual(data.size, 1); - assert.deepEqual([...data.keys()].sort(), ['foo']); + assert.strictEqual(Object.keys(data).length, 1); + assert.deepEqual(Object.keys(data).sort(), ['foo']); } { const data = await service.resolveVariables('Hello @foo', CancellationToken.None); - assert.strictEqual(data.size, 1); - assert.deepEqual([...data.keys()].sort(), ['foo']); + assert.strictEqual(Object.keys(data).length, 1); + assert.deepEqual(Object.keys(data).sort(), ['foo']); } { const data = await service.resolveVariables('Hello @foo and@far @foo', CancellationToken.None); - assert.strictEqual(data.size, 1); - assert.deepEqual([...data.keys()].sort(), ['foo']); + assert.strictEqual(Object.keys(data).length, 1); + assert.deepEqual(Object.keys(data).sort(), ['foo']); } { const data = await service.resolveVariables('Hello @foo and @far @foo', CancellationToken.None); - assert.strictEqual(data.size, 2); - assert.deepEqual([...data.keys()].sort(), ['far', 'foo']); + assert.strictEqual(Object.keys(data).length, 2); + assert.deepEqual(Object.keys(data).sort(), ['far', 'foo']); } { const data = await service.resolveVariables('Hello @foo and @far @foo @unknown', CancellationToken.None); - assert.strictEqual(data.size, 2); - assert.deepEqual([...data.keys()].sort(), ['far', 'foo']); + assert.strictEqual(Object.keys(data).length, 2); + assert.deepEqual(Object.keys(data).sort(), ['far', 'foo']); } }); }); diff --git a/src/vscode-dts/vscode.proposed.chatVariables.d.ts b/src/vscode-dts/vscode.proposed.chatVariables.d.ts index 38f6c1947dc..9a4fec7537c 100644 --- a/src/vscode-dts/vscode.proposed.chatVariables.d.ts +++ b/src/vscode-dts/vscode.proposed.chatVariables.d.ts @@ -5,12 +5,19 @@ declare module 'vscode' { + export interface InteractiveRequest { + variables: Record; + } + export interface ChatVariableValue { - content: string; + // eslint-disable-next-line local/vscode-dts-literal-or-types, local/vscode-dts-string-type-literals + level: 'short' | 'medium' | 'full'; + value: string; + description?: string; } export interface ChatVariableResolver { - resolve(name: string, token: CancellationToken): ProviderResult; + resolve(name: string, token: CancellationToken): ProviderResult; } // Could be provider/resolver pattern, but how dynamic are they? diff --git a/src/vscode-dts/vscode.proposed.interactive.d.ts b/src/vscode-dts/vscode.proposed.interactive.d.ts index 31017b88217..a6046ff0c1e 100644 --- a/src/vscode-dts/vscode.proposed.interactive.d.ts +++ b/src/vscode-dts/vscode.proposed.interactive.d.ts @@ -105,16 +105,10 @@ declare module 'vscode' { args: any; } - export interface ResolvedVariableInRequest { - name: string; - content: string; - } - - export type ParsedRequest = (string | ResolvedVariableInRequest)[]; export interface InteractiveRequest { session: InteractiveSession; - message: ParsedRequest | InteractiveSessionReplyFollowup; + message: string | InteractiveSessionReplyFollowup; } export interface InteractiveResponseErrorDetails { From b077e287dacff0fbaccb895edb6907079f1ece4c Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 14 Aug 2023 11:04:14 -0700 Subject: [PATCH 016/223] fix tests --- .../contrib/terminal/test/browser/xterm/xtermTerminal.test.ts | 4 +++- .../accessibility/test/browser/bufferContentTracker.test.ts | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/test/browser/xterm/xtermTerminal.test.ts b/src/vs/workbench/contrib/terminal/test/browser/xterm/xtermTerminal.test.ts index ab689e76e87..a2eb3617547 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/xterm/xtermTerminal.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/xterm/xtermTerminal.test.ts @@ -33,6 +33,7 @@ import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKe import { Color, RGBA } from 'vs/base/common/color'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ITerminalLogService } from 'vs/platform/terminal/common/terminal'; +import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; class TestWebglAddon implements WebglAddon { static shouldThrow = false; @@ -51,7 +52,7 @@ class TestWebglAddon implements WebglAddon { } clearTextureAtlas() { } } - +// to class TestXtermTerminal extends XtermTerminal { webglAddonPromise: Promise = Promise.resolve(TestWebglAddon); // Force synchronous to avoid async when activating the addon @@ -122,6 +123,7 @@ suite('XtermTerminal', () => { instantiationService.stub(IContextMenuService, instantiationService.createInstance(ContextMenuService)); instantiationService.stub(ILifecycleService, new TestLifecycleService()); instantiationService.stub(IContextKeyService, new MockContextKeyService()); + instantiationService.stub(IAccessibilityService, { isScreenReaderOptimized: () => false }); configHelper = instantiationService.createInstance(TerminalConfigHelper); XTermBaseCtor = (await importAMDNodeModule('xterm', 'lib/xterm.js')).Terminal; diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/test/browser/bufferContentTracker.test.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/test/browser/bufferContentTracker.test.ts index 240c15c4c21..a87ab7d68e4 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/test/browser/bufferContentTracker.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/test/browser/bufferContentTracker.test.ts @@ -6,6 +6,7 @@ import * as assert from 'assert'; import { importAMDNodeModule } from 'vs/amdX'; import { isWindows } from 'vs/base/common/platform'; +import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -61,6 +62,7 @@ suite('Buffer Content Tracker', () => { instantiationService.stub(IContextMenuService, instantiationService.createInstance(ContextMenuService)); instantiationService.stub(ILifecycleService, new TestLifecycleService()); instantiationService.stub(IContextKeyService, new MockContextKeyService()); + instantiationService.stub(IAccessibilityService, { isScreenReaderOptimized: () => false }); configHelper = instantiationService.createInstance(TerminalConfigHelper); capabilities = new TerminalCapabilityStore(); if (!isWindows) { From 84f841784d7cd3d77f329929500d9e29cd860976 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 14 Aug 2023 15:16:47 -0700 Subject: [PATCH 017/223] fix #189318 --- .../common/capabilities/capabilities.ts | 3 +- .../commandDetectionCapability.ts | 24 +------ .../common/xterm/shellIntegrationAddon.ts | 3 +- src/vs/platform/terminal/node/ptyService.ts | 2 +- .../terminal/browser/media/terminal.css | 5 -- .../contrib/terminal/browser/media/xterm.css | 13 ++++ .../terminal/browser/terminalInstance.ts | 8 --- .../browser/xterm/textAreaSyncAddon.ts | 65 +++++++++++++++++++ .../terminal/browser/xterm/xtermTerminal.ts | 21 ++++-- .../commandDetectionCapability.test.ts | 2 +- .../browser/xterm/decorationAddon.test.ts | 2 +- .../xterm/shellIntegrationAddon.test.ts | 2 +- .../test/browser/xterm/xtermTerminal.test.ts | 2 - .../test/browser/bufferContentTracker.test.ts | 2 - .../test/browser/terminalLinkOpeners.test.ts | 2 +- .../test/browser/quickFixAddon.test.ts | 2 +- 16 files changed, 107 insertions(+), 51 deletions(-) create mode 100644 src/vs/workbench/contrib/terminal/browser/xterm/textAreaSyncAddon.ts diff --git a/src/vs/platform/terminal/common/capabilities/capabilities.ts b/src/vs/platform/terminal/common/capabilities/capabilities.ts index 624d986ad6d..920bac8df0d 100644 --- a/src/vs/platform/terminal/common/capabilities/capabilities.ts +++ b/src/vs/platform/terminal/common/capabilities/capabilities.ts @@ -5,6 +5,7 @@ import { Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; +import { ICurrentPartialCommand } from 'vs/platform/terminal/common/capabilities/commandDetectionCapability'; import { ITerminalOutputMatch, ITerminalOutputMatcher } from 'vs/platform/terminal/common/terminal'; import { ReplayEntry } from 'vs/platform/terminal/common/terminalProcess'; @@ -152,12 +153,12 @@ export interface ICommandDetectionCapability { * the state cannot reliably be detected the fallback of undefined will be used. */ readonly hasInput: boolean | undefined; + readonly currentCommand: ICurrentPartialCommand | undefined; readonly onCommandStarted: Event; readonly onCommandFinished: Event; readonly onCommandExecuted: Event; readonly onCommandInvalidated: Event; readonly onCurrentCommandInvalidated: Event; - readonly onRequestWriteToTextArea: Event; setCwd(value: string): void; setIsWindowsPty(value: boolean): void; diff --git a/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts b/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts index 48eb56492d8..f3c2519be9e 100644 --- a/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts +++ b/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts @@ -79,6 +79,9 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe } return undefined; } + get currentCommand(): ICurrentPartialCommand | undefined { + return this._currentCommand; + } get cwd(): string | undefined { return this._cwd; } private get _isInputting(): boolean { return !!(this._currentCommand.commandStartMarker && !this._currentCommand.commandExecutedMarker); @@ -110,12 +113,9 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe readonly onCommandInvalidated = this._onCommandInvalidated.event; private readonly _onCurrentCommandInvalidated = this._register(new Emitter()); readonly onCurrentCommandInvalidated = this._onCurrentCommandInvalidated.event; - private readonly _onRequestWriteToTextArea = this._register(new Emitter()); - readonly onRequestWriteToTextArea = this._onRequestWriteToTextArea.event; constructor( private readonly _terminal: Terminal, - private readonly _isScreenReaderOptimized: boolean, private readonly _logService: ILogService ) { super(); @@ -138,24 +138,6 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe @debounce(500) private _handleCursorMove() { - // Sync the textarea with the terminal contents so screen readers can read it - if (this._isScreenReaderOptimized) { - const buffer = this._terminal.buffer.active; - const line = buffer.getLine(buffer.cursorY)?.translateToString(true); - let commandText: string | undefined; - if (line) { - if (this._currentCommand.commandStartX) { - // Left prompt - commandText = line.substring(this._currentCommand.commandStartX); - } else if (this._currentCommand.commandRightPromptStartX) { - // Right prompt - commandText = line.substring(0, this._currentCommand.commandRightPromptStartX).trimStart(); - } - if (commandText?.length) { - this._onRequestWriteToTextArea.fire(commandText); - } - } - } // Early versions of conpty do not have real support for an alt buffer, in addition certain // commands such as tsc watch will write to the top of the normal buffer. The following // checks when the cursor has moved while the normal buffer is empty and if it is above the diff --git a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts index 409b5714060..dd97f30b522 100644 --- a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts +++ b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts @@ -207,7 +207,6 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati constructor( private _nonce: string, - private readonly _isScreenReaderOptimized: boolean, private readonly _disableTelemetry: boolean | undefined, private readonly _telemetryService: ITelemetryService | undefined, private readonly _logService: ILogService @@ -514,7 +513,7 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati protected _createOrGetCommandDetection(terminal: Terminal): ICommandDetectionCapability { let commandDetection = this.capabilities.get(TerminalCapability.CommandDetection); if (!commandDetection) { - commandDetection = this._register(new CommandDetectionCapability(terminal, this._isScreenReaderOptimized, this._logService)); + commandDetection = this._register(new CommandDetectionCapability(terminal, this._logService)); this.capabilities.add(TerminalCapability.CommandDetection, commandDetection); } return commandDetection; diff --git a/src/vs/platform/terminal/node/ptyService.ts b/src/vs/platform/terminal/node/ptyService.ts index ffcafa82d80..7e017ab5ccd 100644 --- a/src/vs/platform/terminal/node/ptyService.ts +++ b/src/vs/platform/terminal/node/ptyService.ts @@ -1000,7 +1000,7 @@ class XtermSerializer implements ITerminalSerializer { this._xterm.writeln(reviveBufferWithRestoreMessage); } this.setUnicodeVersion(unicodeVersion); - this._shellIntegrationAddon = new ShellIntegrationAddon(shellIntegrationNonce, false, true, undefined, logService); + this._shellIntegrationAddon = new ShellIntegrationAddon(shellIntegrationNonce, true, undefined, logService); this._xterm.loadAddon(this._shellIntegrationAddon); } diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index 814626ed4f5..67d8bb86a0a 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -248,11 +248,6 @@ top: 0; } -.monaco-workbench .xterm .xterm-helper-textarea:focus { - /* Override the general vscode style applies `opacity:1!important` to textareas */ - opacity: 0 !important; -} - .monaco-workbench.vs-dark.mac .pane-body.integrated-terminal .terminal-groups-container:not(.alt-active) .terminal:not(.enable-mouse-events), .monaco-workbench.hc-black.mac .pane-body.integrated-terminal .terminal-groups-container:not(.alt-active) .terminal:not(.enable-mouse-events) { cursor: -webkit-image-set(url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAAL0lEQVQoz2NgCD3x//9/BhBYBWdhgFVAiVW4JBFKGIa4AqD0//9D3pt4I4tAdAMAHTQ/j5Zom30AAAAASUVORK5CYII=') 1x, url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAAAz0lEQVRIx2NgYGBY/R8I/vx5eelX3n82IJ9FxGf6tksvf/8FiTMQAcAGQMDvSwu09abffY8QYSAScNk45G198eX//yev73/4///701eh//kZSARckrNBRvz//+8+6ZohwCzjGNjdgQxkAg7B9WADeBjIBqtJCbhRA0YNoIkBSNmaPEMoNmA0FkYNoFKhapJ6FGyAH3nauaSmPfwI0v/3OukVi0CIZ+F25KrtYcx/CTIy0e+rC7R1Z4KMICVTQQ14feVXIbR695u14+Ir4gwAAD49E54wc1kWAAAAAElFTkSuQmCC') 2x) 5 8, text; diff --git a/src/vs/workbench/contrib/terminal/browser/media/xterm.css b/src/vs/workbench/contrib/terminal/browser/media/xterm.css index a7b1dbaa1c4..8751ab49d82 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/xterm.css +++ b/src/vs/workbench/contrib/terminal/browser/media/xterm.css @@ -78,6 +78,19 @@ white-space: nowrap; overflow: hidden; resize: none; + /** Uncomment for testing purposes */ + z-index: 36 !important; + opacity: 1 !important; + left: 0 !important; + width: fit-content !important; +} + +.monaco-workbench .xterm .xterm-helper-textarea:focus { + /* Override the general vscode style applies `opacity:1!important` to textareas */ + opacity: 0 !important; + + /** Uncomment the below for testing purposes */ + opacity: 1 !important; } .xterm .composition-view { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 499a8ba38de..a699b24c929 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -422,14 +422,6 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { }); } else if (e === TerminalCapability.CommandDetection) { const commandCapability = this.capabilities.get(TerminalCapability.CommandDetection); - commandCapability?.onRequestWriteToTextArea(text => { - const textArea = this.xterm?.raw.textarea; - if (textArea) { - // Sync the textarea using shell integration so screen - // readers can review the command with left/ right arrow keys - textArea.textContent = text; - } - }); commandCapability?.onCommandFinished(e => { if (e.command.trim().length > 0) { this._scopedInstantiationService.invokeFunction(getCommandHistory)?.add(e.command, { shellType: this._shellType }); diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/textAreaSyncAddon.ts b/src/vs/workbench/contrib/terminal/browser/xterm/textAreaSyncAddon.ts new file mode 100644 index 00000000000..146a02c5bee --- /dev/null +++ b/src/vs/workbench/contrib/terminal/browser/xterm/textAreaSyncAddon.ts @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Emitter } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; +import { ITerminalCapabilityStore, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; +import type { Terminal, ITerminalAddon } from 'xterm'; + +export interface ITextAreaData { + content: string; + cursorX: number; +} + +export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { + private _terminal: Terminal | undefined; + private readonly _onDidRequestUpdateTextArea = this._register(new Emitter()); + readonly onDidRequestUpdateTextArea = this._onDidRequestUpdateTextArea.event; + activate(terminal: Terminal): void { + this._terminal = terminal; + this._register(this._terminal.onCursorMove(() => this._refreshTextArea())); + } + + constructor( + private readonly _capabilities: ITerminalCapabilityStore, + @IAccessibilityService private readonly _accessibilityService: IAccessibilityService + ) { + super(); + } + + private _refreshTextArea(): void { + if (!this._terminal) { + return; + } + if (this._accessibilityService.isScreenReaderOptimized()) { + const commandCapability = this._capabilities.get(TerminalCapability.CommandDetection); + const currentCommand = commandCapability?.currentCommand; + if (!currentCommand) { + return; + } + const buffer = this._terminal.buffer.active; + const line = buffer.getLine(buffer.cursorY)?.translateToString(true); + let content: string = ''; + let commandStartX: number | undefined; + if (!line) { + return; + } + if (currentCommand.commandStartX) { + // Left prompt + content = line.substring(currentCommand.commandStartX).trim(); + commandStartX = currentCommand.commandStartX; + } else if (currentCommand.commandRightPromptStartX) { + // Right prompt + content = line.substring(0, currentCommand.commandRightPromptStartX).trim(); + commandStartX = 0; + } + if (commandStartX) { + this._onDidRequestUpdateTextArea.fire({ content, cursorX: buffer.cursorX - commandStartX }); + } + } + } + +} diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index 7ee53037bcc..1a09362164b 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -43,7 +43,7 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService import { debounce } from 'vs/base/common/decorators'; import { MouseWheelClassifier } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { IMouseWheelEvent, StandardWheelEvent } from 'vs/base/browser/mouseEvent'; -import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; +import { TextAreaSyncAddon } from 'vs/workbench/contrib/terminal/browser/xterm/textAreaSyncAddon'; const enum RenderConstants { /** @@ -128,6 +128,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, ID private _markNavigationAddon: MarkNavigationAddon; private _shellIntegrationAddon: ShellIntegrationAddon; private _decorationAddon: DecorationAddon; + private _textAreaSyncAddon: TextAreaSyncAddon; // Optional addons private _suggestAddon?: SuggestAddon; @@ -202,8 +203,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, ID @IThemeService private readonly _themeService: IThemeService, @ITelemetryService private readonly _telemetryService: ITelemetryService, @IClipboardService private readonly _clipboardService: IClipboardService, - @IContextKeyService contextKeyService: IContextKeyService, - @IAccessibilityService private readonly _accessiblityService: IAccessibilityService + @IContextKeyService contextKeyService: IContextKeyService ) { super(); const font = this._configHelper.getFont(undefined, true); @@ -275,8 +275,21 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, ID this._decorationAddon = this._instantiationService.createInstance(DecorationAddon, this._capabilities); this._decorationAddon.onDidRequestRunCommand(e => this._onDidRequestRunCommand.fire(e)); this.raw.loadAddon(this._decorationAddon); - this._shellIntegrationAddon = new ShellIntegrationAddon(shellIntegrationNonce, this._accessiblityService.isScreenReaderOptimized(), disableShellIntegrationReporting, this._telemetryService, this._logService); + this._shellIntegrationAddon = new ShellIntegrationAddon(shellIntegrationNonce, disableShellIntegrationReporting, this._telemetryService, this._logService); this.raw.loadAddon(this._shellIntegrationAddon); + this._textAreaSyncAddon = this._instantiationService.createInstance(TextAreaSyncAddon, this._capabilities); + this.raw.loadAddon(this._textAreaSyncAddon); + this.add(this._textAreaSyncAddon.onDidRequestUpdateTextArea((data) => { + const textArea = this.raw.textarea; + if (!textArea) { + return; + } + // Sync the textarea using shell integration so screen + // readers can review the command with left/ right arrow keys + textArea.textContent = data.content; + textArea.selectionStart = data.cursorX; + textArea.selectionEnd = data.cursorX; + })); this._anyTerminalFocusContextKey = TerminalContextKeys.focusInAny.bindTo(contextKeyService); this._anyFocusedTerminalHasSelection = TerminalContextKeys.textSelectedInFocused.bindTo(contextKeyService); diff --git a/src/vs/workbench/contrib/terminal/test/browser/capabilities/commandDetectionCapability.test.ts b/src/vs/workbench/contrib/terminal/test/browser/capabilities/commandDetectionCapability.test.ts index 1b68224fd18..8ce2b64a810 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/capabilities/commandDetectionCapability.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/capabilities/commandDetectionCapability.test.ts @@ -62,7 +62,7 @@ suite('CommandDetectionCapability', () => { xterm = new TerminalCtor({ allowProposedApi: true, cols: 80 }); instantiationService = new TestInstantiationService(); instantiationService.stub(IContextMenuService, { showContextMenu(delegate: IContextMenuDelegate): void { } } as Partial); - capability = new TestCommandDetectionCapability(xterm, false, new NullLogService()); + capability = new TestCommandDetectionCapability(xterm, new NullLogService()); addEvents = []; capability.onCommandFinished(e => addEvents.push(e)); assertCommands([]); diff --git a/src/vs/workbench/contrib/terminal/test/browser/xterm/decorationAddon.test.ts b/src/vs/workbench/contrib/terminal/test/browser/xterm/decorationAddon.test.ts index 0800708f9f7..b9306c6c0a7 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/xterm/decorationAddon.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/xterm/decorationAddon.test.ts @@ -61,7 +61,7 @@ suite('DecorationAddon', () => { instantiationService.stub(IContextMenuService, instantiationService.createInstance(ContextMenuService)); instantiationService.stub(ILogService, NullLogService); const capabilities = new TerminalCapabilityStore(); - capabilities.add(TerminalCapability.CommandDetection, instantiationService.createInstance(CommandDetectionCapability, xterm, false)); + capabilities.add(TerminalCapability.CommandDetection, instantiationService.createInstance(CommandDetectionCapability, xterm)); instantiationService.stub(ILifecycleService, new TestLifecycleService()); decorationAddon = instantiationService.createInstance(DecorationAddon, capabilities); xterm.loadAddon(decorationAddon); diff --git a/src/vs/workbench/contrib/terminal/test/browser/xterm/shellIntegrationAddon.test.ts b/src/vs/workbench/contrib/terminal/test/browser/xterm/shellIntegrationAddon.test.ts index 6b85f891774..5cba5252508 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/xterm/shellIntegrationAddon.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/xterm/shellIntegrationAddon.test.ts @@ -34,7 +34,7 @@ suite('ShellIntegrationAddon', () => { const TerminalCtor = (await importAMDNodeModule('xterm', 'lib/xterm.js')).Terminal; xterm = new TerminalCtor({ allowProposedApi: true, cols: 80, rows: 30 }); - shellIntegrationAddon = new TestShellIntegrationAddon('', false, true, undefined, new NullLogService()); + shellIntegrationAddon = new TestShellIntegrationAddon('', true, undefined, new NullLogService()); xterm.loadAddon(shellIntegrationAddon); capabilities = shellIntegrationAddon.capabilities; }); diff --git a/src/vs/workbench/contrib/terminal/test/browser/xterm/xtermTerminal.test.ts b/src/vs/workbench/contrib/terminal/test/browser/xterm/xtermTerminal.test.ts index a2eb3617547..42e1bc592dd 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/xterm/xtermTerminal.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/xterm/xtermTerminal.test.ts @@ -33,7 +33,6 @@ import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKe import { Color, RGBA } from 'vs/base/common/color'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ITerminalLogService } from 'vs/platform/terminal/common/terminal'; -import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; class TestWebglAddon implements WebglAddon { static shouldThrow = false; @@ -123,7 +122,6 @@ suite('XtermTerminal', () => { instantiationService.stub(IContextMenuService, instantiationService.createInstance(ContextMenuService)); instantiationService.stub(ILifecycleService, new TestLifecycleService()); instantiationService.stub(IContextKeyService, new MockContextKeyService()); - instantiationService.stub(IAccessibilityService, { isScreenReaderOptimized: () => false }); configHelper = instantiationService.createInstance(TerminalConfigHelper); XTermBaseCtor = (await importAMDNodeModule('xterm', 'lib/xterm.js')).Terminal; diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/test/browser/bufferContentTracker.test.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/test/browser/bufferContentTracker.test.ts index a87ab7d68e4..240c15c4c21 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/test/browser/bufferContentTracker.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/test/browser/bufferContentTracker.test.ts @@ -6,7 +6,6 @@ import * as assert from 'assert'; import { importAMDNodeModule } from 'vs/amdX'; import { isWindows } from 'vs/base/common/platform'; -import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -62,7 +61,6 @@ suite('Buffer Content Tracker', () => { instantiationService.stub(IContextMenuService, instantiationService.createInstance(ContextMenuService)); instantiationService.stub(ILifecycleService, new TestLifecycleService()); instantiationService.stub(IContextKeyService, new MockContextKeyService()); - instantiationService.stub(IAccessibilityService, { isScreenReaderOptimized: () => false }); configHelper = instantiationService.createInstance(TerminalConfigHelper); capabilities = new TerminalCapabilityStore(); if (!isWindows) { diff --git a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkOpeners.test.ts b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkOpeners.test.ts index d355dbe5ed7..e9193758069 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkOpeners.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkOpeners.test.ts @@ -125,7 +125,7 @@ suite('Workbench - TerminalLinkOpeners', () => { setup(() => { capabilities = new TerminalCapabilityStore(); - commandDetection = instantiationService.createInstance(TestCommandDetectionCapability, xterm, false); + commandDetection = instantiationService.createInstance(TestCommandDetectionCapability, xterm); capabilities.add(TerminalCapability.CommandDetection, commandDetection); }); diff --git a/src/vs/workbench/contrib/terminalContrib/quickFix/test/browser/quickFixAddon.test.ts b/src/vs/workbench/contrib/terminalContrib/quickFix/test/browser/quickFixAddon.test.ts index 4b4e867c43b..107304e632c 100644 --- a/src/vs/workbench/contrib/terminalContrib/quickFix/test/browser/quickFixAddon.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/quickFix/test/browser/quickFixAddon.test.ts @@ -58,7 +58,7 @@ suite('QuickFixAddon', () => { instantiationService.stub(ILabelService, {} as Partial); const capabilities = new TerminalCapabilityStore(); instantiationService.stub(ILogService, new NullLogService()); - commandDetection = instantiationService.createInstance(CommandDetectionCapability, terminal, false); + commandDetection = instantiationService.createInstance(CommandDetectionCapability, terminal); capabilities.add(TerminalCapability.CommandDetection, commandDetection); instantiationService.stub(IContextMenuService, instantiationService.createInstance(ContextMenuService)); instantiationService.stub(IOpenerService, {} as Partial); From 5c538c00e25a1d3178a7b0378ed605d5ca6ad164 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 14 Aug 2023 15:19:01 -0700 Subject: [PATCH 018/223] comment the textarea css --- .../platform/terminal/common/capabilities/capabilities.ts | 1 - src/vs/workbench/contrib/terminal/browser/media/xterm.css | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/vs/platform/terminal/common/capabilities/capabilities.ts b/src/vs/platform/terminal/common/capabilities/capabilities.ts index 920bac8df0d..e7b7dcb57bd 100644 --- a/src/vs/platform/terminal/common/capabilities/capabilities.ts +++ b/src/vs/platform/terminal/common/capabilities/capabilities.ts @@ -159,7 +159,6 @@ export interface ICommandDetectionCapability { readonly onCommandExecuted: Event; readonly onCommandInvalidated: Event; readonly onCurrentCommandInvalidated: Event; - setCwd(value: string): void; setIsWindowsPty(value: boolean): void; setIsCommandStorageDisabled(): void; diff --git a/src/vs/workbench/contrib/terminal/browser/media/xterm.css b/src/vs/workbench/contrib/terminal/browser/media/xterm.css index 8751ab49d82..4252a729c22 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/xterm.css +++ b/src/vs/workbench/contrib/terminal/browser/media/xterm.css @@ -78,11 +78,11 @@ white-space: nowrap; overflow: hidden; resize: none; - /** Uncomment for testing purposes */ - z-index: 36 !important; + /** Uncomment the below for textarea testing purposes */ + /* z-index: 36 !important; opacity: 1 !important; left: 0 !important; - width: fit-content !important; + width: fit-content !important; */ } .monaco-workbench .xterm .xterm-helper-textarea:focus { @@ -90,7 +90,7 @@ opacity: 0 !important; /** Uncomment the below for testing purposes */ - opacity: 1 !important; + /* opacity: 1 !important; */ } .xterm .composition-view { From 7c75fd7e1309c71c2ea9afeae46199ed2e89ece8 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 14 Aug 2023 15:20:28 -0700 Subject: [PATCH 019/223] improve name --- .../contrib/terminal/browser/xterm/textAreaSyncAddon.ts | 6 +++--- .../contrib/terminal/browser/xterm/xtermTerminal.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/textAreaSyncAddon.ts b/src/vs/workbench/contrib/terminal/browser/xterm/textAreaSyncAddon.ts index 146a02c5bee..796ba0cd5b5 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/textAreaSyncAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/textAreaSyncAddon.ts @@ -16,8 +16,8 @@ export interface ITextAreaData { export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { private _terminal: Terminal | undefined; - private readonly _onDidRequestUpdateTextArea = this._register(new Emitter()); - readonly onDidRequestUpdateTextArea = this._onDidRequestUpdateTextArea.event; + private readonly _onDidRequestTextAreaSync = this._register(new Emitter()); + readonly onDidRequestTextAreaSync = this._onDidRequestTextAreaSync.event; activate(terminal: Terminal): void { this._terminal = terminal; this._register(this._terminal.onCursorMove(() => this._refreshTextArea())); @@ -57,7 +57,7 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { commandStartX = 0; } if (commandStartX) { - this._onDidRequestUpdateTextArea.fire({ content, cursorX: buffer.cursorX - commandStartX }); + this._onDidRequestTextAreaSync.fire({ content, cursorX: buffer.cursorX - commandStartX }); } } } diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index 1a09362164b..4f2544ef375 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -279,7 +279,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, ID this.raw.loadAddon(this._shellIntegrationAddon); this._textAreaSyncAddon = this._instantiationService.createInstance(TextAreaSyncAddon, this._capabilities); this.raw.loadAddon(this._textAreaSyncAddon); - this.add(this._textAreaSyncAddon.onDidRequestUpdateTextArea((data) => { + this.add(this._textAreaSyncAddon.onDidRequestTextAreaSync((data) => { const textArea = this.raw.textarea; if (!textArea) { return; From e151ba5aca1227cdebeb57ea1e6cbea80bb16872 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 14 Aug 2023 15:21:37 -0700 Subject: [PATCH 020/223] clean up --- .../workbench/contrib/terminal/browser/xterm/xtermTerminal.ts | 4 ++-- .../contrib/terminal/test/browser/xterm/xtermTerminal.test.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index 4f2544ef375..c005695a661 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -284,11 +284,11 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, ID if (!textArea) { return; } - // Sync the textarea using shell integration so screen - // readers can review the command with left/ right arrow keys + // Sync the textarea using shell integration textArea.textContent = data.content; textArea.selectionStart = data.cursorX; textArea.selectionEnd = data.cursorX; + // TODO: cursorY? })); this._anyTerminalFocusContextKey = TerminalContextKeys.focusInAny.bindTo(contextKeyService); diff --git a/src/vs/workbench/contrib/terminal/test/browser/xterm/xtermTerminal.test.ts b/src/vs/workbench/contrib/terminal/test/browser/xterm/xtermTerminal.test.ts index 42e1bc592dd..ab689e76e87 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/xterm/xtermTerminal.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/xterm/xtermTerminal.test.ts @@ -51,7 +51,7 @@ class TestWebglAddon implements WebglAddon { } clearTextureAtlas() { } } -// to + class TestXtermTerminal extends XtermTerminal { webglAddonPromise: Promise = Promise.resolve(TestWebglAddon); // Force synchronous to avoid async when activating the addon From d4e6e8d876e8b837dbe0281f25d7d947daba289b Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Mon, 14 Aug 2023 15:40:19 -0700 Subject: [PATCH 021/223] Update src/vs/workbench/contrib/terminal/browser/media/xterm.css --- src/vs/workbench/contrib/terminal/browser/media/xterm.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/media/xterm.css b/src/vs/workbench/contrib/terminal/browser/media/xterm.css index 4252a729c22..834337b80d5 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/xterm.css +++ b/src/vs/workbench/contrib/terminal/browser/media/xterm.css @@ -89,7 +89,7 @@ /* Override the general vscode style applies `opacity:1!important` to textareas */ opacity: 0 !important; - /** Uncomment the below for testing purposes */ + /** Uncomment the below for textarea testing purposes */ /* opacity: 1 !important; */ } From 971874b9c82081542fec6572b70cacf4b4c7dc21 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Mon, 14 Aug 2023 15:53:59 -0700 Subject: [PATCH 022/223] half baked --- .../browser/controller/cellOutputActions.ts | 9 +++++++++ .../contrib/notebook/browser/notebookBrowser.ts | 2 ++ .../notebook/browser/notebookEditorWidget.ts | 4 ++++ .../browser/view/renderers/backLayerWebView.ts | 13 +++++++++++++ .../browser/view/renderers/webviewMessages.ts | 6 ++++++ 5 files changed, 34 insertions(+) diff --git a/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts index 2b18ac4cca1..b2e3e259842 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts @@ -12,6 +12,8 @@ import { NOTEBOOK_CELL_HAS_OUTPUTS } from 'vs/workbench/contrib/notebook/common/ import * as icons from 'vs/workbench/contrib/notebook/browser/notebookIcons'; import { ILogService } from 'vs/platform/log/common/log'; import { copyCellOutput } from 'vs/workbench/contrib/notebook/browser/contrib/clipboard/cellOutputClipboard'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { NotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookEditor'; export const COPY_OUTPUT_COMMAND_ID = 'notebook.cellOutput.copyToClipboard'; @@ -37,5 +39,12 @@ registerAction2(class CopyCellOutputAction extends NotebookAction { const logService = accessor.get(ILogService); copyCellOutput(mimeType, outputViewModel, clipboardService, logService); + + if (mimeType === 'image/png') { + const editorService = accessor.get(IEditorService); + const editor = editorService.activeEditorPane?.getControl() as NotebookEditor; + + + } } }); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 71bd138fdf8..6f76eb9dcc7 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -549,6 +549,8 @@ export interface INotebookEditor { */ updateOutput(cell: ICellViewModel, output: IInsetRenderOutput, offset: number): Promise; + copyImage(cellOutput: ICellOutputViewModel): Promise; + readonly onDidReceiveMessage: Event; /** diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index c418b358192..38c14e810da 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -2793,6 +2793,10 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD }); } + async copyImage(cellOutput: ICellOutputViewModel): Promise { + this._webview?.copyImage(cellOutput); + } + removeInset(output: ICellOutputViewModel) { this._insetModifyQueueByOutputId.queue(output.model.outputId, async () => { if (this._isDisposed || !this._webview) { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index fe247bf9a85..e12fa98cb9c 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -1543,6 +1543,19 @@ export class BackLayerWebView extends Themable { return; } + async copyImage(output: ICellOutputViewModel): Promise { + const mimeType = output.pickedMimeType?.mimeType; + const imageOutput = output.model.outputs.find(out => out.mime === mimeType); + if (!imageOutput) { + return; + } + + this._sendMessageToWebview({ + type: 'copyImage', + data: imageOutput.data.buffer + }); + } + removeInsets(outputs: readonly ICellOutputViewModel[]): void { if (this._disposed) { return; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts index 35cac90d4aa..cade87b9693 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts @@ -266,6 +266,11 @@ export interface IShowOutputMessage { readonly content?: ICreationContent; } +export interface ICopyOutputImage { + readonly type: 'copyImage'; + readonly data: Uint8Array; +} + export interface IFocusOutputMessage { readonly type: 'focus-output'; readonly cellId: string; @@ -527,6 +532,7 @@ export type ToWebviewMessage = IClearMessage | IClearOutputRequestMessage | IHideOutputMessage | IShowOutputMessage | + ICopyOutputImage | IUpdateControllerPreloadsMessage | IUpdateRenderersMessage | IUpdateDecorationsMessage | From 3185000be90e9619527162475a5166ee6db1c323 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 14 Aug 2023 18:24:51 -0600 Subject: [PATCH 023/223] Add prompt text to variable resolution --- .../api/browser/mainThreadChatVariables.ts | 4 ++-- .../workbench/api/common/extHost.protocol.ts | 2 +- .../api/common/extHostChatVariables.ts | 4 ++-- .../contrib/chat/common/chatVariables.ts | 23 ++----------------- .../vscode.proposed.chatVariables.d.ts | 10 ++++---- 5 files changed, 13 insertions(+), 30 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadChatVariables.ts b/src/vs/workbench/api/browser/mainThreadChatVariables.ts index eaea216e529..bc7fff86be1 100644 --- a/src/vs/workbench/api/browser/mainThreadChatVariables.ts +++ b/src/vs/workbench/api/browser/mainThreadChatVariables.ts @@ -26,8 +26,8 @@ export class MainThreadChatSlashCommands implements MainThreadChatVariablesShape } $registerVariable(handle: number, data: IChatVariableData): void { - const registration = this._chatVariablesService.registerVariable(data, token => { - return this._proxy.$resolveVariable(handle, token); + const registration = this._chatVariablesService.registerVariable(data, (messageText, token) => { + return this._proxy.$resolveVariable(handle, messageText, token); }); this._variables.set(handle, registration); } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index b76cfe05ba4..1b275984a65 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1152,7 +1152,7 @@ export interface MainThreadChatVariablesShape extends IDisposable { } export interface ExtHostChatVariablesShape { - $resolveVariable(handle: number, token: CancellationToken): Promise; + $resolveVariable(handle: number, messageText: string, token: CancellationToken): Promise; } export interface MainThreadInlineChatShape extends IDisposable { diff --git a/src/vs/workbench/api/common/extHostChatVariables.ts b/src/vs/workbench/api/common/extHostChatVariables.ts index 7225b148bd4..dec84402ba9 100644 --- a/src/vs/workbench/api/common/extHostChatVariables.ts +++ b/src/vs/workbench/api/common/extHostChatVariables.ts @@ -22,13 +22,13 @@ export class ExtHostChatVariables implements ExtHostChatVariablesShape { this._proxy = mainContext.getProxy(MainContext.MainThreadChatVariables); } - async $resolveVariable(handle: number, token: CancellationToken): Promise { + async $resolveVariable(handle: number, messageText: string, token: CancellationToken): Promise { const item = this._resolver.get(handle); if (!item) { return undefined; } try { - return (await item.resolver.resolve(item.data.name, token)) ?? undefined; + return (await item.resolver.resolve(item.data.name, { message: messageText }, token)) ?? undefined; } catch (err) { onUnexpectedExternalError(err); return undefined; diff --git a/src/vs/workbench/contrib/chat/common/chatVariables.ts b/src/vs/workbench/contrib/chat/common/chatVariables.ts index 1dc14e1de7b..574e166f8ba 100644 --- a/src/vs/workbench/contrib/chat/common/chatVariables.ts +++ b/src/vs/workbench/contrib/chat/common/chatVariables.ts @@ -22,7 +22,7 @@ export interface IChatRequestVariableValue { export interface IChatVariableResolver { // TODO should we spec "zoom level" - (token: CancellationToken): Promise; + (messageText: string, token: CancellationToken): Promise; } export const IChatVariablesService = createDecorator('IChatVariablesService'); @@ -46,25 +46,6 @@ export class ChatVariablesService implements IChatVariablesService { private _resolver = new Map(); constructor() { - [ - { name: 'selection', description: `The current editor's selection` }, - { name: 'editor', description: 'The current editor' }, - { name: 'debugConsole', description: 'The output in the debug console' }, - { name: 'vscodeAPI', description: 'The docs for the vscode extension API' }, - { name: 'git', description: 'The git details for your workspace' }, - { name: 'problems', description: 'The problems detected in your workspace' }, - { name: 'terminal', description: 'The current terminal buffer' }, - { name: 'terminalSelection', description: 'The current selection in the terminal' }, - { name: 'workspace', description: 'Details of your workspace' }, - { name: 'vscode', description: 'Commands and settings in vscode' }, - ].forEach(item => { - this.registerVariable(item, async () => { - return [{ - level: 'short', - value: item.name - }]; - }); - }); } async resolveVariables(prompt: string, token: CancellationToken): Promise> { @@ -78,7 +59,7 @@ export class ChatVariablesService implements IChatVariablesService { const candidate = match[2]; const data = this._resolver.get(candidate); if (data) { - jobs.push(data[1](token).then(value => { + jobs.push(data[1](prompt, token).then(value => { if (value) { resolvedVariables[candidate] = value; } diff --git a/src/vscode-dts/vscode.proposed.chatVariables.d.ts b/src/vscode-dts/vscode.proposed.chatVariables.d.ts index 9a4fec7537c..4d5d9f897ec 100644 --- a/src/vscode-dts/vscode.proposed.chatVariables.d.ts +++ b/src/vscode-dts/vscode.proposed.chatVariables.d.ts @@ -16,13 +16,15 @@ declare module 'vscode' { description?: string; } - export interface ChatVariableResolver { - resolve(name: string, token: CancellationToken): ProviderResult; + export interface ChatVariableContext { + message: string; + } + + export interface ChatVariableResolver { + resolve(name: string, context: ChatVariableContext, token: CancellationToken): ProviderResult; } - // Could be provider/resolver pattern, but how dynamic are they? export namespace chat { - // name: selection export function registerVariable(name: string, description: string, resolver: ChatVariableResolver): Disposable; } } From 936edafc229c3b1bef1251c230c0a9f7adbb4b4c Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 14 Aug 2023 18:28:24 -0600 Subject: [PATCH 024/223] cleanup --- .../contrib/chat/browser/contrib/chatInputEditorContrib.ts | 1 - src/vscode-dts/vscode.proposed.interactive.d.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts index b80d3b020f0..859d2bfbaca 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts @@ -44,7 +44,6 @@ class InputEditorDecorations extends Disposable { @ICodeEditorService private readonly codeEditorService: ICodeEditorService, @IThemeService private readonly themeService: IThemeService, @IChatService private readonly chatService: IChatService, - // @IChatVariablesService private readonly chatVariablesService: IChatVariablesService, ) { super(); diff --git a/src/vscode-dts/vscode.proposed.interactive.d.ts b/src/vscode-dts/vscode.proposed.interactive.d.ts index a6046ff0c1e..a294597ec6a 100644 --- a/src/vscode-dts/vscode.proposed.interactive.d.ts +++ b/src/vscode-dts/vscode.proposed.interactive.d.ts @@ -105,7 +105,6 @@ declare module 'vscode' { args: any; } - export interface InteractiveRequest { session: InteractiveSession; message: string | InteractiveSessionReplyFollowup; From 6f36672c0d0fb560762d3d881182b3da0ee4a1de Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 14 Aug 2023 18:22:02 -0700 Subject: [PATCH 025/223] Add experimental default drop setting and fix `yieldTo` ordering (#190459) For #179430 --- .../browser/dropIntoEditorContribution.ts | 22 ++++++- .../browser/dropIntoEditorController.ts | 32 +++++++-- .../contrib/dropOrPasteInto/browser/edit.ts | 63 +++++++++++++++--- .../test/browser/editSort.test.ts | 65 +++++++++++++++++++ 4 files changed, 165 insertions(+), 17 deletions(-) create mode 100644 src/vs/editor/contrib/dropOrPasteInto/test/browser/editSort.test.ts diff --git a/src/vs/editor/contrib/dropOrPasteInto/browser/dropIntoEditorContribution.ts b/src/vs/editor/contrib/dropOrPasteInto/browser/dropIntoEditorContribution.ts index a57d194fc7f..4817431d189 100644 --- a/src/vs/editor/contrib/dropOrPasteInto/browser/dropIntoEditorContribution.ts +++ b/src/vs/editor/contrib/dropOrPasteInto/browser/dropIntoEditorContribution.ts @@ -6,11 +6,14 @@ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorCommand, EditorContributionInstantiation, ServicesAccessor, registerEditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import { editorConfigurationBaseNode } from 'vs/editor/common/config/editorConfigurationSchema'; import { registerEditorFeature } from 'vs/editor/common/editorFeatures'; import { DefaultDropProvidersFeature } from 'vs/editor/contrib/dropOrPasteInto/browser/defaultProviders'; +import * as nls from 'vs/nls'; +import { Extensions as ConfigurationExtensions, ConfigurationScope, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { DropIntoEditorController, changeDropTypeCommandId, dropWidgetVisibleCtx } from './dropIntoEditorController'; - +import { Registry } from 'vs/platform/registry/common/platform'; +import { DropIntoEditorController, changeDropTypeCommandId, defaultProviderConfig, dropWidgetVisibleCtx } from './dropIntoEditorController'; registerEditorContribution(DropIntoEditorController.ID, DropIntoEditorController, EditorContributionInstantiation.BeforeFirstInteraction); @@ -32,3 +35,18 @@ registerEditorCommand(new class extends EditorCommand { }); registerEditorFeature(DefaultDropProvidersFeature); + +Registry.as(ConfigurationExtensions.Configuration).registerConfiguration({ + ...editorConfigurationBaseNode, + properties: { + [defaultProviderConfig]: { + type: 'object', + scope: ConfigurationScope.LANGUAGE_OVERRIDABLE, + description: nls.localize('defaultProviderDescription', "Configures the default drop provider to use for content of a given mime type."), + default: {}, + additionalProperties: { + type: 'string', + }, + }, + } +}); diff --git a/src/vs/editor/contrib/dropOrPasteInto/browser/dropIntoEditorController.ts b/src/vs/editor/contrib/dropOrPasteInto/browser/dropIntoEditorController.ts index ca3ed7dfabb..48dae75565c 100644 --- a/src/vs/editor/contrib/dropOrPasteInto/browser/dropIntoEditorController.ts +++ b/src/vs/editor/contrib/dropOrPasteInto/browser/dropIntoEditorController.ts @@ -5,7 +5,7 @@ import { coalesce } from 'vs/base/common/arrays'; import { CancelablePromise, createCancelablePromise, raceCancellation } from 'vs/base/common/async'; -import { VSDataTransfer } from 'vs/base/common/dataTransfer'; +import { VSDataTransfer, matchesMimeType } from 'vs/base/common/dataTransfer'; import { Disposable } from 'vs/base/common/lifecycle'; import { toExternalVSDataTransfer } from 'vs/editor/browser/dnd'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; @@ -13,7 +13,7 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { IPosition } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; -import { DocumentOnDropEditProvider } from 'vs/editor/common/languages'; +import { DocumentOnDropEdit, DocumentOnDropEditProvider } from 'vs/editor/common/languages'; import { ITextModel } from 'vs/editor/common/model'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { DraggedTreeItemsIdentifier } from 'vs/editor/common/services/treeViewsDnd'; @@ -21,11 +21,14 @@ import { ITreeViewsDnDService } from 'vs/editor/common/services/treeViewsDndServ import { CodeEditorStateFlag, EditorStateCancellationTokenSource } from 'vs/editor/contrib/editorState/browser/editorState'; import { InlineProgressManager } from 'vs/editor/contrib/inlineProgress/browser/inlineProgress'; import { localize } from 'vs/nls'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { LocalSelectionTransfer } from 'vs/platform/dnd/browser/dnd'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { sortEditsByYieldTo } from './edit'; import { PostEditWidgetManager } from './postEditWidget'; -import { sortEditsByYieldTo as sortEditsByYieldTo } from './edit'; + +export const defaultProviderConfig = 'editor.experimental.dropIntoEditor.defaultProvider'; export const changeDropTypeCommandId = 'editor.changeDropType'; @@ -49,6 +52,7 @@ export class DropIntoEditorController extends Disposable implements IEditorContr constructor( editor: ICodeEditor, @IInstantiationService instantiationService: IInstantiationService, + @IConfigurationService private readonly _configService: IConfigurationService, @ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService, @ITreeViewsDnDService private readonly _treeViewsDragAndDropService: ITreeViewsDnDService ) { @@ -108,9 +112,10 @@ export class DropIntoEditorController extends Disposable implements IEditorContr } if (edits.length) { + const activeEditIndex = this.getInitialActiveEditIndex(model, edits); const canShowWidget = editor.getOption(EditorOption.dropIntoEditor).showDropSelector === 'afterDrop'; // Pass in the parent token here as it tracks cancelling the entire drop operation - await this._postDropWidgetManager.applyEditAndShowIfNeeded([Range.fromPositions(position)], { activeEditIndex: 0, allEdits: edits }, canShowWidget, token); + await this._postDropWidgetManager.applyEditAndShowIfNeeded([Range.fromPositions(position)], { activeEditIndex, allEdits: edits }, canShowWidget, token); } } finally { tokenSource.dispose(); @@ -124,7 +129,7 @@ export class DropIntoEditorController extends Disposable implements IEditorContr this._currentOperation = p; } - private async getDropEdits(providers: DocumentOnDropEditProvider[], model: ITextModel, position: IPosition, dataTransfer: VSDataTransfer, tokenSource: EditorStateCancellationTokenSource) { + private async getDropEdits(providers: readonly DocumentOnDropEditProvider[], model: ITextModel, position: IPosition, dataTransfer: VSDataTransfer, tokenSource: EditorStateCancellationTokenSource) { const results = await raceCancellation(Promise.all(providers.map(async provider => { try { const edit = await provider.provideDocumentOnDropEdits(model, position, dataTransfer, tokenSource.token); @@ -136,9 +141,22 @@ export class DropIntoEditorController extends Disposable implements IEditorContr } return undefined; })), tokenSource.token); + const edits = coalesce(results ?? []); - sortEditsByYieldTo(edits); - return edits; + return sortEditsByYieldTo(edits); + } + + private getInitialActiveEditIndex(model: ITextModel, edits: ReadonlyArray) { + const preferredProviders = this._configService.getValue>(defaultProviderConfig, { resource: model.uri }); + for (const [configMime, desiredId] of Object.entries(preferredProviders)) { + const editIndex = edits.findIndex(edit => + desiredId === edit.providerId + && edit.handledMimeType && matchesMimeType(configMime, [edit.handledMimeType])); + if (editIndex >= 0) { + return editIndex; + } + } + return 0; } private async extractDataTransferData(dragEvent: DragEvent): Promise { diff --git a/src/vs/editor/contrib/dropOrPasteInto/browser/edit.ts b/src/vs/editor/contrib/dropOrPasteInto/browser/edit.ts index f117d88549a..e7b42fe7843 100644 --- a/src/vs/editor/contrib/dropOrPasteInto/browser/edit.ts +++ b/src/vs/editor/contrib/dropOrPasteInto/browser/edit.ts @@ -32,21 +32,68 @@ export function sortEditsByYieldTo(edits: T[]): void { +}>(edits: readonly T[]): T[] { function yieldsTo(yTo: DropYieldTo, other: T): boolean { return ('providerId' in yTo && yTo.providerId === other.providerId) || ('mimeType' in yTo && yTo.mimeType === other.handledMimeType); } - edits.sort((a, b) => { - if (a.yieldTo?.some(yTo => yieldsTo(yTo, b))) { - return 1; + // Build list of nodes each node yields to + const yieldsToMap = new Map(); + for (const edit of edits) { + for (const yTo of edit.yieldTo ?? []) { + for (const other of edits) { + if (other === edit) { + continue; + } + + if (yieldsTo(yTo, other)) { + let arr = yieldsToMap.get(edit); + if (!arr) { + arr = []; + yieldsToMap.set(edit, arr); + } + arr.push(other); + } + } + } + } + + if (!yieldsToMap.size) { + return Array.from(edits); + } + + // Topological sort + const visited = new Set(); + const tempStack: T[] = []; + + function visit(nodes: T[]): T[] { + if (!nodes.length) { + return []; } - if (b.yieldTo?.some(yTo => yieldsTo(yTo, a))) { - return -1; + const node = nodes[0]; + if (tempStack.includes(node)) { + console.warn(`Yield to cycle detected for ${node.providerId}`); + return nodes; } - return 0; - }); + if (visited.has(node)) { + return visit(nodes.slice(1)); + } + + let pre: T[] = []; + const yTo = yieldsToMap.get(node); + if (yTo) { + tempStack.push(node); + pre = visit(yTo); + tempStack.pop(); + } + + visited.add(node); + + return [...pre, node, ...visit(nodes.slice(1))]; + } + + return visit(Array.from(edits)); } diff --git a/src/vs/editor/contrib/dropOrPasteInto/test/browser/editSort.test.ts b/src/vs/editor/contrib/dropOrPasteInto/test/browser/editSort.test.ts new file mode 100644 index 00000000000..44939652de3 --- /dev/null +++ b/src/vs/editor/contrib/dropOrPasteInto/test/browser/editSort.test.ts @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import * as assert from 'assert'; +import { DocumentOnDropEdit } from 'vs/editor/common/languages'; +import { sortEditsByYieldTo } from 'vs/editor/contrib/dropOrPasteInto/browser/edit'; + +type DropEdit = DocumentOnDropEdit & { providerId: string | undefined }; + +function createTestEdit(providerId: string, args?: Partial): DropEdit { + return { + label: '', + insertText: '', + providerId, + ...args, + }; +} + +suite('sortEditsByYieldTo', () => { + test('Should noop for empty edits', () => { + const edits: DropEdit[] = []; + + assert.deepStrictEqual(sortEditsByYieldTo(edits), []); + }); + + test('Yielded to edit should get sorted after target', () => { + const edits: DropEdit[] = [ + createTestEdit('a', { yieldTo: [{ providerId: 'b' }] }), + createTestEdit('b'), + ]; + assert.deepStrictEqual(sortEditsByYieldTo(edits).map(x => x.providerId), ['b', 'a']); + }); + + test('Should handle chain of yield to', () => { + { + const edits: DropEdit[] = [ + createTestEdit('c', { yieldTo: [{ providerId: 'a' }] }), + createTestEdit('a', { yieldTo: [{ providerId: 'b' }] }), + createTestEdit('b'), + ]; + + assert.deepStrictEqual(sortEditsByYieldTo(edits).map(x => x.providerId), ['b', 'a', 'c']); + } + { + const edits: DropEdit[] = [ + createTestEdit('a', { yieldTo: [{ providerId: 'b' }] }), + createTestEdit('c', { yieldTo: [{ providerId: 'a' }] }), + createTestEdit('b'), + ]; + + assert.deepStrictEqual(sortEditsByYieldTo(edits).map(x => x.providerId), ['b', 'a', 'c']); + } + }); + + test(`Should not reorder when yield to isn't used`, () => { + const edits: DropEdit[] = [ + createTestEdit('c', { yieldTo: [{ providerId: 'x' }] }), + createTestEdit('a', { yieldTo: [{ providerId: 'y' }] }), + createTestEdit('b'), + ]; + + assert.deepStrictEqual(sortEditsByYieldTo(edits).map(x => x.providerId), ['c', 'a', 'b']); + }); +}); From 69adc1fa64ae886718fdc4556935d305d4c45d30 Mon Sep 17 00:00:00 2001 From: tisilent Date: Tue, 15 Aug 2023 11:11:03 +0800 Subject: [PATCH 026/223] Find with selected values when reveal TerminalFind --- .../terminalContrib/find/browser/terminalFindWidget.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/find/browser/terminalFindWidget.ts b/src/vs/workbench/contrib/terminalContrib/find/browser/terminalFindWidget.ts index 780540bc02b..f730c159463 100644 --- a/src/vs/workbench/contrib/terminalContrib/find/browser/terminalFindWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/find/browser/terminalFindWidget.ts @@ -81,9 +81,9 @@ export class TerminalFindWidget extends SimpleFindWidget { override reveal(): void { const initialInput = this._instance.hasSelection() && !this._instance.selection!.includes('\n') ? this._instance.selection : undefined; const xterm = this._instance.xterm; - if (xterm && this.inputValue && this.inputValue !== '') { + if (xterm && initialInput && initialInput !== '') { // trigger highlight all matches - this._findPreviousWithEvent(xterm, this.inputValue, { incremental: true, regex: this._getRegexValue(), wholeWord: this._getWholeWordValue(), caseSensitive: this._getCaseSensitiveValue() }).then(foundMatch => { + this._findPreviousWithEvent(xterm, initialInput, { incremental: true, regex: this._getRegexValue(), wholeWord: this._getWholeWordValue(), caseSensitive: this._getCaseSensitiveValue() }).then(foundMatch => { this.updateButtons(foundMatch); this._register(Event.once(xterm.onDidChangeSelection)(() => xterm.clearActiveSearchDecoration())); }); From cb94a70e1f43b4115e86907165c80d7870f10e18 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 14 Aug 2023 21:05:18 -0700 Subject: [PATCH 027/223] Bump distro (#190470) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5ecb6e3e82c..91a81353fdc 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.82.0", - "distro": "510a8d740286d6895197995d1edc978ffe23cadf", + "distro": "3cfcd1c2c54a103b75e0b926951640991bc579a4", "author": { "name": "Microsoft Corporation" }, From 66e5308af303f988b213e4a0847d3155e99142b8 Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 15 Aug 2023 14:36:18 +0200 Subject: [PATCH 028/223] use proper enum for `ChatVariableLevel` --- .../workbench/api/common/extHost.api.impl.ts | 1 + src/vs/workbench/api/common/extHostChat.ts | 8 +++- .../api/common/extHostChatVariables.ts | 8 +++- .../api/common/extHostTypeConverters.ts | 43 +++++++++++++++++++ src/vs/workbench/api/common/extHostTypes.ts | 6 +++ .../vscode.proposed.chatVariables.d.ts | 9 +++- 6 files changed, 70 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 8350a98ba6c..1673b845c0c 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1371,6 +1371,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I TerminalOutputAnchor: extHostTypes.TerminalOutputAnchor, ChatMessage: extHostTypes.ChatMessage, ChatMessageRole: extHostTypes.ChatMessageRole, + ChatVariableLevel: extHostTypes.ChatVariableLevel, CallHierarchyIncomingCall: extHostTypes.CallHierarchyIncomingCall, CallHierarchyItem: extHostTypes.CallHierarchyItem, CallHierarchyOutgoingCall: extHostTypes.CallHierarchyOutgoingCall, diff --git a/src/vs/workbench/api/common/extHostChat.ts b/src/vs/workbench/api/common/extHostChat.ts index 5d1cbd4474a..db4f90727ba 100644 --- a/src/vs/workbench/api/common/extHostChat.ts +++ b/src/vs/workbench/api/common/extHostChat.ts @@ -200,9 +200,15 @@ export class ExtHostChat implements ExtHostChatShape { const requestObj: vscode.InteractiveRequest = { session: realSession, message: typeof request.message === 'string' ? request.message : typeConvert.ChatReplyFollowup.to(request.message), - variables: request.variables ?? {} + variables: {} }; + if (request.variables) { + for (const key of Object.keys(request.variables)) { + requestObj.variables[key] = request.variables[key].map(typeConvert.ChatVariable.to); + } + } + const stopWatch = StopWatch.create(false); let firstProgress: number | undefined; const progressObj: vscode.Progress = { diff --git a/src/vs/workbench/api/common/extHostChatVariables.ts b/src/vs/workbench/api/common/extHostChatVariables.ts index dec84402ba9..8f142a13a99 100644 --- a/src/vs/workbench/api/common/extHostChatVariables.ts +++ b/src/vs/workbench/api/common/extHostChatVariables.ts @@ -10,6 +10,7 @@ import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensio import { ExtHostChatVariablesShape, IMainContext, MainContext, MainThreadChatVariablesShape } from 'vs/workbench/api/common/extHost.protocol'; import { IChatRequestVariableValue, IChatVariableData } from 'vs/workbench/contrib/chat/common/chatVariables'; import { onUnexpectedExternalError } from 'vs/base/common/errors'; +import { ChatVariable } from 'vs/workbench/api/common/extHostTypeConverters'; export class ExtHostChatVariables implements ExtHostChatVariablesShape { @@ -28,11 +29,14 @@ export class ExtHostChatVariables implements ExtHostChatVariablesShape { return undefined; } try { - return (await item.resolver.resolve(item.data.name, { message: messageText }, token)) ?? undefined; + const value = await item.resolver.resolve(item.data.name, { message: messageText }, token); + if (value) { + return value.map(ChatVariable.from); + } } catch (err) { onUnexpectedExternalError(err); - return undefined; } + return undefined; } registerVariableResolver(extension: IExtensionDescription, name: string, description: string, resolver: vscode.ChatVariableResolver): IDisposable { diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 37868738d64..d0e1bc99d4a 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -45,6 +45,7 @@ import { ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/ed import type * as vscode from 'vscode'; import * as types from './extHostTypes'; import * as chatProvider from 'vs/workbench/contrib/chat/common/chatProvider'; +import { IChatRequestVariableValue } from 'vs/workbench/contrib/chat/common/chatVariables'; export namespace Command { @@ -2200,6 +2201,48 @@ export namespace ChatMessageRole { } } +export namespace ChatVariable { + export function to(variable: IChatRequestVariableValue): vscode.ChatVariableValue { + return { + level: ChatVariableLevel.to(variable.level), + value: variable.value, + description: variable.description + }; + } + + export function from(variable: vscode.ChatVariableValue): IChatRequestVariableValue { + return { + level: ChatVariableLevel.from(variable.level), + value: variable.value, + description: variable.description + }; + } +} + +export namespace ChatVariableLevel { + + + export function to(level: 'short' | 'medium' | 'full'): vscode.ChatVariableLevel { + switch (level) { + case 'short': return types.ChatVariableLevel.Short; + case 'medium': return types.ChatVariableLevel.Medium; + case 'full': + default: + return types.ChatVariableLevel.Full; + } + } + export function from(level: vscode.ChatVariableLevel): 'short' | 'medium' | 'full' { + switch (level) { + case types.ChatVariableLevel.Short: return 'short'; + case types.ChatVariableLevel.Medium: return 'medium'; + case types.ChatVariableLevel.Full: + default: + return 'full'; + } + } +} + + export namespace TerminalQuickFix { export function from(quickFix: vscode.TerminalQuickFixExecuteTerminalCommand | vscode.TerminalQuickFixOpener | vscode.Command, converter: Command.ICommandsConverter, disposables: DisposableStore): extHostProtocol.ITerminalQuickFixExecuteTerminalCommandDto | extHostProtocol.ITerminalQuickFixOpenerDto | extHostProtocol.ICommandDto | undefined { if ('terminalCommand' in quickFix) { diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 723e2e0c500..be6ebc586b2 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -4097,6 +4097,12 @@ export enum ChatMessageRole { Function = 3, } +export enum ChatVariableLevel { + Short = 1, + Medium = 2, + Full = 3 +} + export class ChatMessage implements vscode.ChatMessage { role: ChatMessageRole; diff --git a/src/vscode-dts/vscode.proposed.chatVariables.d.ts b/src/vscode-dts/vscode.proposed.chatVariables.d.ts index 4d5d9f897ec..6d9de44be86 100644 --- a/src/vscode-dts/vscode.proposed.chatVariables.d.ts +++ b/src/vscode-dts/vscode.proposed.chatVariables.d.ts @@ -9,9 +9,14 @@ declare module 'vscode' { variables: Record; } + export enum ChatVariableLevel { + Short = 1, + Medium = 2, + Full = 3 + } + export interface ChatVariableValue { - // eslint-disable-next-line local/vscode-dts-literal-or-types, local/vscode-dts-string-type-literals - level: 'short' | 'medium' | 'full'; + level: ChatVariableLevel; value: string; description?: string; } From a715f952261b44d2a11ce26cc68f77ee59050988 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 15 Aug 2023 17:18:02 +0200 Subject: [PATCH 029/223] should not update max dimensions when contents have changed, only on resize --- src/vs/editor/contrib/hover/browser/contentHover.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/editor/contrib/hover/browser/contentHover.ts b/src/vs/editor/contrib/hover/browser/contentHover.ts index 691545a3e9e..c06e6bac4a7 100644 --- a/src/vs/editor/contrib/hover/browser/contentHover.ts +++ b/src/vs/editor/contrib/hover/browser/contentHover.ts @@ -854,7 +854,6 @@ export class ContentHoverWidget extends ResizableContentWidget { width = dom.getTotalWidth(containerDomNode); this._contentWidth = width; this._updateMinimumWidth(); - this._updateResizableNodeMaxDimensions(); this._resizableNode.layout(height, width); if (this._hasHorizontalScrollbar()) { From 75303d905e8aec22d7a361ddfba02eeb60cd3fd9 Mon Sep 17 00:00:00 2001 From: yiliang114 <1204183885@qq.com> Date: Wed, 16 Aug 2023 00:19:05 +0800 Subject: [PATCH 030/223] chore: remove build output of compilation.js --- build/lib/compilation.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/lib/compilation.js b/build/lib/compilation.js index c4b6dd98af5..8e82e1cd234 100644 --- a/build/lib/compilation.js +++ b/build/lib/compilation.js @@ -287,4 +287,4 @@ exports.watchApiProposalNamesTask = task.define('watch-api-proposal-names', () = .pipe(util.debounce(task)) .pipe(gulp.dest('src')); }); -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tcGlsYXRpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJjb21waWxhdGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztnR0FHZ0c7OztBQUVoRyxtQ0FBbUM7QUFDbkMseUJBQXlCO0FBQ3pCLDZCQUE2QjtBQUM3Qiw2QkFBNkI7QUFDN0IsMENBQTBDO0FBQzFDLDZCQUE2QjtBQUM3Qix5Q0FBNEM7QUFDNUMsK0JBQStCO0FBQy9CLHNDQUFzQztBQUN0QywwQ0FBMEM7QUFDMUMseUJBQXlCO0FBQ3pCLGlDQUFrQztBQUNsQyw4QkFBOEI7QUFDOUIsK0JBQStCO0FBQy9CLDBDQUF5QztBQUV6QyxNQUFNLEtBQUssR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUM7QUFFakMsdUVBQXVFO0FBRXZFLE1BQU0sUUFBUSxHQUFHLElBQUEseUJBQWMsR0FBRSxDQUFDO0FBRWxDLFNBQVMsNEJBQTRCLENBQUMsR0FBVztJQUNoRCxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxTQUFTLEdBQUcsRUFBRSxDQUFDLENBQUM7SUFDckQsTUFBTSxPQUFPLEdBQXVCLEVBQUUsQ0FBQztJQUN2QyxPQUFPLENBQUMsT0FBTyxHQUFHLEtBQUssQ0FBQztJQUN4QixPQUFPLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQztJQUN6QixJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMscUJBQXFCLENBQUMsRUFBRSxFQUFFLHNDQUFzQztRQUMvRSxPQUFPLENBQUMsU0FBUyxHQUFHLEtBQUssQ0FBQztLQUMxQjtJQUNELE9BQU8sQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO0lBQzFCLE9BQU8sQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO0lBQzFCLE9BQU8sQ0FBQyxVQUFVLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUM3QyxPQUFPLENBQUMsT0FBTyxHQUFHLE1BQU0sQ0FBQyxJQUFJ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 \ No newline at end of file +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tcGlsYXRpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJjb21waWxhdGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztnR0FHZ0c7OztBQUVoRyxtQ0FBbUM7QUFDbkMseUJBQXlCO0FBQ3pCLDZCQUE2QjtBQUM3Qiw2QkFBNkI7QUFDN0IsMENBQTBDO0FBQzFDLDZCQUE2QjtBQUM3Qix5Q0FBNEM7QUFDNUMsK0JBQStCO0FBQy9CLHNDQUFzQztBQUN0QywwQ0FBMEM7QUFDMUMseUJBQXlCO0FBQ3pCLGlDQUFrQztBQUNsQyw4QkFBOEI7QUFDOUIsK0JBQStCO0FBQy9CLDBDQUF5QztBQUV6QyxNQUFNLEtBQUssR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUM7QUFHakMsdUVBQXVFO0FBRXZFLE1BQU0sUUFBUSxHQUFHLElBQUEseUJBQWMsR0FBRSxDQUFDO0FBRWxDLFNBQVMsNEJBQTRCLENBQUMsR0FBVztJQUNoRCxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxTQUFTLEdBQUcsRUFBRSxDQUFDLENBQUM7SUFDckQsTUFBTSxPQUFPLEdBQXVCLEVBQUUsQ0FBQztJQUN2QyxPQUFPLENBQUMsT0FBTyxHQUFHLEtBQUssQ0FBQztJQUN4QixPQUFPLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQztJQUN6QixJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMscUJBQXFCLENBQUMsRUFBRSxFQUFFLHNDQUFzQztRQUMvRSxPQUFPLENBQUMsU0FBUyxHQUFHLEtBQUssQ0FBQztLQUMxQjtJQUNELE9BQU8sQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO0lBQzFCLE9BQU8sQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO0lBQzFCLE9BQU8sQ0FBQyxVQUFVLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUM3QyxPQUFPLENBQUMsT0FBTyxHQUFHLE1BQU0sQ0FBQyxJQUFJ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 \ No newline at end of file From ca333cd5ead014d9034e2b4f515084cb1c2c53af Mon Sep 17 00:00:00 2001 From: yiliang114 <1204183885@qq.com> Date: Wed, 16 Aug 2023 00:20:12 +0800 Subject: [PATCH 031/223] chore: remove build output of compilation.js --- build/lib/compilation.js | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/build/lib/compilation.js b/build/lib/compilation.js index 8e82e1cd234..2270e054ba5 100644 --- a/build/lib/compilation.js +++ b/build/lib/compilation.js @@ -82,19 +82,17 @@ function createCompile(src, build, emitError, transpileOnly) { return pipeline; } function transpileTask(src, out, swc) { - const task = () => { + return function () { const transpile = createCompile(src, false, true, { swc }); const srcPipe = gulp.src(`${src}/**`, { base: `${src}` }); return srcPipe .pipe(transpile()) .pipe(gulp.dest(out)); }; - task.taskName = `transpile-${path.basename(src)}`; - return task; } exports.transpileTask = transpileTask; function compileTask(src, out, build, options = {}) { - const task = () => { + return function () { if (os.totalmem() < 4000000000) { throw new Error('compilation requires 4GB of RAM'); } @@ -130,12 +128,10 @@ function compileTask(src, out, build, options = {}) { .pipe(compile()) .pipe(gulp.dest(out)); }; - task.taskName = `compile-${path.basename(src)}`; - return task; } exports.compileTask = compileTask; function watchTask(out, build) { - const task = () => { + return function () { const compile = createCompile('src', build, false, false); const src = gulp.src('src/**', { base: 'src' }); const watchSrc = watch('src/**', { base: 'src', readDelay: 200 }); @@ -146,8 +142,6 @@ function watchTask(out, build) { .pipe(util.incremental(compile, src, true)) .pipe(gulp.dest(out)); }; - task.taskName = `watch-${path.basename(out)}`; - return task; } exports.watchTask = watchTask; const REPO_SRC_FOLDER = path.join(__dirname, '../../src'); @@ -287,4 +281,4 @@ exports.watchApiProposalNamesTask = task.define('watch-api-proposal-names', () = .pipe(util.debounce(task)) .pipe(gulp.dest('src')); }); -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tcGlsYXRpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJjb21waWxhdGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztnR0FHZ0c7OztBQUVoRyxtQ0FBbUM7QUFDbkMseUJBQXlCO0FBQ3pCLDZCQUE2QjtBQUM3Qiw2QkFBNkI7QUFDN0IsMENBQTBDO0FBQzFDLDZCQUE2QjtBQUM3Qix5Q0FBNEM7QUFDNUMsK0JBQStCO0FBQy9CLHNDQUFzQztBQUN0QywwQ0FBMEM7QUFDMUMseUJBQXlCO0FBQ3pCLGlDQUFrQztBQUNsQyw4QkFBOEI7QUFDOUIsK0JBQStCO0FBQy9CLDBDQUF5QztBQUV6QyxNQUFNLEtBQUssR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUM7QUFHakMsdUVBQXVFO0FBRXZFLE1BQU0sUUFBUSxHQUFHLElBQUEseUJBQWMsR0FBRSxDQUFDO0FBRWxDLFNBQVMsNEJBQTRCLENBQUMsR0FBVztJQUNoRCxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxTQUFTLEdBQUcsRUFBRSxDQUFDLENBQUM7SUFDckQsTUFBTSxPQUFPLEdBQXVCLEVBQUUsQ0FBQztJQUN2QyxPQUFPLENBQUMsT0FBTyxHQUFHLEtBQUssQ0FBQztJQUN4QixPQUFPLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQztJQUN6QixJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMscUJBQXFCLENBQUMsRUFBRSxFQUFFLHNDQUFzQztRQUMvRSxPQUFPLENBQUMsU0FBUyxHQUFHLEtBQUssQ0FBQztLQUMxQjtJQUNELE9BQU8sQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO0lBQzFCLE9BQU8sQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO0lBQzFCLE9BQU8sQ0FBQyxVQUFVLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUM3QyxPQUFPLENBQUMsT0FBTyxHQUFHLE1BQU0sQ0FBQyxJQUFJ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 \ No newline at end of file +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tcGlsYXRpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJjb21waWxhdGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztnR0FHZ0c7OztBQUVoRyxtQ0FBbUM7QUFDbkMseUJBQXlCO0FBQ3pCLDZCQUE2QjtBQUM3Qiw2QkFBNkI7QUFDN0IsMENBQTBDO0FBQzFDLDZCQUE2QjtBQUM3Qix5Q0FBNEM7QUFDNUMsK0JBQStCO0FBQy9CLHNDQUFzQztBQUN0QywwQ0FBMEM7QUFDMUMseUJBQXlCO0FBQ3pCLGlDQUFrQztBQUNsQyw4QkFBOEI7QUFDOUIsK0JBQStCO0FBQy9CLDBDQUF5QztBQUV6QyxNQUFNLEtBQUssR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUM7QUFHakMsdUVBQXVFO0FBRXZFLE1BQU0sUUFBUSxHQUFHLElBQUEseUJBQWMsR0FBRSxDQUFDO0FBRWxDLFNBQVMsNEJBQTRCLENBQUMsR0FBVztJQUNoRCxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxTQUFTLEdBQUcsRUFBRSxDQUFDLENBQUM7SUFDckQsTUFBTSxPQUFPLEdBQXVCLEVBQUUsQ0FBQztJQUN2QyxPQUFPLENBQUMsT0FBTyxHQUFHLEtBQUssQ0FBQztJQUN4QixPQUFPLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQztJQUN6QixJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMscUJBQXFCLENBQUMsRUFBRSxFQUFFLHNDQUFzQztRQUMvRSxPQUFPLENBQUMsU0FBUyxHQUFHLEtBQUssQ0FBQztLQUMxQjtJQUNELE9BQU8sQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO0lBQzFCLE9BQU8sQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO0lBQzFCLE9BQU8sQ0FBQyxVQUFVLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUM3QyxPQUFPLENBQUMsT0FBTyxHQUFHLE1BQU0sQ0FBQyxJQUFJ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 \ No newline at end of file From 6d939f466ef5427a09e15647e33652f234d5b0f1 Mon Sep 17 00:00:00 2001 From: Robo Date: Wed, 16 Aug 2023 01:31:11 +0900 Subject: [PATCH 032/223] chore: bump electron@25.5.0 (#190490) * chore: bump electron@25.5.0 * chore: bump distro --- .yarnrc | 4 +-- build/checksums/electron.txt | 54 ++++++++++++++++++------------------ cgmanifest.json | 8 +++--- package.json | 4 +-- yarn.lock | 8 +++--- 5 files changed, 39 insertions(+), 39 deletions(-) diff --git a/.yarnrc b/.yarnrc index 6826ca67118..8f658afd4a9 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,5 +1,5 @@ disturl "https://electronjs.org/headers" -target "25.4.0" -ms_build_id "22958381" +target "25.5.0" +ms_build_id "23084831" runtime "electron" build_from_source "true" diff --git a/build/checksums/electron.txt b/build/checksums/electron.txt index c0d1e51c571..f5c22bc1c23 100644 --- a/build/checksums/electron.txt +++ b/build/checksums/electron.txt @@ -1,27 +1,27 @@ -fc51b54078335a8d687e0dda5e65e0b9916d9678d13d8f84b1dc91bceaac6b94 *electron-v25.4.0-darwin-arm64-symbols.zip -8a1f0cc5c8fea926360347f9448a766f56fe4fea8418ce11ca5755be933dcae2 *electron-v25.4.0-darwin-arm64.zip -a221e0fb48d178940e77bb96c8bfe26fb5f4eb5b916c93d7b771d0f216e9f310 *electron-v25.4.0-darwin-x64-symbols.zip -f6e65c0d27e99121e53eb52335ef42653108bfa5cb8de06c35853c253fafc000 *electron-v25.4.0-darwin-x64.zip -e1006e2a327f23fe73c172789a9957c83e1b7563358908c0d1afb6f2a95f305c *electron-v25.4.0-linux-arm64-symbols.zip -2225b64fbe7ff602160ad371ede9a4eb79534d865dbac3a6fcc252faf0214240 *electron-v25.4.0-linux-arm64.zip -7ff71baa0e0c55728569a3a05fb1aadd079e2dd79bbc7bb4ce460c8fefe1c422 *electron-v25.4.0-linux-armv7l-symbols.zip -71469e85d3b3bbf40b698ffb3e86b96eef854c2bc63096900d1c1a606f8b9bb4 *electron-v25.4.0-linux-armv7l.zip -28a29971aa65a72ead251bb3be5135b54a1a1c0a858e80959b877aea16975acb *electron-v25.4.0-linux-x64-symbols.zip -7cce96b1f33c7f6333d24f6966bd0613e6d95a9fd3e7fba4f20d11869adc22c6 *electron-v25.4.0-linux-x64.zip -615e880460216419d9b6dfa8cdf59d067ad3f629497e4419cd723351e544645a *electron-v25.4.0-win32-arm64-pdb.zip -548ac3cbfe5a4bd1c8628aa4a73cc424116c0870ab119cf8077993b932baf51b *electron-v25.4.0-win32-arm64-symbols.zip -c51677babba862a0b98ceee992b42eaf7d2c9e8e9dfb1496d15d7381b908dc72 *electron-v25.4.0-win32-arm64.zip -9a6271c5d9eaf07fb01b38bd743b2cdc0642bcf16f9add2724eccad2aea2b3a8 *electron-v25.4.0-win32-ia32-pdb.zip -e961dd15cfde46c9765eebd4f421e17850b85c3f25e0e6891b4f55a9834805f6 *electron-v25.4.0-win32-ia32-symbols.zip -14320e1f7db40e885ee101acd39698be58745cdfe6a519e0a5316269837f5bf7 *electron-v25.4.0-win32-ia32.zip -b055eb2b0bf037fc54f8af9d76c5894ffaffe6bd24e231da7ce847048ed60c97 *electron-v25.4.0-win32-x64-pdb.zip -0d84815a05dd0dd96266808d3cefde52cdea149c5faf2abb2d4d3c028e5f30d0 *electron-v25.4.0-win32-x64-symbols.zip -6f0c6da70a4daa50ec46c389896d08009ce14cdac5896c608e5af8956fd359de *electron-v25.4.0-win32-x64.zip -0e4fd74705d098d616b313b9078e74ba3b44ead2f3abd083acd8fab68048a563 *ffmpeg-v25.4.0-darwin-arm64.zip -a4c1b013448c5b9955d4927be7a72f8836a7af909c795aeecdb43086e76bd0d8 *ffmpeg-v25.4.0-darwin-x64.zip -bd52d57ff97fb56ac01a3482af905d04f0d4e9c13c53858c6d9f99957eca82da *ffmpeg-v25.4.0-linux-arm64.zip -9b3d09177fa1e63e2a6beecfa70aeec30aeb5c1873ff21128a68051c4e23f95d *ffmpeg-v25.4.0-linux-armv7l.zip -edc7b1c9f1a0733f109a2c0375a4e40c5bfe0bf28b7f06dcc76e1ada0aa2f125 *ffmpeg-v25.4.0-linux-x64.zip -39d5389ff83fd30bacfd82e3beea5cb95eaa46aaa7d2ddaccc705f2490210f2e *ffmpeg-v25.4.0-win32-arm64.zip -059c72026437c4d97ec55ef6fab85526d2fa1de3571c478a55dec3489487fdf4 *ffmpeg-v25.4.0-win32-ia32.zip -01871604f273c3206c808954c604afda0294da15dd02dd6360fe736e5b5c82a8 *ffmpeg-v25.4.0-win32-x64.zip +c3fb8cb4804143eb25fce55a179a6f2df8c215ed709104ec235c96e357b20f42 *electron-v25.5.0-darwin-arm64-symbols.zip +e6d2a09348d4fe7c9fbd92bf796489a95e625642f0f1ce96169212554cfa6841 *electron-v25.5.0-darwin-arm64.zip +15c28e613dfee0f7e46a296bb4aed64e17f9644d7ef19129aeb6480b8da230ab *electron-v25.5.0-darwin-x64-symbols.zip +a5c5c0b621daf8242258c89edb2387fbdf1c69125c984f8564d89b87927373e3 *electron-v25.5.0-darwin-x64.zip +69d60a69b7f692b9069cdf9e518bcbaca9cec561f40199cc87196929936a34ce *electron-v25.5.0-linux-arm64-symbols.zip +adec2ba09faf5f8d8af8997c8bed43c7712eba5546db1e7d06f8357bf4613921 *electron-v25.5.0-linux-arm64.zip +0fe7a5d152c9c401671e02ebcd9da34ab9bb0c28598ede3657077a79f8b3e70a *electron-v25.5.0-linux-armv7l-symbols.zip +eccb66e4a308a0bd2d90474894370e3d687e32f78672e6b5077c1822c7bc526b *electron-v25.5.0-linux-armv7l.zip +82bd9bc9e66f8ae802fe48a51b8d7e2fb599403e9715fa4b859190200a7376b1 *electron-v25.5.0-linux-x64-symbols.zip +485cbeb206fccfb4ed42f694100eebb80c9db6639b3537a95823c4fcb7f210cd *electron-v25.5.0-linux-x64.zip +ed2c2a7da571b53bcb336b9a2a024753a272df82ece45df83df888df51bf1912 *electron-v25.5.0-win32-arm64-pdb.zip +c316f6364e9b4cd61e19d4763c96abda7247a2c31ae7d30e81219c9a7754f11a *electron-v25.5.0-win32-arm64-symbols.zip +582ccbfc5a85a093f5639ee476bb5fef18c2d25dab4a60e5f5cb47e64c99f7fb *electron-v25.5.0-win32-arm64.zip +5776e650c23e3847b0c52d850f61c57a84a4fa30943c3cc82197250112911b5b *electron-v25.5.0-win32-ia32-pdb.zip +730b429ad2c4cae0fe3ba9600a6ee86c79dada77976bac1c75ca2404993495bc *electron-v25.5.0-win32-ia32-symbols.zip +7e6e68aa33a89c0d647575b06daf415a2401feb170ebb9cd795e221af321f751 *electron-v25.5.0-win32-ia32.zip +0792665fda9255b340a829cdd24601887a2ca8f04cc49f9a6d4557db0c0cd2f2 *electron-v25.5.0-win32-x64-pdb.zip +dc2459546951f8418e866857e9111dd83a0789d401906b2200c2f8dd59d9146b *electron-v25.5.0-win32-x64-symbols.zip +9bf7980fbc024ba77ea8ab3e2d32088a5f69bf32506a7d2db72ede17028abdf4 *electron-v25.5.0-win32-x64.zip +5b1ea601b737842eacf88d7456c4d14e697822753e9a08ede889cce9e20bafb2 *ffmpeg-v25.5.0-darwin-arm64.zip +37bf5c75edefc0b6735b44d0b5b06fdd427179a8501f6e93df9840283cdb4a95 *ffmpeg-v25.5.0-darwin-x64.zip +bd52d57ff97fb56ac01a3482af905d04f0d4e9c13c53858c6d9f99957eca82da *ffmpeg-v25.5.0-linux-arm64.zip +9b3d09177fa1e63e2a6beecfa70aeec30aeb5c1873ff21128a68051c4e23f95d *ffmpeg-v25.5.0-linux-armv7l.zip +edc7b1c9f1a0733f109a2c0375a4e40c5bfe0bf28b7f06dcc76e1ada0aa2f125 *ffmpeg-v25.5.0-linux-x64.zip +2e28767b3570ea247869a20988cddd23af710eb994d6099404f123390cedeba6 *ffmpeg-v25.5.0-win32-arm64.zip +715568eefd7267573a30186ade3de901587baeb1f013200d8ae50b35941b613f *ffmpeg-v25.5.0-win32-ia32.zip +29876504452aaa505f696642178968e24b8dc8cc4b055071e6f0f3f073088acc *ffmpeg-v25.5.0-win32-x64.zip diff --git a/cgmanifest.json b/cgmanifest.json index dfcdf4de6bb..574ec75d24d 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "chromium", "repositoryUrl": "https://chromium.googlesource.com/chromium/src", - "commitHash": "8bab63006effd27646be3d5c07eb09d3282c9b4d" + "commitHash": "88c0a3606e1a856ad43651130d27bb880e1833d6" } }, "licenseDetail": [ @@ -40,7 +40,7 @@ "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ], "isOnlyProductionDependency": true, - "version": "114.0.5735.248" + "version": "114.0.5735.289" }, { "component": { @@ -528,12 +528,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "d219fcc8b61dbeb459e92c90a069076f18a9a12a" + "commitHash": "34be316c404e84cdd967fa0e10fceeb6424eed90" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "25.4.0" + "version": "25.5.0" }, { "component": { diff --git a/package.json b/package.json index 91a81353fdc..2568174898b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.82.0", - "distro": "3cfcd1c2c54a103b75e0b926951640991bc579a4", + "distro": "505b0336f71abdfa7d1ed0dbaafa20c812fa58e7", "author": { "name": "Microsoft Corporation" }, @@ -150,7 +150,7 @@ "cssnano": "^4.1.11", "debounce": "^1.0.0", "deemon": "^1.8.0", - "electron": "25.4.0", + "electron": "25.5.0", "eslint": "8.36.0", "eslint-plugin-header": "3.1.1", "eslint-plugin-jsdoc": "^39.3.2", diff --git a/yarn.lock b/yarn.lock index e06f0699b66..441e9d1a97c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3609,10 +3609,10 @@ electron-to-chromium@^1.4.202: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.207.tgz#9c3310ebace2952903d05dcaba8abe3a4ed44c01" integrity sha512-piH7MJDJp4rJCduWbVvmUd59AUne1AFBJ8JaRQvk0KzNTSUnZrVXHCZc+eg+CGE4OujkcLJznhGKD6tuAshj5Q== -electron@25.4.0: - version "25.4.0" - resolved "https://registry.yarnpkg.com/electron/-/electron-25.4.0.tgz#d45b1cf3e4e96eb5bff5fee704d7aa13b532f3a5" - integrity sha512-VLTRxDhL4UvQbqM7pTNENnJo62cdAPZT92N+B7BZQ5Xfok1wuVPEewIjBot4K7U3EpLUuHn1veeLzho3ihiP+Q== +electron@25.5.0: + version "25.5.0" + resolved "https://registry.yarnpkg.com/electron/-/electron-25.5.0.tgz#6465d49c0731424e3e48776628c35771697caf11" + integrity sha512-w1DNj1LuAk0Vaas1rQ0pAkTe2gZ5YG75J27mC2m88y0G6Do5b5YoFDaF84fOGQHeQ4j8tC5LngSgWhbwmqDlrw== dependencies: "@electron/get" "^2.0.0" "@types/node" "^18.11.18" From a4895ad5cdd1736b20c8f9aafc67854ddda40aa7 Mon Sep 17 00:00:00 2001 From: Andrea Mah <31675041+andreamah@users.noreply.github.com> Date: Tue, 15 Aug 2023 09:35:02 -0700 Subject: [PATCH 033/223] endgame notebooks cell additions for viewing blocked issues (#189581) --- .vscode/notebooks/endgame.github-issues | 10 ++++++++++ .vscode/notebooks/my-endgame.github-issues | 14 ++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/.vscode/notebooks/endgame.github-issues b/.vscode/notebooks/endgame.github-issues index 842fe282f17..5bd52b7826e 100644 --- a/.vscode/notebooks/endgame.github-issues +++ b/.vscode/notebooks/endgame.github-issues @@ -124,6 +124,16 @@ "language": "github-issues", "value": "$REPOS $MILESTONE is:issue is:closed reason:completed sort:updated-asc label:bug -label:verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:z-author-verified label:unreleased" }, + { + "kind": 1, + "language": "markdown", + "value": "## All Unverified Fixes" + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE is:issue is:closed reason:completed sort:updated-asc label:bug -label:verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:z-author-verified " + }, { "kind": 1, "language": "markdown", diff --git a/.vscode/notebooks/my-endgame.github-issues b/.vscode/notebooks/my-endgame.github-issues index d41c81d839e..96c780e5a6f 100644 --- a/.vscode/notebooks/my-endgame.github-issues +++ b/.vscode/notebooks/my-endgame.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels\n\n$MILESTONE=milestone:\"August 2023\"\n\n$MINE=assignee:@me" + "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels\r\n\r\n$MILESTONE=milestone:\"August 2023\"\r\n\r\n$MINE=assignee:@me" }, { "kind": 1, @@ -169,6 +169,16 @@ "language": "github-issues", "value": "$REPOS $MILESTONE -$MINE is:issue is:closed reason:completed -author:@me sort:updated-asc label:bug -label:unreleased -label:verified -label:z-author-verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found" }, + { + "kind": 1, + "language": "markdown", + "value": "## Test steps needed from others" + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE -$MINE is:issue label:bug label:verification-steps-needed" + }, { "kind": 1, "language": "markdown", @@ -177,6 +187,6 @@ { "kind": 2, "language": "github-issues", - "value": "repo:microsoft/vscode $MILESTONE $MINE is:issue is:closed reason:completed label:feature-request -label:on-release-notes\nrepo:microsoft/vscode $MILESTONE $MINE is:issue is:closed reason:completed label:engineering -label:on-release-notes\nrepo:microsoft/vscode $MILESTONE $MINE is:issue is:closed reason:completed label:plan-item -label:on-release-notes" + "value": "repo:microsoft/vscode $MILESTONE $MINE is:issue is:closed reason:completed label:feature-request -label:on-release-notes\r\nrepo:microsoft/vscode $MILESTONE $MINE is:issue is:closed reason:completed label:engineering -label:on-release-notes\r\nrepo:microsoft/vscode $MILESTONE $MINE is:issue is:closed reason:completed label:plan-item -label:on-release-notes" } ] \ No newline at end of file From 7cd34760d1f1a86dfa73f0f5c9df06b48bc572a2 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 15 Aug 2023 18:54:46 +0200 Subject: [PATCH 034/223] Add compiled file --- build/lib/compilation.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/build/lib/compilation.js b/build/lib/compilation.js index 2270e054ba5..8e82e1cd234 100644 --- a/build/lib/compilation.js +++ b/build/lib/compilation.js @@ -82,17 +82,19 @@ function createCompile(src, build, emitError, transpileOnly) { return pipeline; } function transpileTask(src, out, swc) { - return function () { + const task = () => { const transpile = createCompile(src, false, true, { swc }); const srcPipe = gulp.src(`${src}/**`, { base: `${src}` }); return srcPipe .pipe(transpile()) .pipe(gulp.dest(out)); }; + task.taskName = `transpile-${path.basename(src)}`; + return task; } exports.transpileTask = transpileTask; function compileTask(src, out, build, options = {}) { - return function () { + const task = () => { if (os.totalmem() < 4000000000) { throw new Error('compilation requires 4GB of RAM'); } @@ -128,10 +130,12 @@ function compileTask(src, out, build, options = {}) { .pipe(compile()) .pipe(gulp.dest(out)); }; + task.taskName = `compile-${path.basename(src)}`; + return task; } exports.compileTask = compileTask; function watchTask(out, build) { - return function () { + const task = () => { const compile = createCompile('src', build, false, false); const src = gulp.src('src/**', { base: 'src' }); const watchSrc = watch('src/**', { base: 'src', readDelay: 200 }); @@ -142,6 +146,8 @@ function watchTask(out, build) { .pipe(util.incremental(compile, src, true)) .pipe(gulp.dest(out)); }; + task.taskName = `watch-${path.basename(out)}`; + return task; } exports.watchTask = watchTask; const REPO_SRC_FOLDER = path.join(__dirname, '../../src'); @@ -281,4 +287,4 @@ exports.watchApiProposalNamesTask = task.define('watch-api-proposal-names', () = .pipe(util.debounce(task)) .pipe(gulp.dest('src')); }); -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tcGlsYXRpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJjb21waWxhdGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztnR0FHZ0c7OztBQUVoRyxtQ0FBbUM7QUFDbkMseUJBQXlCO0FBQ3pCLDZCQUE2QjtBQUM3Qiw2QkFBNkI7QUFDN0IsMENBQTBDO0FBQzFDLDZCQUE2QjtBQUM3Qix5Q0FBNEM7QUFDNUMsK0JBQStCO0FBQy9CLHNDQUFzQztBQUN0QywwQ0FBMEM7QUFDMUMseUJBQXlCO0FBQ3pCLGlDQUFrQztBQUNsQyw4QkFBOEI7QUFDOUIsK0JBQStCO0FBQy9CLDBDQUF5QztBQUV6QyxNQUFNLEtBQUssR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUM7QUFHakMsdUVBQXVFO0FBRXZFLE1BQU0sUUFBUSxHQUFHLElBQUEseUJBQWMsR0FBRSxDQUFDO0FBRWxDLFNBQVMsNEJBQTRCLENBQUMsR0FBVztJQUNoRCxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxTQUFTLEdBQUcsRUFBRSxDQUFDLENBQUM7SUFDckQsTUFBTSxPQUFPLEdBQXVCLEVBQUUsQ0FBQztJQUN2QyxPQUFPLENBQUMsT0FBTyxHQUFHLEtBQUssQ0FBQztJQUN4QixPQUFPLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQztJQUN6QixJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMscUJBQXFCLENBQUMsRUFBRSxFQUFFLHNDQUFzQztRQUMvRSxPQUFPLENBQUMsU0FBUyxHQUFHLEtBQUssQ0FBQztLQUMxQjtJQUNELE9BQU8sQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO0lBQzFCLE9BQU8sQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO0lBQzFCLE9BQU8sQ0FBQyxVQUFVLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUM3QyxPQUFPLENBQUMsT0FBTyxHQUFHLE1BQU0sQ0FBQyxJQUFJ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 \ No newline at end of file +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tcGlsYXRpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJjb21waWxhdGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztnR0FHZ0c7OztBQUVoRyxtQ0FBbUM7QUFDbkMseUJBQXlCO0FBQ3pCLDZCQUE2QjtBQUM3Qiw2QkFBNkI7QUFDN0IsMENBQTBDO0FBQzFDLDZCQUE2QjtBQUM3Qix5Q0FBNEM7QUFDNUMsK0JBQStCO0FBQy9CLHNDQUFzQztBQUN0QywwQ0FBMEM7QUFDMUMseUJBQXlCO0FBQ3pCLGlDQUFrQztBQUNsQyw4QkFBOEI7QUFDOUIsK0JBQStCO0FBQy9CLDBDQUF5QztBQUV6QyxNQUFNLEtBQUssR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUM7QUFHakMsdUVBQXVFO0FBRXZFLE1BQU0sUUFBUSxHQUFHLElBQUEseUJBQWMsR0FBRSxDQUFDO0FBRWxDLFNBQVMsNEJBQTRCLENBQUMsR0FBVztJQUNoRCxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxTQUFTLEdBQUcsRUFBRSxDQUFDLENBQUM7SUFDckQsTUFBTSxPQUFPLEdBQXVCLEVBQUUsQ0FBQztJQUN2QyxPQUFPLENBQUMsT0FBTyxHQUFHLEtBQUssQ0FBQztJQUN4QixPQUFPLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQztJQUN6QixJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMscUJBQXFCLENBQUMsRUFBRSxFQUFFLHNDQUFzQztRQUMvRSxPQUFPLENBQUMsU0FBUyxHQUFHLEtBQUssQ0FBQztLQUMxQjtJQUNELE9BQU8sQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO0lBQzFCLE9BQU8sQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO0lBQzFCLE9BQU8sQ0FBQyxVQUFVLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUM3QyxPQUFPLENBQUMsT0FBTyxHQUFHLE1BQU0sQ0FBQyxJQUFJ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 \ No newline at end of file From 1820ef83f3b01b189ed30bbbce9ed36b0be74d93 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 15 Aug 2023 10:25:52 -0700 Subject: [PATCH 035/223] move styles to terminal.css, revert changes to xterm.css --- .../contrib/terminal/browser/media/terminal.css | 11 +++++++++++ .../contrib/terminal/browser/media/xterm.css | 8 -------- .../contrib/terminal/browser/terminal.contribution.ts | 2 +- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index 67d8bb86a0a..32e2f4bc6d1 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -53,6 +53,17 @@ outline: 0 !important; } +/** Uncomment the below two blocks to view the textarea */ +.xterm .xterm-helper-textarea { + z-index: 36 !important; + opacity: 1 !important; + left: 0 !important; + width: fit-content !important; +} +.monaco-workbench .xterm .xterm-helper-textarea:focus { + opacity: 1 !important; +} + .monaco-workbench .terminal-tab::before { font-family: 'codicon' !important; font-size: 16px !important; diff --git a/src/vs/workbench/contrib/terminal/browser/media/xterm.css b/src/vs/workbench/contrib/terminal/browser/media/xterm.css index 834337b80d5..75a139b9022 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/xterm.css +++ b/src/vs/workbench/contrib/terminal/browser/media/xterm.css @@ -78,19 +78,11 @@ white-space: nowrap; overflow: hidden; resize: none; - /** Uncomment the below for textarea testing purposes */ - /* z-index: 36 !important; - opacity: 1 !important; - left: 0 !important; - width: fit-content !important; */ } .monaco-workbench .xterm .xterm-helper-textarea:focus { /* Override the general vscode style applies `opacity:1!important` to textareas */ opacity: 0 !important; - - /** Uncomment the below for textarea testing purposes */ - /* opacity: 1 !important; */ } .xterm .composition-view { diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index fa324a2b150..eea627b0e88 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -5,9 +5,9 @@ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import 'vs/css!./media/scrollbar'; -import 'vs/css!./media/terminal'; import 'vs/css!./media/widgets'; import 'vs/css!./media/xterm'; +import 'vs/css!./media/terminal'; import * as nls from 'vs/nls'; import { URI } from 'vs/base/common/uri'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; From 394250e0d030b3beb5a0dbba5301b26be940a9b1 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 15 Aug 2023 10:27:19 -0700 Subject: [PATCH 036/223] fix type any problem --- .../terminal/common/capabilities/commandDetectionCapability.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts b/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts index f3c2519be9e..25b5fed7f3d 100644 --- a/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts +++ b/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts @@ -176,7 +176,7 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe // For non-Windows backends we can just listen to CSI J which is what the clear command // typically emits. - this._terminal.parser.registerCsiHandler({ final: 'J' }, params => { + this._terminal.parser.registerCsiHandler({ final: 'J' }, (params: (number | number[])[]) => { if (!this._isWindowsPty) { if (params.length >= 1 && (params[0] === 2 || params[0] === 3)) { this._clearCommandsInViewport(); From 9d9dad96b2db290a916d61651c2f99785989eda3 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 15 Aug 2023 10:28:35 -0700 Subject: [PATCH 037/223] comment out the css --- .../workbench/contrib/terminal/browser/media/terminal.css | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index 32e2f4bc6d1..53aa0ef6966 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -54,7 +54,7 @@ } /** Uncomment the below two blocks to view the textarea */ -.xterm .xterm-helper-textarea { +/* .xterm .xterm-helper-textarea { z-index: 36 !important; opacity: 1 !important; left: 0 !important; @@ -62,6 +62,11 @@ } .monaco-workbench .xterm .xterm-helper-textarea:focus { opacity: 1 !important; +} */ + +.monaco-workbench .xterm .xterm-helper-textarea:focus { + /* Override the general vscode style applies `opacity:1!important` to textareas */ + opacity: 0 !important; } .monaco-workbench .terminal-tab::before { @@ -69,6 +74,7 @@ font-size: 16px !important; } + .monaco-workbench .terminal-tab:not(.terminal-uri-icon)::before { background-image: none !important; } From 22717915f39809a620d074076cfd5d2923e25b16 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Tue, 15 Aug 2023 10:31:32 -0700 Subject: [PATCH 038/223] Update src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts --- .../terminal/common/capabilities/commandDetectionCapability.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts b/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts index 25b5fed7f3d..f3c2519be9e 100644 --- a/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts +++ b/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts @@ -176,7 +176,7 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe // For non-Windows backends we can just listen to CSI J which is what the clear command // typically emits. - this._terminal.parser.registerCsiHandler({ final: 'J' }, (params: (number | number[])[]) => { + this._terminal.parser.registerCsiHandler({ final: 'J' }, params => { if (!this._isWindowsPty) { if (params.length >= 1 && (params[0] === 2 || params[0] === 3)) { this._clearCommandsInViewport(); From d70dde38309ac27168523df5ae61f1da447826e4 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Tue, 15 Aug 2023 10:31:51 -0700 Subject: [PATCH 039/223] Update src/vs/workbench/contrib/terminal/browser/media/terminal.css --- src/vs/workbench/contrib/terminal/browser/media/terminal.css | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index 53aa0ef6966..4db433e2ec3 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -74,7 +74,6 @@ font-size: 16px !important; } - .monaco-workbench .terminal-tab:not(.terminal-uri-icon)::before { background-image: none !important; } From 1c647be89cd07f9983339b8cffc62db011668a7d Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Tue, 15 Aug 2023 10:32:35 -0700 Subject: [PATCH 040/223] Update src/vs/workbench/contrib/terminal/browser/media/xterm.css --- src/vs/workbench/contrib/terminal/browser/media/xterm.css | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/media/xterm.css b/src/vs/workbench/contrib/terminal/browser/media/xterm.css index 75a139b9022..a7b1dbaa1c4 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/xterm.css +++ b/src/vs/workbench/contrib/terminal/browser/media/xterm.css @@ -80,11 +80,6 @@ resize: none; } -.monaco-workbench .xterm .xterm-helper-textarea:focus { - /* Override the general vscode style applies `opacity:1!important` to textareas */ - opacity: 0 !important; -} - .xterm .composition-view { /* TODO: Composition position got messed up somewhere */ background: #000; From 6ba45652170611d6088a777ca4701c5ef77f4389 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 15 Aug 2023 10:36:34 -0700 Subject: [PATCH 041/223] xterm@5.3.0-beta.42 Fixes #189753 --- package.json | 12 +++++------ remote/package.json | 12 +++++------ remote/web/package.json | 8 +++---- remote/web/yarn.lock | 32 +++++++++++++-------------- remote/yarn.lock | 48 ++++++++++++++++++++--------------------- yarn.lock | 48 ++++++++++++++++++++--------------------- 6 files changed, 80 insertions(+), 80 deletions(-) diff --git a/package.json b/package.json index 5ecb6e3e82c..6fabe14153a 100644 --- a/package.json +++ b/package.json @@ -95,14 +95,14 @@ "vscode-oniguruma": "1.7.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "9.0.0", - "xterm": "5.3.0-beta.39", + "xterm": "5.3.0-beta.42", "xterm-addon-canvas": "0.5.0-beta.9", - "xterm-addon-image": "0.6.0-beta.1", - "xterm-addon-search": "0.13.0-beta.7", - "xterm-addon-serialize": "0.11.0-beta.7", + "xterm-addon-image": "0.6.0-beta.2", + "xterm-addon-search": "0.13.0-beta.8", + "xterm-addon-serialize": "0.11.0-beta.8", "xterm-addon-unicode11": "0.5.0", - "xterm-addon-webgl": "0.16.0-beta.13", - "xterm-headless": "5.3.0-beta.39", + "xterm-addon-webgl": "0.16.0-beta.14", + "xterm-headless": "5.3.0-beta.42", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, diff --git a/remote/package.json b/remote/package.json index e2407e66782..fe40ca68498 100644 --- a/remote/package.json +++ b/remote/package.json @@ -27,14 +27,14 @@ "vscode-oniguruma": "1.7.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "9.0.0", - "xterm": "5.3.0-beta.39", + "xterm": "5.3.0-beta.42", "xterm-addon-canvas": "0.5.0-beta.9", - "xterm-addon-image": "0.6.0-beta.1", - "xterm-addon-search": "0.13.0-beta.7", - "xterm-addon-serialize": "0.11.0-beta.7", + "xterm-addon-image": "0.6.0-beta.2", + "xterm-addon-search": "0.13.0-beta.8", + "xterm-addon-serialize": "0.11.0-beta.8", "xterm-addon-unicode11": "0.5.0", - "xterm-addon-webgl": "0.16.0-beta.13", - "xterm-headless": "5.3.0-beta.39", + "xterm-addon-webgl": "0.16.0-beta.14", + "xterm-headless": "5.3.0-beta.42", "yauzl": "^2.9.2", "yazl": "^2.4.3" } diff --git a/remote/web/package.json b/remote/web/package.json index a4bd45b2be5..268582951b9 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -11,11 +11,11 @@ "tas-client-umd": "0.1.8", "vscode-oniguruma": "1.7.0", "vscode-textmate": "9.0.0", - "xterm": "5.3.0-beta.39", + "xterm": "5.3.0-beta.42", "xterm-addon-canvas": "0.5.0-beta.9", - "xterm-addon-image": "0.6.0-beta.1", - "xterm-addon-search": "0.13.0-beta.7", + "xterm-addon-image": "0.6.0-beta.2", + "xterm-addon-search": "0.13.0-beta.8", "xterm-addon-unicode11": "0.5.0", - "xterm-addon-webgl": "0.16.0-beta.13" + "xterm-addon-webgl": "0.16.0-beta.14" } } diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index 9fe8c502181..6419492377f 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -95,27 +95,27 @@ xterm-addon-canvas@0.5.0-beta.9: resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.5.0-beta.9.tgz#d4a9536cf586f78a54527751e03abf6445613886" integrity sha512-AMyFctHbIWx0aLcACINuZqFFNoz4ndGtIAb4pQguIl9t8r+2otiV11MY4FJmPM0kUzEzoLWzVFJaGxOzykCWuw== -xterm-addon-image@0.6.0-beta.1: - version "0.6.0-beta.1" - resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.6.0-beta.1.tgz#058c171a4866f1cde7608e3b00050476b55ece4c" - integrity sha512-vLxUEo/KAsEaRhMshJ4uxnfc/Si8YfT3kilaK5WuiJflVODzQjytzZrl2iLeZaWKiqQp0NR6AEhuXlYASK675A== +xterm-addon-image@0.6.0-beta.2: + version "0.6.0-beta.2" + resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.6.0-beta.2.tgz#04c5ea11c89032b01b577b37f891109168ed0b09" + integrity sha512-58d1mqIpJwq8Zy/a/QM8JVuA1g5SceG8UvLoj9T6r+2XIktka6GK2oS66Fmo+0MP0s0HDPy+yJtl8lHY6UyzHw== -xterm-addon-search@0.13.0-beta.7: - version "0.13.0-beta.7" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.13.0-beta.7.tgz#2f9bf9d8380c4c40f021c47dc583aa32bea8796e" - integrity sha512-Dnuws0ThaqzxRDo8TjkSb5WlMYycONVDSr/irx2pk2I4vC5RuSpOifmD46ALlryNSDkMasjfCLjHPILHsFDCfw== +xterm-addon-search@0.13.0-beta.8: + version "0.13.0-beta.8" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.13.0-beta.8.tgz#90bc70bff87cd7e4f540cce51d9f968f04d5c0ef" + integrity sha512-Hlocg6gGvKAs4eRTW78wTtVqaOEurweJjdyPDjr7KrzwlEEsASsUXJaB2+vgsyTFKuYnCuLrp5u369jck993GQ== xterm-addon-unicode11@0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.5.0.tgz#41c0d96acc1e3bb6c6596eee64e163b6bca74be7" integrity sha512-Jm4/g4QiTxiKiTbYICQgC791ubhIZyoIwxAIgOW8z8HWFNY+lwk+dwaKEaEeGBfM48Vk8fklsUW9u/PlenYEBg== -xterm-addon-webgl@0.16.0-beta.13: - version "0.16.0-beta.13" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.13.tgz#ffacd22b8d86ca7f9ba903a64bc85654523955fb" - integrity sha512-1avJBoAKl+0dOjRK0pvu2im6gaXyP/6oJcEBQXCTz3V8YlgcG/jom8A6FEQSEDGZ/4dnyDvnLbsg3+aLZIr6gw== +xterm-addon-webgl@0.16.0-beta.14: + version "0.16.0-beta.14" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.14.tgz#de5bf6c97e31f62d13907a875ffc6be154bc4a02" + integrity sha512-cj+rQWqOeZYJ6JMjMuTTKu3OnP+1D+sqSZPuwZAd6s4NkY4dFXFQlKxJ2yvDHOU/jrgpIYR1c3Cnu0AOWZpS3w== -xterm@5.3.0-beta.39: - version "5.3.0-beta.39" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.39.tgz#b64da3fd1b374fd535db61294a49c16453dbb77e" - integrity sha512-Z1r0ijTgad5MjFoz8qZ+2Qc3bgjG6kYY/kZI3DTA1pibArHeOMDYk9+IeCb9adF74zvZurjytRfXKJgX6M9hwg== +xterm@5.3.0-beta.42: + version "5.3.0-beta.42" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.42.tgz#f0806ebe9b033530c54ac5b03955827fb729c38f" + integrity sha512-3OLXTfwtSy9UKo8Uzm3/kd7qBg0JD/qFpotg8iKJ2aB9zx0fkJnysl4aIuNleUJzgvuxzBUHkcRZUmEw1XCKdw== diff --git a/remote/yarn.lock b/remote/yarn.lock index 8ad9cc2907f..3dab9abaf97 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -904,40 +904,40 @@ xterm-addon-canvas@0.5.0-beta.9: resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.5.0-beta.9.tgz#d4a9536cf586f78a54527751e03abf6445613886" integrity sha512-AMyFctHbIWx0aLcACINuZqFFNoz4ndGtIAb4pQguIl9t8r+2otiV11MY4FJmPM0kUzEzoLWzVFJaGxOzykCWuw== -xterm-addon-image@0.6.0-beta.1: - version "0.6.0-beta.1" - resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.6.0-beta.1.tgz#058c171a4866f1cde7608e3b00050476b55ece4c" - integrity sha512-vLxUEo/KAsEaRhMshJ4uxnfc/Si8YfT3kilaK5WuiJflVODzQjytzZrl2iLeZaWKiqQp0NR6AEhuXlYASK675A== +xterm-addon-image@0.6.0-beta.2: + version "0.6.0-beta.2" + resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.6.0-beta.2.tgz#04c5ea11c89032b01b577b37f891109168ed0b09" + integrity sha512-58d1mqIpJwq8Zy/a/QM8JVuA1g5SceG8UvLoj9T6r+2XIktka6GK2oS66Fmo+0MP0s0HDPy+yJtl8lHY6UyzHw== -xterm-addon-search@0.13.0-beta.7: - version "0.13.0-beta.7" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.13.0-beta.7.tgz#2f9bf9d8380c4c40f021c47dc583aa32bea8796e" - integrity sha512-Dnuws0ThaqzxRDo8TjkSb5WlMYycONVDSr/irx2pk2I4vC5RuSpOifmD46ALlryNSDkMasjfCLjHPILHsFDCfw== +xterm-addon-search@0.13.0-beta.8: + version "0.13.0-beta.8" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.13.0-beta.8.tgz#90bc70bff87cd7e4f540cce51d9f968f04d5c0ef" + integrity sha512-Hlocg6gGvKAs4eRTW78wTtVqaOEurweJjdyPDjr7KrzwlEEsASsUXJaB2+vgsyTFKuYnCuLrp5u369jck993GQ== -xterm-addon-serialize@0.11.0-beta.7: - version "0.11.0-beta.7" - resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.11.0-beta.7.tgz#934a48a4490c2ca94a5cfcdd646fb0813b4e56d0" - integrity sha512-TwYsEHq6TflY0vmq3yAI2VKNqzEAZ0gCm3TThDMPrPQiv4LcewQSeGu9RujEqF9RAhujhceaM3GajBxySAWyqQ== +xterm-addon-serialize@0.11.0-beta.8: + version "0.11.0-beta.8" + resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.11.0-beta.8.tgz#343357aad7c549a37aacac2fb19692b1c2fd69cd" + integrity sha512-1jNrX+Y8dorWtjMvz02mRUP+ahqQ4STO0lYlKe8m06p+NYuFk24CylnPZFuBgk91OUU3RAdl4glaKh5M4f0/Zw== xterm-addon-unicode11@0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.5.0.tgz#41c0d96acc1e3bb6c6596eee64e163b6bca74be7" integrity sha512-Jm4/g4QiTxiKiTbYICQgC791ubhIZyoIwxAIgOW8z8HWFNY+lwk+dwaKEaEeGBfM48Vk8fklsUW9u/PlenYEBg== -xterm-addon-webgl@0.16.0-beta.13: - version "0.16.0-beta.13" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.13.tgz#ffacd22b8d86ca7f9ba903a64bc85654523955fb" - integrity sha512-1avJBoAKl+0dOjRK0pvu2im6gaXyP/6oJcEBQXCTz3V8YlgcG/jom8A6FEQSEDGZ/4dnyDvnLbsg3+aLZIr6gw== +xterm-addon-webgl@0.16.0-beta.14: + version "0.16.0-beta.14" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.14.tgz#de5bf6c97e31f62d13907a875ffc6be154bc4a02" + integrity sha512-cj+rQWqOeZYJ6JMjMuTTKu3OnP+1D+sqSZPuwZAd6s4NkY4dFXFQlKxJ2yvDHOU/jrgpIYR1c3Cnu0AOWZpS3w== -xterm-headless@5.3.0-beta.39: - version "5.3.0-beta.39" - resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.3.0-beta.39.tgz#84a509dbe1dc9b2229b5274ae3430ce968986346" - integrity sha512-Q4vfqAaYoWh7/T4HearlLlKkn9vjN5EugsNyuPd6hq+Ug1FwJIJYj+I9vY67+Rqxr24XGnrQdXfvvEas29KXsw== +xterm-headless@5.3.0-beta.42: + version "5.3.0-beta.42" + resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.3.0-beta.42.tgz#714d167dc0af010067420956ffca80149dddeca9" + integrity sha512-3xhyy9DaPpadd1iiuKjOn4Zvntnx8WSBMEXPJbrORmy3ZbIwmLf/HYvg7iZfVreSDH+UFlm06uq3ictXpv7YvA== -xterm@5.3.0-beta.39: - version "5.3.0-beta.39" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.39.tgz#b64da3fd1b374fd535db61294a49c16453dbb77e" - integrity sha512-Z1r0ijTgad5MjFoz8qZ+2Qc3bgjG6kYY/kZI3DTA1pibArHeOMDYk9+IeCb9adF74zvZurjytRfXKJgX6M9hwg== +xterm@5.3.0-beta.42: + version "5.3.0-beta.42" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.42.tgz#f0806ebe9b033530c54ac5b03955827fb729c38f" + integrity sha512-3OLXTfwtSy9UKo8Uzm3/kd7qBg0JD/qFpotg8iKJ2aB9zx0fkJnysl4aIuNleUJzgvuxzBUHkcRZUmEw1XCKdw== yallist@^4.0.0: version "4.0.0" diff --git a/yarn.lock b/yarn.lock index e06f0699b66..d82c0b1d8da 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10786,40 +10786,40 @@ xterm-addon-canvas@0.5.0-beta.9: resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.5.0-beta.9.tgz#d4a9536cf586f78a54527751e03abf6445613886" integrity sha512-AMyFctHbIWx0aLcACINuZqFFNoz4ndGtIAb4pQguIl9t8r+2otiV11MY4FJmPM0kUzEzoLWzVFJaGxOzykCWuw== -xterm-addon-image@0.6.0-beta.1: - version "0.6.0-beta.1" - resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.6.0-beta.1.tgz#058c171a4866f1cde7608e3b00050476b55ece4c" - integrity sha512-vLxUEo/KAsEaRhMshJ4uxnfc/Si8YfT3kilaK5WuiJflVODzQjytzZrl2iLeZaWKiqQp0NR6AEhuXlYASK675A== +xterm-addon-image@0.6.0-beta.2: + version "0.6.0-beta.2" + resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.6.0-beta.2.tgz#04c5ea11c89032b01b577b37f891109168ed0b09" + integrity sha512-58d1mqIpJwq8Zy/a/QM8JVuA1g5SceG8UvLoj9T6r+2XIktka6GK2oS66Fmo+0MP0s0HDPy+yJtl8lHY6UyzHw== -xterm-addon-search@0.13.0-beta.7: - version "0.13.0-beta.7" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.13.0-beta.7.tgz#2f9bf9d8380c4c40f021c47dc583aa32bea8796e" - integrity sha512-Dnuws0ThaqzxRDo8TjkSb5WlMYycONVDSr/irx2pk2I4vC5RuSpOifmD46ALlryNSDkMasjfCLjHPILHsFDCfw== +xterm-addon-search@0.13.0-beta.8: + version "0.13.0-beta.8" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.13.0-beta.8.tgz#90bc70bff87cd7e4f540cce51d9f968f04d5c0ef" + integrity sha512-Hlocg6gGvKAs4eRTW78wTtVqaOEurweJjdyPDjr7KrzwlEEsASsUXJaB2+vgsyTFKuYnCuLrp5u369jck993GQ== -xterm-addon-serialize@0.11.0-beta.7: - version "0.11.0-beta.7" - resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.11.0-beta.7.tgz#934a48a4490c2ca94a5cfcdd646fb0813b4e56d0" - integrity sha512-TwYsEHq6TflY0vmq3yAI2VKNqzEAZ0gCm3TThDMPrPQiv4LcewQSeGu9RujEqF9RAhujhceaM3GajBxySAWyqQ== +xterm-addon-serialize@0.11.0-beta.8: + version "0.11.0-beta.8" + resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.11.0-beta.8.tgz#343357aad7c549a37aacac2fb19692b1c2fd69cd" + integrity sha512-1jNrX+Y8dorWtjMvz02mRUP+ahqQ4STO0lYlKe8m06p+NYuFk24CylnPZFuBgk91OUU3RAdl4glaKh5M4f0/Zw== xterm-addon-unicode11@0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.5.0.tgz#41c0d96acc1e3bb6c6596eee64e163b6bca74be7" integrity sha512-Jm4/g4QiTxiKiTbYICQgC791ubhIZyoIwxAIgOW8z8HWFNY+lwk+dwaKEaEeGBfM48Vk8fklsUW9u/PlenYEBg== -xterm-addon-webgl@0.16.0-beta.13: - version "0.16.0-beta.13" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.13.tgz#ffacd22b8d86ca7f9ba903a64bc85654523955fb" - integrity sha512-1avJBoAKl+0dOjRK0pvu2im6gaXyP/6oJcEBQXCTz3V8YlgcG/jom8A6FEQSEDGZ/4dnyDvnLbsg3+aLZIr6gw== +xterm-addon-webgl@0.16.0-beta.14: + version "0.16.0-beta.14" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.14.tgz#de5bf6c97e31f62d13907a875ffc6be154bc4a02" + integrity sha512-cj+rQWqOeZYJ6JMjMuTTKu3OnP+1D+sqSZPuwZAd6s4NkY4dFXFQlKxJ2yvDHOU/jrgpIYR1c3Cnu0AOWZpS3w== -xterm-headless@5.3.0-beta.39: - version "5.3.0-beta.39" - resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.3.0-beta.39.tgz#84a509dbe1dc9b2229b5274ae3430ce968986346" - integrity sha512-Q4vfqAaYoWh7/T4HearlLlKkn9vjN5EugsNyuPd6hq+Ug1FwJIJYj+I9vY67+Rqxr24XGnrQdXfvvEas29KXsw== +xterm-headless@5.3.0-beta.42: + version "5.3.0-beta.42" + resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.3.0-beta.42.tgz#714d167dc0af010067420956ffca80149dddeca9" + integrity sha512-3xhyy9DaPpadd1iiuKjOn4Zvntnx8WSBMEXPJbrORmy3ZbIwmLf/HYvg7iZfVreSDH+UFlm06uq3ictXpv7YvA== -xterm@5.3.0-beta.39: - version "5.3.0-beta.39" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.39.tgz#b64da3fd1b374fd535db61294a49c16453dbb77e" - integrity sha512-Z1r0ijTgad5MjFoz8qZ+2Qc3bgjG6kYY/kZI3DTA1pibArHeOMDYk9+IeCb9adF74zvZurjytRfXKJgX6M9hwg== +xterm@5.3.0-beta.42: + version "5.3.0-beta.42" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.42.tgz#f0806ebe9b033530c54ac5b03955827fb729c38f" + integrity sha512-3OLXTfwtSy9UKo8Uzm3/kd7qBg0JD/qFpotg8iKJ2aB9zx0fkJnysl4aIuNleUJzgvuxzBUHkcRZUmEw1XCKdw== y18n@^3.2.1: version "3.2.2" From eb519d207f60d1dd71e4490aa9b316edfe18f5f7 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Tue, 15 Aug 2023 10:57:36 -0700 Subject: [PATCH 042/223] copy from the image html element --- .../browser/controller/cellOutputActions.ts | 10 ++--- .../view/renderers/backLayerWebView.ts | 4 +- .../browser/view/renderers/webviewMessages.ts | 6 +-- .../browser/view/renderers/webviewPreloads.ts | 42 +++++++++++++++++++ 4 files changed, 52 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts index b2e3e259842..e069bba4485 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts @@ -13,7 +13,7 @@ import * as icons from 'vs/workbench/contrib/notebook/browser/notebookIcons'; import { ILogService } from 'vs/platform/log/common/log'; import { copyCellOutput } from 'vs/workbench/contrib/notebook/browser/contrib/clipboard/cellOutputClipboard'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { NotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookEditor'; +import { ICellViewModel, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; export const COPY_OUTPUT_COMMAND_ID = 'notebook.cellOutput.copyToClipboard'; @@ -40,11 +40,11 @@ registerAction2(class CopyCellOutputAction extends NotebookAction { copyCellOutput(mimeType, outputViewModel, clipboardService, logService); - if (mimeType === 'image/png') { + if (mimeType?.startsWith('image/')) { const editorService = accessor.get(IEditorService); - const editor = editorService.activeEditorPane?.getControl() as NotebookEditor; - - + const editor = editorService.activeEditorPane?.getControl() as INotebookEditor; + await editor.focusNotebookCell(outputViewModel.cellViewModel as ICellViewModel, 'output'); + editor.copyImage(outputViewModel); } } }); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index e12fa98cb9c..0cb0dfce3fb 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -1546,13 +1546,13 @@ export class BackLayerWebView extends Themable { async copyImage(output: ICellOutputViewModel): Promise { const mimeType = output.pickedMimeType?.mimeType; const imageOutput = output.model.outputs.find(out => out.mime === mimeType); - if (!imageOutput) { + if (!imageOutput || !mimeType) { return; } this._sendMessageToWebview({ type: 'copyImage', - data: imageOutput.data.buffer + outputId: output.model.outputId }); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts index cade87b9693..653a16a35bd 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts @@ -266,9 +266,9 @@ export interface IShowOutputMessage { readonly content?: ICreationContent; } -export interface ICopyOutputImage { +export interface ICopyImageMessage { readonly type: 'copyImage'; - readonly data: Uint8Array; + readonly outputId: string; } export interface IFocusOutputMessage { @@ -532,7 +532,7 @@ export type ToWebviewMessage = IClearMessage | IClearOutputRequestMessage | IHideOutputMessage | IShowOutputMessage | - ICopyOutputImage | + ICopyImageMessage | IUpdateControllerPreloadsMessage | IUpdateRenderersMessage | IUpdateDecorationsMessage | diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts index f7eb1abd38f..49d98464758 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -1362,6 +1362,38 @@ async function webviewPreloads(ctx: PreloadContext) { }); }; + const copyImage = async (image: HTMLImageElement, retries = 5) => { + if (!document.hasFocus() && retries > 0) { + // copyImage can be called from outside of the webview, which means this function may be running whilst the webview is gaining focus. + // Since navigator.clipboard.write requires the document to be focused, we need to wait for focus. + // We cannot use a listener, as there is a high chance the focus is gained during the setup of the listener resulting in us missing it. + setTimeout(() => { copyImage(image, retries - 1); }, 20); + return; + } + + try { + await navigator.clipboard.write([new ClipboardItem({ + 'image/png': new Promise((resolve) => { + const canvas = document.createElement('canvas'); + if (canvas !== null) { + canvas.width = image.naturalWidth; + canvas.height = image.naturalHeight; + const context = canvas.getContext('2d'); + context?.drawImage(image, 0, 0); + } + canvas.toBlob((blob) => { + if (blob) { + resolve(blob); + } + canvas.remove(); + }, 'image/png'); + }) + })]); + } catch (e) { + console.error(e); + } + }; + window.addEventListener('message', async rawEvent => { const event = rawEvent as ({ data: webviewMessages.ToWebviewMessage }); @@ -1462,6 +1494,16 @@ async function webviewPreloads(ctx: PreloadContext) { }); break; } + case 'copyImage': { + const { outputId } = event.data; + + const image = document.getElementById(outputId)?.querySelector('img'); + if (!!image) { + await copyImage(image); + } + + break; + } case 'ack-dimension': { for (const { cellId, outputId, height } of event.data.updates) { viewModel.updateOutputHeight(cellId, outputId, height); From 1f09f999997f3f95fc6f870af0ec857ea658f4ef Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 15 Aug 2023 10:58:59 -0700 Subject: [PATCH 043/223] add devMode setting, reduce complexity --- src/vs/platform/terminal/common/terminal.ts | 2 + .../terminal/browser/media/terminal.css | 7 +- .../terminal/browser/xterm/xtermTerminal.ts | 11 --- .../browser/textAreaSyncAddon.ts | 71 +++++++++++++++++++ .../terminal.developer.contribution.ts | 40 ++++++++++- 5 files changed, 113 insertions(+), 18 deletions(-) create mode 100644 src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index c647edca009..9dee3d5b41a 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -121,6 +121,8 @@ export const enum TerminalSettingId { DeveloperPtyHostLatency = 'terminal.integrated.developer.ptyHost.latency', /** Simulated startup delay of the pty host process */ DeveloperPtyHostStartupDelay = 'terminal.integrated.developer.ptyHost.startupDelay', + /** Shows the textarea element */ + DevMode = 'terminal.integrated.developer.devMode' } export const enum PosixShellType { diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index 4db433e2ec3..1b240eafdab 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -53,16 +53,15 @@ outline: 0 !important; } -/** Uncomment the below two blocks to view the textarea */ -/* .xterm .xterm-helper-textarea { +.xterm.dev-mode .xterm-helper-textarea { z-index: 36 !important; opacity: 1 !important; left: 0 !important; width: fit-content !important; } -.monaco-workbench .xterm .xterm-helper-textarea:focus { +.monaco-workbench .xterm.dev-mode .xterm-helper-textarea:focus { opacity: 1 !important; -} */ +} .monaco-workbench .xterm .xterm-helper-textarea:focus { /* Override the general vscode style applies `opacity:1!important` to textareas */ diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index c005695a661..f6366dd9d4e 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -279,17 +279,6 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, ID this.raw.loadAddon(this._shellIntegrationAddon); this._textAreaSyncAddon = this._instantiationService.createInstance(TextAreaSyncAddon, this._capabilities); this.raw.loadAddon(this._textAreaSyncAddon); - this.add(this._textAreaSyncAddon.onDidRequestTextAreaSync((data) => { - const textArea = this.raw.textarea; - if (!textArea) { - return; - } - // Sync the textarea using shell integration - textArea.textContent = data.content; - textArea.selectionStart = data.cursorX; - textArea.selectionEnd = data.cursorX; - // TODO: cursorY? - })); this._anyTerminalFocusContextKey = TerminalContextKeys.focusInAny.bindTo(contextKeyService); this._anyFocusedTerminalHasSelection = TerminalContextKeys.textSelectedInFocused.bindTo(contextKeyService); diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts new file mode 100644 index 00000000000..922714192ab --- /dev/null +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable } from 'vs/base/common/lifecycle'; +import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; +import { ITerminalCapabilityStore, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; +import type { Terminal, ITerminalAddon } from 'xterm'; + +export interface ITextAreaData { + content: string; + cursorX: number; +} + +export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { + private _terminal: Terminal | undefined; + activate(terminal: Terminal): void { + this._terminal = terminal; + this._register(this._terminal.onCursorMove(() => this._refreshTextArea())); + } + + constructor( + private readonly _capabilities: ITerminalCapabilityStore, + @IAccessibilityService private readonly _accessibilityService: IAccessibilityService + ) { + super(); + } + + private _refreshTextArea(): void { + if (!this._terminal) { + return; + } + if (this._accessibilityService.isScreenReaderOptimized()) { + const commandCapability = this._capabilities.get(TerminalCapability.CommandDetection); + const currentCommand = commandCapability?.currentCommand; + if (!currentCommand) { + return; + } + const buffer = this._terminal.buffer.active; + const line = buffer.getLine(buffer.cursorY)?.translateToString(true); + let content: string = ''; + let commandStartX: number | undefined; + if (!line) { + return; + } + if (currentCommand.commandStartX) { + // Left prompt + content = line.substring(currentCommand.commandStartX).trim(); + commandStartX = currentCommand.commandStartX; + } else if (currentCommand.commandRightPromptStartX) { + // Right prompt + content = line.substring(0, currentCommand.commandRightPromptStartX).trim(); + commandStartX = 0; + } + if (commandStartX) { + const textArea = this._terminal.textarea; + if (!textArea) { + return; + } + // Sync the textarea using shell integration + textArea.textContent = content; + const cursorX = buffer.cursorX - commandStartX; + textArea.selectionStart = cursorX; + textArea.selectionEnd = cursorX; + // TODO: cursorY? + } + } + } + +} diff --git a/src/vs/workbench/contrib/terminalContrib/developer/browser/terminal.developer.contribution.ts b/src/vs/workbench/contrib/terminalContrib/developer/browser/terminal.developer.contribution.ts index 147b8676571..e1c4ee3662c 100644 --- a/src/vs/workbench/contrib/terminalContrib/developer/browser/terminal.developer.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/developer/browser/terminal.developer.contribution.ts @@ -4,19 +4,24 @@ *--------------------------------------------------------------------------------------------*/ import { VSBuffer } from 'vs/base/common/buffer'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { Categories } from 'vs/platform/action/common/actionCommonCategories'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IFileService } from 'vs/platform/files/common/files'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; -import { ITerminalLogService } from 'vs/platform/terminal/common/terminal'; +import { ITerminalLogService, TerminalSettingId } from 'vs/platform/terminal/common/terminal'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { IInternalXtermTerminal } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { IInternalXtermTerminal, ITerminalContribution, ITerminalInstance, IXtermTerminal } from 'vs/workbench/contrib/terminal/browser/terminal'; import { registerTerminalAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; -import { TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal'; +import { registerTerminalContribution } from 'vs/workbench/contrib/terminal/browser/terminalExtensions'; +import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; +import { ITerminalProcessManager, TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; +import type { Terminal } from 'xterm'; registerTerminalAction({ id: TerminalCommandId.ShowTextureAtlas, @@ -102,3 +107,32 @@ registerTerminalAction({ } } }); + +class DevModeContribution extends DisposableStore implements ITerminalContribution { + static readonly ID = 'terminal.devMode'; + devModeClass = 'dev-mode'; + private _xterm: IXtermTerminal & { raw: Terminal } | undefined; + static get(instance: ITerminalInstance): DevModeContribution | null { + return instance.getContribution(DevModeContribution.ID); + } + constructor( + instance: ITerminalInstance, + processManager: ITerminalProcessManager, + widgetManager: TerminalWidgetManager, + @IConfigurationService private readonly _configurationService: IConfigurationService) { + super(); + this.add(this._configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(TerminalSettingId.DevMode)) { + const devMode: boolean = this._configurationService.getValue(TerminalSettingId.DevMode) || false; + this._xterm?.raw.element?.classList.toggle(this.devModeClass, devMode); + } + })); + } + xtermReady(xterm: IXtermTerminal & { raw: Terminal }): void { + this._xterm = xterm; + if (this._configurationService.getValue(TerminalSettingId.DevMode)) { + this._xterm.raw.element?.classList.add(this.devModeClass); + } + } +} +registerTerminalContribution(DevModeContribution.ID, DevModeContribution); From 4459913c56e49a3c793fa9ef4ca117ad12efa190 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Tue, 15 Aug 2023 11:03:32 -0700 Subject: [PATCH 044/223] enable for image only outputs --- .../browser/contrib/clipboard/cellOutputClipboard.ts | 6 +++--- .../notebook/browser/controller/cellOutputActions.ts | 9 +++++---- .../notebook/browser/view/cellParts/cellOutput.ts | 4 ++-- .../notebook/browser/view/renderers/webviewPreloads.ts | 6 ++++-- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/clipboard/cellOutputClipboard.ts b/src/vs/workbench/contrib/notebook/browser/contrib/clipboard/cellOutputClipboard.ts index 1ec996bd137..576ded3d1f6 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/clipboard/cellOutputClipboard.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/clipboard/cellOutputClipboard.ts @@ -10,9 +10,9 @@ import { isTextStreamMime } from 'vs/workbench/contrib/notebook/common/notebookC export async function copyCellOutput(mimeType: string | undefined, outputViewModel: ICellOutputViewModel, clipboardService: IClipboardService, logService: ILogService) { const cellOutput = outputViewModel.model; - const output = mimeType && CLIPBOARD_COMPATIBLE_MIMETYPES.includes(mimeType) ? + const output = mimeType && TEXT_BASED_MIMETYPES.includes(mimeType) ? cellOutput.outputs.find(output => output.mime === mimeType) : - cellOutput.outputs.find(output => CLIPBOARD_COMPATIBLE_MIMETYPES.includes(output.mime)); + cellOutput.outputs.find(output => TEXT_BASED_MIMETYPES.includes(output.mime)); mimeType = output?.mime; @@ -52,7 +52,7 @@ export async function copyCellOutput(mimeType: string | undefined, outputViewMod } } -export const CLIPBOARD_COMPATIBLE_MIMETYPES = [ +export const TEXT_BASED_MIMETYPES = [ 'text/latex', 'text/html', 'application/vnd.code.notebook.error', diff --git a/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts index e069bba4485..f1b114620b8 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts @@ -35,16 +35,17 @@ registerAction2(class CopyCellOutputAction extends NotebookAction { const outputViewModel = context.outputViewModel; const mimeType = outputViewModel.pickedMimeType?.mimeType; - const clipboardService = accessor.get(IClipboardService); - const logService = accessor.get(ILogService); - - copyCellOutput(mimeType, outputViewModel, clipboardService, logService); if (mimeType?.startsWith('image/')) { const editorService = accessor.get(IEditorService); const editor = editorService.activeEditorPane?.getControl() as INotebookEditor; await editor.focusNotebookCell(outputViewModel.cellViewModel as ICellViewModel, 'output'); editor.copyImage(outputViewModel); + } else { + const clipboardService = accessor.get(IClipboardService); + const logService = accessor.get(ILogService); + + copyCellOutput(mimeType, outputViewModel, clipboardService, logService); } } }); diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts index a118618482a..01fd2d2d227 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts @@ -35,7 +35,7 @@ import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookS import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; import { COPY_OUTPUT_COMMAND_ID } from 'vs/workbench/contrib/notebook/browser/controller/cellOutputActions'; import { CLEAR_CELL_OUTPUTS_COMMAND_ID } from 'vs/workbench/contrib/notebook/browser/controller/editActions'; -import { CLIPBOARD_COMPATIBLE_MIMETYPES } from 'vs/workbench/contrib/notebook/browser/contrib/clipboard/cellOutputClipboard'; +import { TEXT_BASED_MIMETYPES } from 'vs/workbench/contrib/notebook/browser/contrib/clipboard/cellOutputClipboard'; interface IMimeTypeRenderer extends IQuickPickItem { index: number; @@ -257,7 +257,7 @@ class CellOutputElement extends Disposable { } private shouldEnableCopy(mimeTypes: readonly IOrderedMimeType[]) { - if (!mimeTypes.find(mimeType => CLIPBOARD_COMPATIBLE_MIMETYPES.indexOf(mimeType.mimeType))) { + if (!mimeTypes.find(mimeType => TEXT_BASED_MIMETYPES.indexOf(mimeType.mimeType) || mimeType.mimeType.startsWith('image/'))) { return false; } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts index 49d98464758..764ca9880d2 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -1390,7 +1390,7 @@ async function webviewPreloads(ctx: PreloadContext) { }) })]); } catch (e) { - console.error(e); + console.error('Could not copy image:', e); } }; @@ -1498,8 +1498,10 @@ async function webviewPreloads(ctx: PreloadContext) { const { outputId } = event.data; const image = document.getElementById(outputId)?.querySelector('img'); - if (!!image) { + if (image) { await copyImage(image); + } else { + console.warn('Could not find image element to copy'); } break; From e11d85a9218c9377b4dcba574a303f1a00b08c52 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 15 Aug 2023 11:16:48 -0700 Subject: [PATCH 045/223] use MutableDisposable --- .../browser/textAreaSyncAddon.ts | 97 +++++++++++-------- 1 file changed, 59 insertions(+), 38 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts index 922714192ab..515370eabaa 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { ITerminalCapabilityStore, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; import type { Terminal, ITerminalAddon } from 'xterm'; @@ -15,9 +15,12 @@ export interface ITextAreaData { export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { private _terminal: Terminal | undefined; + private _onCursorMoveListener = this._register(new MutableDisposable()); activate(terminal: Terminal): void { this._terminal = terminal; - this._register(this._terminal.onCursorMove(() => this._refreshTextArea())); + if (this._accessibilityService.isScreenReaderOptimized()) { + this._onCursorMoveListener.value = this._terminal.onCursorMove(() => this._refreshTextArea()); + } } constructor( @@ -25,47 +28,65 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { @IAccessibilityService private readonly _accessibilityService: IAccessibilityService ) { super(); + this._register(this._accessibilityService.onDidChangeScreenReaderOptimized(() => { + if (this._accessibilityService.isScreenReaderOptimized() && this._terminal) { + this._refreshTextArea() + this._onCursorMoveListener.value = this._terminal.onCursorMove(() => this._refreshTextArea()); + } else { + this._onCursorMoveListener.clear(); + } + })); } private _refreshTextArea(): void { if (!this._terminal) { return; } - if (this._accessibilityService.isScreenReaderOptimized()) { - const commandCapability = this._capabilities.get(TerminalCapability.CommandDetection); - const currentCommand = commandCapability?.currentCommand; - if (!currentCommand) { - return; - } - const buffer = this._terminal.buffer.active; - const line = buffer.getLine(buffer.cursorY)?.translateToString(true); - let content: string = ''; - let commandStartX: number | undefined; - if (!line) { - return; - } - if (currentCommand.commandStartX) { - // Left prompt - content = line.substring(currentCommand.commandStartX).trim(); - commandStartX = currentCommand.commandStartX; - } else if (currentCommand.commandRightPromptStartX) { - // Right prompt - content = line.substring(0, currentCommand.commandRightPromptStartX).trim(); - commandStartX = 0; - } - if (commandStartX) { - const textArea = this._terminal.textarea; - if (!textArea) { - return; - } - // Sync the textarea using shell integration - textArea.textContent = content; - const cursorX = buffer.cursorX - commandStartX; - textArea.selectionStart = cursorX; - textArea.selectionEnd = cursorX; - // TODO: cursorY? - } - } - } + const commandCapability = this._capabilities.get(TerminalCapability.CommandDetection); + const currentCommand = commandCapability?.currentCommand; + if (!currentCommand) { + return; + } + const buffer = this._terminal.buffer.active; + const line = buffer.getLine(buffer.cursorY)?.translateToString(true); + let commandStartX: number | undefined; + if (!line) { + return; + } + let content: string | undefined; + if (currentCommand.commandStartX) { + // Left prompt + content = line.substring(currentCommand.commandStartX); + commandStartX = currentCommand.commandStartX; + } else if (currentCommand.commandRightPromptStartX) { + // Right prompt + content = line.substring(0, currentCommand.commandRightPromptStartX); + commandStartX = 0; + } + + if (!content) { + return; + } + + if (commandStartX === undefined) { + return; + } + + const textArea = this._terminal.textarea; + if (!textArea) { + return; + } + + if (content !== textArea.textContent) { + textArea.textContent = content; + } + + const cursorX = buffer.cursorX - commandStartX; + if (cursorX !== textArea.selectionStart) { + textArea.selectionStart = cursorX; + textArea.selectionEnd = cursorX; + } + // TODO: cursorY? + } } From 38679a2fd1714ee6f655e238100a3774fa5ebf85 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Tue, 15 Aug 2023 11:22:01 -0700 Subject: [PATCH 046/223] comment, better name --- .../contrib/notebook/browser/controller/cellOutputActions.ts | 2 +- src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts | 5 ++++- .../contrib/notebook/browser/notebookEditorWidget.ts | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts index f1b114620b8..25d0ffbc1ba 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts @@ -40,7 +40,7 @@ registerAction2(class CopyCellOutputAction extends NotebookAction { const editorService = accessor.get(IEditorService); const editor = editorService.activeEditorPane?.getControl() as INotebookEditor; await editor.focusNotebookCell(outputViewModel.cellViewModel as ICellViewModel, 'output'); - editor.copyImage(outputViewModel); + editor.copyOutputImage(outputViewModel); } else { const clipboardService = accessor.get(IClipboardService); const logService = accessor.get(ILogService); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 6f76eb9dcc7..d7516848531 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -549,7 +549,10 @@ export interface INotebookEditor { */ updateOutput(cell: ICellViewModel, output: IInsetRenderOutput, offset: number): Promise; - copyImage(cellOutput: ICellOutputViewModel): Promise; + /** + * Copy the image in the specific cell output to the clipboard + */ + copyOutputImage(cellOutput: ICellOutputViewModel): Promise; readonly onDidReceiveMessage: Event; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 38c14e810da..d424587ada8 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -2793,7 +2793,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD }); } - async copyImage(cellOutput: ICellOutputViewModel): Promise { + async copyOutputImage(cellOutput: ICellOutputViewModel): Promise { this._webview?.copyImage(cellOutput); } From 0202f3270acaec7f8fd269bffe6e76277c31ae5e Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Tue, 15 Aug 2023 11:31:18 -0700 Subject: [PATCH 047/223] removed unneeded code --- .../notebook/browser/view/renderers/backLayerWebView.ts | 6 ------ .../notebook/browser/view/renderers/webviewPreloads.ts | 6 ++---- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index 0cb0dfce3fb..5b3bb79f8e0 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -1544,12 +1544,6 @@ export class BackLayerWebView extends Themable { } async copyImage(output: ICellOutputViewModel): Promise { - const mimeType = output.pickedMimeType?.mimeType; - const imageOutput = output.model.outputs.find(out => out.mime === mimeType); - if (!imageOutput || !mimeType) { - return; - } - this._sendMessageToWebview({ type: 'copyImage', outputId: output.model.outputId diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts index 764ca9880d2..28e91de05c4 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -1495,13 +1495,11 @@ async function webviewPreloads(ctx: PreloadContext) { break; } case 'copyImage': { - const { outputId } = event.data; - - const image = document.getElementById(outputId)?.querySelector('img'); + const image = document.getElementById(event.data.outputId)?.querySelector('img'); if (image) { await copyImage(image); } else { - console.warn('Could not find image element to copy'); + console.warn('Could not find image element to copy for output with id', event.data.outputId); } break; From d561eda6de08950155c6fc6f0d51d004f60e2f96 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 15 Aug 2023 11:35:36 -0700 Subject: [PATCH 048/223] rm from xtermterminal --- .../browser/xterm/textAreaSyncAddon.ts | 65 ------------------- .../terminal/browser/xterm/xtermTerminal.ts | 4 -- .../browser/textAreaSyncAddon.ts | 19 +++++- 3 files changed, 16 insertions(+), 72 deletions(-) delete mode 100644 src/vs/workbench/contrib/terminal/browser/xterm/textAreaSyncAddon.ts diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/textAreaSyncAddon.ts b/src/vs/workbench/contrib/terminal/browser/xterm/textAreaSyncAddon.ts deleted file mode 100644 index 796ba0cd5b5..00000000000 --- a/src/vs/workbench/contrib/terminal/browser/xterm/textAreaSyncAddon.ts +++ /dev/null @@ -1,65 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Emitter } from 'vs/base/common/event'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; -import { ITerminalCapabilityStore, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; -import type { Terminal, ITerminalAddon } from 'xterm'; - -export interface ITextAreaData { - content: string; - cursorX: number; -} - -export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { - private _terminal: Terminal | undefined; - private readonly _onDidRequestTextAreaSync = this._register(new Emitter()); - readonly onDidRequestTextAreaSync = this._onDidRequestTextAreaSync.event; - activate(terminal: Terminal): void { - this._terminal = terminal; - this._register(this._terminal.onCursorMove(() => this._refreshTextArea())); - } - - constructor( - private readonly _capabilities: ITerminalCapabilityStore, - @IAccessibilityService private readonly _accessibilityService: IAccessibilityService - ) { - super(); - } - - private _refreshTextArea(): void { - if (!this._terminal) { - return; - } - if (this._accessibilityService.isScreenReaderOptimized()) { - const commandCapability = this._capabilities.get(TerminalCapability.CommandDetection); - const currentCommand = commandCapability?.currentCommand; - if (!currentCommand) { - return; - } - const buffer = this._terminal.buffer.active; - const line = buffer.getLine(buffer.cursorY)?.translateToString(true); - let content: string = ''; - let commandStartX: number | undefined; - if (!line) { - return; - } - if (currentCommand.commandStartX) { - // Left prompt - content = line.substring(currentCommand.commandStartX).trim(); - commandStartX = currentCommand.commandStartX; - } else if (currentCommand.commandRightPromptStartX) { - // Right prompt - content = line.substring(0, currentCommand.commandRightPromptStartX).trim(); - commandStartX = 0; - } - if (commandStartX) { - this._onDidRequestTextAreaSync.fire({ content, cursorX: buffer.cursorX - commandStartX }); - } - } - } - -} diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index f6366dd9d4e..b5847637730 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -43,7 +43,6 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService import { debounce } from 'vs/base/common/decorators'; import { MouseWheelClassifier } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { IMouseWheelEvent, StandardWheelEvent } from 'vs/base/browser/mouseEvent'; -import { TextAreaSyncAddon } from 'vs/workbench/contrib/terminal/browser/xterm/textAreaSyncAddon'; const enum RenderConstants { /** @@ -128,7 +127,6 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, ID private _markNavigationAddon: MarkNavigationAddon; private _shellIntegrationAddon: ShellIntegrationAddon; private _decorationAddon: DecorationAddon; - private _textAreaSyncAddon: TextAreaSyncAddon; // Optional addons private _suggestAddon?: SuggestAddon; @@ -277,8 +275,6 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, ID this.raw.loadAddon(this._decorationAddon); this._shellIntegrationAddon = new ShellIntegrationAddon(shellIntegrationNonce, disableShellIntegrationReporting, this._telemetryService, this._logService); this.raw.loadAddon(this._shellIntegrationAddon); - this._textAreaSyncAddon = this._instantiationService.createInstance(TextAreaSyncAddon, this._capabilities); - this.raw.loadAddon(this._textAreaSyncAddon); this._anyTerminalFocusContextKey = TerminalContextKeys.focusInAny.bindTo(contextKeyService); this._anyFocusedTerminalHasSelection = TerminalContextKeys.textSelectedInFocused.bindTo(contextKeyService); diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts index 515370eabaa..7a65eda3c5b 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts @@ -6,6 +6,7 @@ import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { ITerminalCapabilityStore, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; +import { ITerminalLogService } from 'vs/platform/terminal/common/terminal'; import type { Terminal, ITerminalAddon } from 'xterm'; export interface ITextAreaData { @@ -25,12 +26,13 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { constructor( private readonly _capabilities: ITerminalCapabilityStore, - @IAccessibilityService private readonly _accessibilityService: IAccessibilityService + @IAccessibilityService private readonly _accessibilityService: IAccessibilityService, + @ITerminalLogService private readonly _logService: ITerminalLogService ) { super(); this._register(this._accessibilityService.onDidChangeScreenReaderOptimized(() => { if (this._accessibilityService.isScreenReaderOptimized() && this._terminal) { - this._refreshTextArea() + this._refreshTextArea(); this._onCursorMoveListener.value = this._terminal.onCursorMove(() => this._refreshTextArea()); } else { this._onCursorMoveListener.clear(); @@ -42,16 +44,18 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { if (!this._terminal) { return; } - + this._logService.debug('TextAreaSyncAddon#refreshTextArea'); const commandCapability = this._capabilities.get(TerminalCapability.CommandDetection); const currentCommand = commandCapability?.currentCommand; if (!currentCommand) { + this._logService.debug(`TextAreaSyncAddon#refreshTextArea: no currentCommand`); return; } const buffer = this._terminal.buffer.active; const line = buffer.getLine(buffer.cursorY)?.translateToString(true); let commandStartX: number | undefined; if (!line) { + this._logService.debug(`TextAreaSyncAddon#refreshTextArea: no line`); return; } let content: string | undefined; @@ -66,26 +70,35 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { } if (!content) { + this._logService.debug(`TextAreaSyncAddon#refreshTextArea: no content`); return; } if (commandStartX === undefined) { + this._logService.debug(`TextAreaSyncAddon#refreshTextArea: no commandStartX`); return; } const textArea = this._terminal.textarea; if (!textArea) { + this._logService.debug(`TextAreaSyncAddon#refreshTextArea: no textarea`); return; } + this._logService.debug(`TextAreaSyncAddon#refreshTextArea: content is "${content}"`); + this._logService.debug(`TextAreaSyncAddon#refreshTextArea: textContent is "${textArea.textContent}"`); if (content !== textArea.textContent) { textArea.textContent = content; + this._logService.debug(`TextAreaSyncAddon#refreshTextArea: textContent changed to "${content}"`); } const cursorX = buffer.cursorX - commandStartX; + this._logService.debug(`TextAreaSyncAddon#refreshTextArea: cursorX is ${cursorX}`); + this._logService.debug(`TextAreaSyncAddon#refreshTextArea: selectionStart is ${textArea.selectionStart}`); if (cursorX !== textArea.selectionStart) { textArea.selectionStart = cursorX; textArea.selectionEnd = cursorX; + this._logService.debug(`TextAreaSyncAddon#refreshTextArea: selectionStart changed to ${cursorX}`); } // TODO: cursorY? } From 0f38f226eb06c95829b33286031e403d932b4e2f Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 15 Aug 2023 12:02:28 -0700 Subject: [PATCH 049/223] make it a contribution --- .../terminal/browser/terminalInstance.ts | 1 + .../terminal.accessibility.contribution.ts | 24 +++++++++++++++++++ .../browser/textAreaSyncAddon.ts | 6 ++--- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index a699b24c929..00e0ad8d01d 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -755,6 +755,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { const lineDataEventAddon = this._register(new LineDataEventAddon(initialTextWrittenPromise)); lineDataEventAddon.onLineData(e => this._onLineData.fire(e)); this._lineDataEventAddon = lineDataEventAddon; + // Delay the creation of the bell listener to avoid showing the bell when the terminal // starts up or reconnects setTimeout(() => { diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts index cc697917066..6d127d0cb16 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts @@ -22,8 +22,32 @@ import { ITerminalProcessManager, TerminalCommandId } from 'vs/workbench/contrib import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; import { TerminalAccessibleContentProvider } from 'vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp'; import { AccessibleBufferWidget, NavigationType } from 'vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBuffer'; +import { TextAreaSyncAddon } from 'vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon'; import type { Terminal } from 'xterm'; + +class TextAreaSyncContribution extends DisposableStore implements ITerminalContribution { + static readonly ID = 'terminal.textAreaSync'; + private _xterm: IXtermTerminal & { raw: Terminal } | undefined; + static get(instance: ITerminalInstance): TextAreaSyncContribution | null { + return instance.getContribution(TextAreaSyncContribution.ID); + } + constructor( + private readonly _instance: ITerminalInstance, + processManager: ITerminalProcessManager, + widgetManager: TerminalWidgetManager, + @IInstantiationService private readonly _instantiationService: IInstantiationService + ) { + super(); + } + xtermReady(xterm: IXtermTerminal & { raw: Terminal }): void { + const addon = this._instantiationService.createInstance(TextAreaSyncAddon, this._instance.capabilities); + this._xterm?.raw.loadAddon(addon); + addon.activate(xterm.raw); + } +} +registerTerminalContribution(TextAreaSyncContribution.ID, TextAreaSyncContribution); + class AccessibleBufferContribution extends DisposableStore implements ITerminalContribution { static readonly ID = 'terminal.accessible-buffer'; private _xterm: IXtermTerminal & { raw: Terminal } | undefined; diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts index 7a65eda3c5b..8ed986db3cf 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts @@ -40,7 +40,7 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { })); } - private _refreshTextArea(): void { + private _refreshTextArea(focusChanged?: boolean): void { if (!this._terminal) { return; } @@ -87,7 +87,7 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { this._logService.debug(`TextAreaSyncAddon#refreshTextArea: content is "${content}"`); this._logService.debug(`TextAreaSyncAddon#refreshTextArea: textContent is "${textArea.textContent}"`); - if (content !== textArea.textContent) { + if (focusChanged || content !== textArea.textContent) { textArea.textContent = content; this._logService.debug(`TextAreaSyncAddon#refreshTextArea: textContent changed to "${content}"`); } @@ -95,7 +95,7 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { const cursorX = buffer.cursorX - commandStartX; this._logService.debug(`TextAreaSyncAddon#refreshTextArea: cursorX is ${cursorX}`); this._logService.debug(`TextAreaSyncAddon#refreshTextArea: selectionStart is ${textArea.selectionStart}`); - if (cursorX !== textArea.selectionStart) { + if (focusChanged || cursorX !== textArea.selectionStart) { textArea.selectionStart = cursorX; textArea.selectionEnd = cursorX; this._logService.debug(`TextAreaSyncAddon#refreshTextArea: selectionStart changed to ${cursorX}`); From 6a847ba6d1d72d3af4d4b10c2b3f193a0a841cfc Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 15 Aug 2023 12:03:51 -0700 Subject: [PATCH 050/223] eng: add support for snapshot tests (#190444) * eng: add support for snapshot tests This adds Jest-like support for snapshot testing. Developers can do something like: ```js await assertSnapshot(myComplexObject) ``` The first time this is run, the snapshot expectation file is written to a `__snapshots__` directory beside the test file. Subsequent runs will compare the object to the snapshot, and fail if it doesn't match. You can see an example of this in the test for snapshots themselves! After a successful run, any unused snapshots are cleaned up. On a failed run, a gitignored `.actual` snapshot file is created beside the snapshot for easy processing and inspection. Shortly I will do some integration with the selfhost test extension to allow developers to easily update snapshots from the vscode UI. For #189680 cc @ulugbekna @hediet * fix async stacktraces getting clobbered * random fixes * comment out leak detector, for now * add option to snapshot file extension --- .gitignore | 1 + build/hygiene.js | 2 + src/vs/base/common/iterator.ts | 2 +- src/vs/base/test/common/snapshot.ts | 185 ++++++++++++++++++ .../snapshot_cleans_up_old_snapshots_0.snap | 8 + .../snapshot_cleans_up_old_snapshots_1.snap | 4 + .../snapshot_creates_a_snapshot_0.snap | 2 + .../snapshot_formats_object_nicely_0.snap | 35 ++++ src/vs/base/test/node/snapshot.test.ts | 143 ++++++++++++++ .../test/browser/bracketMatching.test.ts | 4 +- .../suggest/test/browser/suggest.test.ts | 25 ++- .../bracketPairColorizer/brackets.test.ts | 5 +- .../workbench/api/common/extensionHostMain.ts | 4 +- .../api/test/common/extensionHostMain.test.ts | 5 + .../mergeEditor/test/browser/model.test.ts | 4 +- .../workbench/test/browser/codeeditor.test.ts | 9 +- src/vs/workbench/test/common/utils.ts | 1 - test/unit/browser/index.js | 16 +- test/unit/browser/renderer.html | 21 +- test/unit/electron/renderer.js | 32 ++- test/unit/fullJsonStreamReporter.js | 1 + test/unit/node/index.js | 41 +++- 22 files changed, 504 insertions(+), 46 deletions(-) create mode 100644 src/vs/base/test/common/snapshot.ts create mode 100644 src/vs/base/test/node/__snapshots__/snapshot_cleans_up_old_snapshots_0.snap create mode 100644 src/vs/base/test/node/__snapshots__/snapshot_cleans_up_old_snapshots_1.snap create mode 100644 src/vs/base/test/node/__snapshots__/snapshot_creates_a_snapshot_0.snap create mode 100644 src/vs/base/test/node/__snapshots__/snapshot_formats_object_nicely_0.snap create mode 100644 src/vs/base/test/node/snapshot.test.ts diff --git a/.gitignore b/.gitignore index 32f514ad07a..0601e762dff 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ vscode.db /cli/target /cli/openssl product.overrides.json +*.snap.actual diff --git a/build/hygiene.js b/build/hygiene.js index b8881081b2e..2c01b1f4d75 100644 --- a/build/hygiene.js +++ b/build/hygiene.js @@ -150,10 +150,12 @@ function hygiene(some, linting = true) { } const productJsonFilter = filter('product.json', { restore: true }); + const snapshotFilter = filter(['**', '!**/*.snap', '!**/*.snap.actual']); const unicodeFilterStream = filter(unicodeFilter, { restore: true }); const result = input .pipe(filter((f) => !f.stat.isDirectory())) + .pipe(snapshotFilter) .pipe(productJsonFilter) .pipe(process.env['BUILD_SOURCEVERSION'] ? es.through() : productJson) .pipe(productJsonFilter.restore) diff --git a/src/vs/base/common/iterator.ts b/src/vs/base/common/iterator.ts index 308e03bfcd7..f06dcf7acbc 100644 --- a/src/vs/base/common/iterator.ts +++ b/src/vs/base/common/iterator.ts @@ -47,7 +47,7 @@ export namespace Iterable { return false; } - export function find(iterable: Iterable, predicate: (t: T) => t is R): T | undefined; + export function find(iterable: Iterable, predicate: (t: T) => t is R): R | undefined; export function find(iterable: Iterable, predicate: (t: T) => boolean): T | undefined; export function find(iterable: Iterable, predicate: (t: T) => boolean): T | undefined { for (const element of iterable) { diff --git a/src/vs/base/test/common/snapshot.ts b/src/vs/base/test/common/snapshot.ts new file mode 100644 index 00000000000..f1104cf65dc --- /dev/null +++ b/src/vs/base/test/common/snapshot.ts @@ -0,0 +1,185 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Lazy } from 'vs/base/common/lazy'; +import { FileAccess } from 'vs/base/common/network'; +import { URI } from 'vs/base/common/uri'; + +declare const __readFileInTests: (path: string) => Promise; +declare const __writeFileInTests: (path: string, contents: string) => Promise; +declare const __readDirInTests: (path: string) => Promise; +declare const __unlinkInTests: (path: string) => Promise; +declare const __mkdirPInTests: (path: string) => Promise; + +// setup on import so assertSnapshot has the current context without explicit passing +let context: Lazy | undefined; +const sanitizeName = (name: string) => name.replace(/[^a-z0-9_-]/gi, '_'); +const normalizeCrlf = (str: string) => str.replace(/\r\n/g, '\n'); + +export interface ISnapshotOptions { + /** Name for snapshot file, rather than an incremented number */ + name?: string; + /** Extension name of the snapshot file, defaults to `.snap` */ + extension?: string; +} + +/** + * This is exported only for tests against the snapshotting itself! Use + * {@link assertSnapshot} as a consumer! + */ +export class SnapshotContext { + private nextIndex = 0; + private readonly namePrefix: string; + private readonly snapshotsDir: URI; + private readonly usedNames = new Set(); + + constructor(private readonly test: Mocha.Test | undefined) { + if (!test) { + throw new Error('assertSnapshot can only be used in a test'); + } + + if (!test.file) { + throw new Error('currentTest.file is not set, please open an issue with the test you\'re trying to run'); + } + + const src = FileAccess.asFileUri(''); + const parts = test.file.split(/[/\\]/g); + + this.namePrefix = sanitizeName(test.fullTitle()) + '_'; + this.snapshotsDir = URI.joinPath(src, ...[...parts.slice(0, -1), '__snapshots__']); + } + + public async assert(value: any, options?: ISnapshotOptions) { + const originalStack = new Error().stack!; // save to make the stack nicer on failure + const nameOrIndex = (options?.name ? sanitizeName(options.name) : this.nextIndex++); + const fileName = this.namePrefix + nameOrIndex + '.' + (options?.extension || 'snap'); + this.usedNames.add(fileName); + + const fpath = URI.joinPath(this.snapshotsDir, fileName).fsPath; + const actual = formatValue(value); + let expected: string; + try { + expected = await __readFileInTests(fpath); + } catch { + console.info(`Creating new snapshot in: ${fpath}`); + await __mkdirPInTests(this.snapshotsDir.fsPath); + await __writeFileInTests(fpath, actual); + return; + } + + if (normalizeCrlf(expected) !== normalizeCrlf(actual)) { + await __writeFileInTests(fpath + '.actual', actual); + const err: any = new Error(`Snapshot #${nameOrIndex} does not match expected output`); + err.expected = expected; + err.actual = actual; + err.snapshotPath = fpath; + err.stack = (err.stack as string) + .split('\n') + // remove all frames from the async stack and keep the original caller's frame + .slice(0, 1) + .concat(originalStack.split('\n').slice(3)) + .join('\n'); + throw err; + } + } + + public async removeOldSnapshots() { + const contents = await __readDirInTests(this.snapshotsDir.fsPath); + const toDelete = contents.filter(f => f.startsWith(this.namePrefix) && !this.usedNames.has(f)); + if (toDelete.length) { + console.info(`Deleting ${toDelete.length} old snapshots for ${this.test?.fullTitle()}`); + } + + await Promise.all(toDelete.map(f => __unlinkInTests(URI.joinPath(this.snapshotsDir, f).fsPath))); + } +} + +const debugDescriptionSymbol = Symbol.for('debug.description'); + +function formatValue(value: unknown, level = 0, seen: unknown[] = []): string { + switch (typeof value) { + case 'bigint': + case 'boolean': + case 'number': + case 'symbol': + case 'undefined': + return String(value); + case 'string': + return level === 0 ? value : JSON.stringify(value); + case 'function': + return `[Function ${value.name}]`; + case 'object': { + if (value === null) { + return 'null'; + } + if (value instanceof RegExp) { + return String(value); + } + if (seen.includes(value)) { + return '[Circular]'; + } + if (debugDescriptionSymbol in value && typeof (value as any)[debugDescriptionSymbol] === 'function') { + return (value as any)[debugDescriptionSymbol](); + } + const oi = ' '.repeat(level); + const ci = ' '.repeat(level + 1); + if (Array.isArray(value)) { + const children = value.map(v => formatValue(v, level + 1, [...seen, value])); + const multiline = children.some(c => c.includes('\n')) || children.join(', ').length > 80; + return multiline ? `[\n${ci}${children.join(`,\n${ci}`)}\n${oi}]` : `[ ${children.join(', ')} ]`; + } + + let entries; + let prefix = ''; + if (value instanceof Map) { + prefix = 'Map '; + entries = [...value.entries()]; + } else if (value instanceof Set) { + prefix = 'Set '; + entries = [...value.entries()]; + } else { + entries = Object.entries(value); + } + + const lines = entries.map(([k, v]) => `${k}: ${formatValue(v, level + 1, [...seen, value])}`); + return prefix + (lines.length > 1 + ? `{\n${ci}${lines.join(`,\n${ci}`)}\n${oi}}` + : `{ ${lines.join(',\n')} }`); + } + default: + throw new Error(`Unknown type ${value}`); + } +} + +setup(function () { + const currentTest = this.currentTest; + context = new Lazy(() => new SnapshotContext(currentTest)); +}); +teardown(async function () { + if (this.currentTest?.state === 'passed') { + await context?.rawValue?.removeOldSnapshots(); + } + context = undefined; +}); + +/** + * Implements a snapshot testing utility. ⚠️ This is async! ⚠️ + * + * The first time a snapshot test is run, it'll record the value it's called + * with as the expected value. Subsequent runs will fail if the value differs, + * but the snapshot can be regenerated by hand or using the Selfhost Test + * Provider Extension which'll offer to update it. + * + * The snapshot will be associated with the currently running test and stored + * in a `__snapshots__` directory next to the test file, which is expected to + * be the first `.test.js` file in the callstack. + */ +export function assertSnapshot(value: any, options?: ISnapshotOptions): Promise { + if (!context) { + throw new Error('assertSnapshot can only be used in a test'); + } + + return context.value.assert(value, options); +} diff --git a/src/vs/base/test/node/__snapshots__/snapshot_cleans_up_old_snapshots_0.snap b/src/vs/base/test/node/__snapshots__/snapshot_cleans_up_old_snapshots_0.snap new file mode 100644 index 00000000000..53d99c7989b --- /dev/null +++ b/src/vs/base/test/node/__snapshots__/snapshot_cleans_up_old_snapshots_0.snap @@ -0,0 +1,8 @@ +hello_world__0.snap: + { cool: true } +hello_world__1.snap: + { nifty: true } +hello_world__fourthTest.snap: + { customName: 2 } +hello_world__thirdTest.txt: + { customName: 1 } diff --git a/src/vs/base/test/node/__snapshots__/snapshot_cleans_up_old_snapshots_1.snap b/src/vs/base/test/node/__snapshots__/snapshot_cleans_up_old_snapshots_1.snap new file mode 100644 index 00000000000..b7106c217c2 --- /dev/null +++ b/src/vs/base/test/node/__snapshots__/snapshot_cleans_up_old_snapshots_1.snap @@ -0,0 +1,4 @@ +hello_world__0.snap: + { cool: true } +hello_world__thirdTest.snap: + { customName: 1 } diff --git a/src/vs/base/test/node/__snapshots__/snapshot_creates_a_snapshot_0.snap b/src/vs/base/test/node/__snapshots__/snapshot_creates_a_snapshot_0.snap new file mode 100644 index 00000000000..4c53158fc13 --- /dev/null +++ b/src/vs/base/test/node/__snapshots__/snapshot_creates_a_snapshot_0.snap @@ -0,0 +1,2 @@ +hello_world__0.snap: + { cool: true } diff --git a/src/vs/base/test/node/__snapshots__/snapshot_formats_object_nicely_0.snap b/src/vs/base/test/node/__snapshots__/snapshot_formats_object_nicely_0.snap new file mode 100644 index 00000000000..d5e7745d232 --- /dev/null +++ b/src/vs/base/test/node/__snapshots__/snapshot_formats_object_nicely_0.snap @@ -0,0 +1,35 @@ +[ + 1, + true, + undefined, + null, + 123, + Symbol(heyo), + "hello", + { hello: "world" }, + { a: [Circular] }, + Map { + hello: 1, + goodbye: 2 + }, + Set { + 1: 1, + 2: 2, + 3: 3 + }, + [Function helloWorld], + /hello/g, + [ + "long stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong string", + "long stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong string", + "long stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong string", + "long stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong string", + "long stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong string", + "long stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong string", + "long stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong string", + "long stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong string", + "long stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong string", + "long stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong string" + ], + Range [1 -> 5] +] \ No newline at end of file diff --git a/src/vs/base/test/node/snapshot.test.ts b/src/vs/base/test/node/snapshot.test.ts new file mode 100644 index 00000000000..0f36e39a91d --- /dev/null +++ b/src/vs/base/test/node/snapshot.test.ts @@ -0,0 +1,143 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { tmpdir } from 'os'; +import { getRandomTestPath } from 'vs/base/test/node/testUtils'; +import { Promises } from 'vs/base/node/pfs'; +import { SnapshotContext, assertSnapshot } from 'vs/base/test/common/snapshot'; +import { URI } from 'vs/base/common/uri'; +import path = require('path'); +import { assertThrowsAsync } from 'vs/base/test/common/utils'; + +// tests for snapshot are in Node so that we can use native FS operations to +// set up and validate things. +// +// Uses snapshots for testing snapshots. It's snapception! + +suite('snapshot', () => { + let testDir: string; + + setup(function () { + testDir = getRandomTestPath(tmpdir(), 'vsctests', 'snapshot'); + return Promises.mkdir(testDir, { recursive: true }); + }); + + teardown(function () { + return Promises.rm(testDir); + }); + + const makeContext = (test: Partial | undefined) => { + const ctx = new SnapshotContext(test as Mocha.Test); + (ctx as any as { snapshotsDir: URI }).snapshotsDir = URI.file(testDir); + return ctx; + }; + + const snapshotFileTree = async () => { + let str = ''; + + const printDir = async (dir: string, indent: number) => { + const children = await Promises.readdir(dir); + for (const child of children) { + const p = path.join(dir, child); + if ((await Promises.stat(p)).isFile()) { + const content = await Promises.readFile(p, 'utf-8'); + str += `${' '.repeat(indent)}${child}:\n`; + for (const line of content.split('\n')) { + str += `${' '.repeat(indent + 2)}${line}\n`; + } + } else { + str += `${' '.repeat(indent)}${child}/\n`; + await printDir(p, indent + 2); + } + } + }; + + await printDir(testDir, 0); + await assertSnapshot(str); + }; + + test('creates a snapshot', async () => { + const ctx = makeContext({ + file: 'foo/bar', + fullTitle: () => 'hello world!' + }); + + await ctx.assert({ cool: true }); + await snapshotFileTree(); + }); + + test('validates a snapshot', async () => { + const ctx1 = makeContext({ + file: 'foo/bar', + fullTitle: () => 'hello world!' + }); + + await ctx1.assert({ cool: true }); + + const ctx2 = makeContext({ + file: 'foo/bar', + fullTitle: () => 'hello world!' + }); + + // should pass: + await ctx2.assert({ cool: true }); + + const ctx3 = makeContext({ + file: 'foo/bar', + fullTitle: () => 'hello world!' + }); + + // should fail: + await assertThrowsAsync(() => ctx3.assert({ cool: false })); + }); + + test('cleans up old snapshots', async () => { + const ctx1 = makeContext({ + file: 'foo/bar', + fullTitle: () => 'hello world!' + }); + + await ctx1.assert({ cool: true }); + await ctx1.assert({ nifty: true }); + await ctx1.assert({ customName: 1 }, { name: 'thirdTest', extension: 'txt' }); + await ctx1.assert({ customName: 2 }, { name: 'fourthTest' }); + + await snapshotFileTree(); + + const ctx2 = makeContext({ + file: 'foo/bar', + fullTitle: () => 'hello world!' + }); + + await ctx2.assert({ cool: true }); + await ctx2.assert({ customName: 1 }, { name: 'thirdTest' }); + await ctx2.removeOldSnapshots(); + + await snapshotFileTree(); + }); + + test('formats object nicely', async () => { + const circular: any = {}; + circular.a = circular; + + await assertSnapshot([ + 1, + true, + undefined, + null, + 123n, + Symbol('heyo'), + 'hello', + { hello: 'world' }, + circular, + new Map([['hello', 1], ['goodbye', 2]]), + new Set([1, 2, 3]), + function helloWorld() { }, + /hello/g, + new Array(10).fill('long string'.repeat(10)), + { [Symbol.for('debug.description')]() { return `Range [1 -> 5]`; } }, + ]); + }); +}); diff --git a/src/vs/editor/contrib/bracketMatching/test/browser/bracketMatching.test.ts b/src/vs/editor/contrib/bracketMatching/test/browser/bracketMatching.test.ts index c16f4d7e9af..289fe8aa96a 100644 --- a/src/vs/editor/contrib/bracketMatching/test/browser/bracketMatching.test.ts +++ b/src/vs/editor/contrib/bracketMatching/test/browser/bracketMatching.test.ts @@ -12,9 +12,9 @@ import { instantiateTextModel } from 'vs/editor/test/common/testTextModel'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { ILanguageService } from 'vs/editor/common/languages/language'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('bracket matching', () => { - let disposables: DisposableStore; let instantiationService: TestInstantiationService; let languageConfigurationService: ILanguageConfigurationService; @@ -31,6 +31,8 @@ suite('bracket matching', () => { disposables.dispose(); }); + ensureNoDisposablesAreLeakedInTestSuite(); + function createTextModelWithBrackets(text: string) { const languageId = 'bracketMode'; disposables.add(languageService.registerLanguage({ id: languageId })); diff --git a/src/vs/editor/contrib/suggest/test/browser/suggest.test.ts b/src/vs/editor/contrib/suggest/test/browser/suggest.test.ts index 1ff62f2cb35..d4df28ea332 100644 --- a/src/vs/editor/contrib/suggest/test/browser/suggest.test.ts +++ b/src/vs/editor/contrib/suggest/test/browser/suggest.test.ts @@ -12,10 +12,10 @@ import { CompletionItemKind, CompletionItemProvider } from 'vs/editor/common/lan import { CompletionOptions, provideSuggestionItems, SnippetSortOrder } from 'vs/editor/contrib/suggest/browser/suggest'; import { createTextModel } from 'vs/editor/test/common/testTextModel'; import { LanguageFeatureRegistry } from 'vs/editor/common/languageFeatureRegistry'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('Suggest', function () { - let model: TextModel; let registration: IDisposable; let registry: LanguageFeatureRegistry; @@ -54,37 +54,43 @@ suite('Suggest', function () { model.dispose(); }); + ensureNoDisposablesAreLeakedInTestSuite(); + test('sort - snippet inline', async function () { - const { items } = await provideSuggestionItems(registry, model, new Position(1, 1), new CompletionOptions(SnippetSortOrder.Inline)); + const { items, disposable } = await provideSuggestionItems(registry, model, new Position(1, 1), new CompletionOptions(SnippetSortOrder.Inline)); assert.strictEqual(items.length, 3); assert.strictEqual(items[0].completion.label, 'aaa'); assert.strictEqual(items[1].completion.label, 'fff'); assert.strictEqual(items[2].completion.label, 'zzz'); + disposable.dispose(); }); test('sort - snippet top', async function () { - const { items } = await provideSuggestionItems(registry, model, new Position(1, 1), new CompletionOptions(SnippetSortOrder.Top)); + const { items, disposable } = await provideSuggestionItems(registry, model, new Position(1, 1), new CompletionOptions(SnippetSortOrder.Top)); assert.strictEqual(items.length, 3); assert.strictEqual(items[0].completion.label, 'aaa'); assert.strictEqual(items[1].completion.label, 'zzz'); assert.strictEqual(items[2].completion.label, 'fff'); + disposable.dispose(); }); test('sort - snippet bottom', async function () { - const { items } = await provideSuggestionItems(registry, model, new Position(1, 1), new CompletionOptions(SnippetSortOrder.Bottom)); + const { items, disposable } = await provideSuggestionItems(registry, model, new Position(1, 1), new CompletionOptions(SnippetSortOrder.Bottom)); assert.strictEqual(items.length, 3); assert.strictEqual(items[0].completion.label, 'fff'); assert.strictEqual(items[1].completion.label, 'aaa'); assert.strictEqual(items[2].completion.label, 'zzz'); + disposable.dispose(); }); test('sort - snippet none', async function () { - const { items } = await provideSuggestionItems(registry, model, new Position(1, 1), new CompletionOptions(undefined, new Set().add(CompletionItemKind.Snippet))); + const { items, disposable } = await provideSuggestionItems(registry, model, new Position(1, 1), new CompletionOptions(undefined, new Set().add(CompletionItemKind.Snippet))); assert.strictEqual(items.length, 1); assert.strictEqual(items[0].completion.label, 'fff'); + disposable.dispose(); }); - test('only from', function () { + test('only from', function (callback) { const foo: any = { triggerCharacters: [], @@ -102,11 +108,13 @@ suite('Suggest', function () { }; const registration = registry.register({ pattern: 'bar/path', scheme: 'foo' }, foo); - provideSuggestionItems(registry, model, new Position(1, 1), new CompletionOptions(undefined, undefined, new Set().add(foo))).then(({ items }) => { + provideSuggestionItems(registry, model, new Position(1, 1), new CompletionOptions(undefined, undefined, new Set().add(foo))).then(({ items, disposable }) => { registration.dispose(); assert.strictEqual(items.length, 1); assert.ok(items[0].provider === foo); + disposable.dispose(); + callback(); }); }); @@ -142,7 +150,7 @@ suite('Suggest', function () { }; const registration = registry.register({ pattern: 'bar/path', scheme: 'foo' }, foo); - const { items } = await provideSuggestionItems(registry, model, new Position(0, 0), new CompletionOptions(undefined, undefined, new Set().add(foo))); + const { items, disposable } = await provideSuggestionItems(registry, model, new Position(0, 0), new CompletionOptions(undefined, undefined, new Set().add(foo))); registration.dispose(); assert.strictEqual(items.length, 2); @@ -152,5 +160,6 @@ suite('Suggest', function () { assert.strictEqual(a.isInvalid, false); assert.strictEqual(b.completion.label, 'two'); assert.strictEqual(b.isInvalid, true); + disposable.dispose(); }); }); diff --git a/src/vs/editor/test/common/model/bracketPairColorizer/brackets.test.ts b/src/vs/editor/test/common/model/bracketPairColorizer/brackets.test.ts index abf6200a8fa..2a21907778d 100644 --- a/src/vs/editor/test/common/model/bracketPairColorizer/brackets.test.ts +++ b/src/vs/editor/test/common/model/bracketPairColorizer/brackets.test.ts @@ -5,12 +5,15 @@ import * as assert from 'assert'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { LanguageAgnosticBracketTokens } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/brackets'; import { SmallImmutableSet, DenseKeyProvider } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/smallImmutableSet'; import { Token, TokenKind } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/tokenizer'; import { TestLanguageConfigurationService } from 'vs/editor/test/common/modes/testLanguageConfigurationService'; suite('Bracket Pair Colorizer - Brackets', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + test('Basic', () => { const languageId = 'testMode1'; const denseKeyProvider = new DenseKeyProvider(); @@ -24,7 +27,7 @@ suite('Bracket Pair Colorizer - Brackets', () => { }; const disposableStore = new DisposableStore(); - const languageConfigService = new TestLanguageConfigurationService(); + const languageConfigService = disposableStore.add(new TestLanguageConfigurationService()); disposableStore.add(languageConfigService.register(languageId, { brackets: [ ['{', '}'], ['[', ']'], ['(', ')'], diff --git a/src/vs/workbench/api/common/extensionHostMain.ts b/src/vs/workbench/api/common/extensionHostMain.ts index a06c4e93d8f..40f1cdaf146 100644 --- a/src/vs/workbench/api/common/extensionHostMain.ts +++ b/src/vs/workbench/api/common/extensionHostMain.ts @@ -97,8 +97,8 @@ export abstract class ErrorHandler { return _prepareStackTrace; }, set(v) { - if (v === prepareStackTraceAndFindExtension || v[_wasWrapped]) { - _prepareStackTrace = v; + if (v === prepareStackTraceAndFindExtension || !v || v[_wasWrapped]) { + _prepareStackTrace = v || prepareStackTraceAndFindExtension; return; } diff --git a/src/vs/workbench/api/test/common/extensionHostMain.test.ts b/src/vs/workbench/api/test/common/extensionHostMain.test.ts index 52b49352d81..e201637150c 100644 --- a/src/vs/workbench/api/test/common/extensionHostMain.test.ts +++ b/src/vs/workbench/api/test/common/extensionHostMain.test.ts @@ -63,6 +63,7 @@ suite('ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slo }] ); + const originalPrepareStackTrace = Error.prepareStackTrace; const insta = new InstantiationService(collection, false); let existingErrorHandler: (e: any) => void; @@ -81,6 +82,10 @@ suite('ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slo findSubstrCount = 0; }); + teardown(() => { + Error.prepareStackTrace = originalPrepareStackTrace; + }); + test('basics', function () { const err = new Error('test1'); diff --git a/src/vs/workbench/contrib/mergeEditor/test/browser/model.test.ts b/src/vs/workbench/contrib/mergeEditor/test/browser/model.test.ts index a32952de8ca..d411e9a4c1f 100644 --- a/src/vs/workbench/contrib/mergeEditor/test/browser/model.test.ts +++ b/src/vs/workbench/contrib/mergeEditor/test/browser/model.test.ts @@ -7,7 +7,6 @@ import * as assert from 'assert'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { IReader, transaction } from 'vs/base/common/observable'; import { isDefined } from 'vs/base/common/types'; -import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { Range } from 'vs/editor/common/core/range'; import { linesDiffComputers } from 'vs/editor/common/diff/linesDiffComputers'; import { EndOfLinePreference, ITextModel } from 'vs/editor/common/model'; @@ -20,7 +19,8 @@ import { MergeEditorModel } from 'vs/workbench/contrib/mergeEditor/browser/model import { MergeEditorTelemetry } from 'vs/workbench/contrib/mergeEditor/browser/telemetry'; suite('merge editor model', () => { - ensureNoDisposablesAreLeakedInTestSuite(); + // todo: renable when failing case is found https://github.com/microsoft/vscode/pull/190444#issuecomment-1678151428 + // ensureNoDisposablesAreLeakedInTestSuite(); test('prepend line', async () => { await testMergeModel( diff --git a/src/vs/workbench/test/browser/codeeditor.test.ts b/src/vs/workbench/test/browser/codeeditor.test.ts index 75f27d69dca..6bea2f87b2e 100644 --- a/src/vs/workbench/test/browser/codeeditor.test.ts +++ b/src/vs/workbench/test/browser/codeeditor.test.ts @@ -25,6 +25,7 @@ import { createTextModel } from 'vs/editor/test/common/testTextModel'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('Editor - Range decorations', () => { @@ -43,13 +44,13 @@ suite('Editor - Range decorations', () => { instantiationService.stub(ILanguageService, LanguageService); instantiationService.stub(IModelService, stubModelService(instantiationService)); text = 'LINE1' + '\n' + 'LINE2' + '\n' + 'LINE3' + '\n' + 'LINE4' + '\r\n' + 'LINE5'; - model = aModel(URI.file('some_file')); - codeEditor = createTestCodeEditor(model); + model = disposables.add(aModel(URI.file('some_file'))); + codeEditor = disposables.add(createTestCodeEditor(model)); instantiationService.stub(IEditorService, 'activeEditor', { get resource() { return codeEditor.getModel()!.uri; } }); instantiationService.stub(IEditorService, 'activeTextEditorControl', codeEditor); - testObject = instantiationService.createInstance(RangeHighlightDecorations); + testObject = disposables.add(instantiationService.createInstance(RangeHighlightDecorations)); }); teardown(() => { @@ -58,6 +59,8 @@ suite('Editor - Range decorations', () => { disposables.dispose(); }); + ensureNoDisposablesAreLeakedInTestSuite(); + test('highlight range for the resource if it is an active editor', function () { const range: IRange = new Range(1, 1, 1, 1); testObject.highlightRange({ resource: model.uri, range }); diff --git a/src/vs/workbench/test/common/utils.ts b/src/vs/workbench/test/common/utils.ts index 498c5c499fe..f4a175f315a 100644 --- a/src/vs/workbench/test/common/utils.ts +++ b/src/vs/workbench/test/common/utils.ts @@ -5,7 +5,6 @@ import * as assert from 'assert'; import { LanguagesRegistry } from 'vs/editor/common/services/languagesRegistry'; -// import { LanguageService } from 'vs/editor/common/services/languageServiceImpl'; /** * This function is called before test running and also again at the end of test running diff --git a/test/unit/browser/index.js b/test/unit/browser/index.js index 668227d2fa5..5d19c5e5205 100644 --- a/test/unit/browser/index.js +++ b/test/unit/browser/index.js @@ -14,6 +14,7 @@ const createStatsCollector = require('../../../node_modules/mocha/lib/stats-coll const MochaJUnitReporter = require('mocha-junit-reporter'); const url = require('url'); const minimatch = require('minimatch'); +const fs = require('fs'); const playwright = require('@playwright/test'); const { applyReporter } = require('../reporter'); @@ -141,9 +142,18 @@ async function runTestsInBrowser(testModules, browserType) { } const emitter = new events.EventEmitter(); - await page.exposeFunction('mocha_report', (type, data1, data2) => { - emitter.emit(type, data1, data2); - }); + + await Promise.all([ + page.exposeFunction('mocha_report', (type, data1, data2) => { + emitter.emit(type, data1, data2); + }), + // Test file operations that are common across platforms. Used for test infra, namely snapshot tests + page.exposeFunction('__readFileInTests', (path) => fs.promises.readFile(path, 'utf-8')), + page.exposeFunction('__writeFileInTests', (path, contents) => fs.promises.writeFile(path, contents)), + page.exposeFunction('__readDirInTests', (path) => fs.promises.readdir(path)), + page.exposeFunction('__unlinkInTests', (path) => fs.promises.unlink(path)), + page.exposeFunction('__mkdirPInTests', (path) => fs.promises.mkdir(path, { recursive: true })), + ]); await page.goto(target.href); diff --git a/test/unit/browser/renderer.html b/test/unit/browser/renderer.html index 540dd2a82aa..45786072900 100644 --- a/test/unit/browser/renderer.html +++ b/test/unit/browser/renderer.html @@ -116,15 +116,22 @@ runner.on('pending', test => window.mocha_report('pending', serializeRunnable(test))); }; + + async function loadModules(modules) { + for (const file of modules) { + mocha.suite.emit(Mocha.Suite.constants.EVENT_FILE_PRE_REQUIRE, globalThis, file, mocha); + const m = await new Promise((resolve, reject) => require([file], resolve, err => { + console.log("BAD " + file + JSON.stringify(err, undefined, '\t')); + resolve({}); + })); + mocha.suite.emit(Mocha.Suite.constants.EVENT_FILE_REQUIRE, m, file, mocha); + mocha.suite.emit(Mocha.Suite.constants.EVENT_FILE_POST_REQUIRE, globalThis, file, mocha); + } + } + window.loadAndRun = async function loadAndRun({ modules, grep }, manual = false) { // load - await Promise.all(modules.map(module => new Promise((resolve, reject) => { - require([module], resolve, err => { - console.log("BAD " + module + JSON.stringify(err, undefined, '\t')); - // console.log(module); - resolve({}); - }); - }))); + await loadModules(modules); // await new Promise((resolve, reject) => { // require(modules, resolve, err => { // console.log(err); diff --git a/test/unit/electron/renderer.js b/test/unit/electron/renderer.js index a91725887a5..21ef972f7c4 100644 --- a/test/unit/electron/renderer.js +++ b/test/unit/electron/renderer.js @@ -5,8 +5,9 @@ /*eslint-env mocha*/ +const fs = require('fs'); + (function () { - const fs = require('fs'); const originals = {}; let logging = false; let withStacks = false; @@ -79,6 +80,15 @@ globalThis._VSCODE_NODE_MODULES = new Proxy(Object.create(null), { get: (_target globalThis._VSCODE_PRODUCT_JSON = (require.__$__nodeRequire ?? require)('../../../product.json'); globalThis._VSCODE_PACKAGE_JSON = (require.__$__nodeRequire ?? require)('../../../package.json'); +// Test file operations that are common across platforms. Used for test infra, namely snapshot tests +Object.assign(globalThis, { + __readFileInTests: path => fs.promises.readFile(path, 'utf-8'), + __writeFileInTests: (path, contents) => fs.promises.writeFile(path, contents), + __readDirInTests: path => fs.promises.readdir(path), + __unlinkInTests: path => fs.promises.unlink(path), + __mkdirPInTests: path => fs.promises.mkdir(path, { recursive: true }), +}); + const _tests_glob = '**/test/**/*.test.js'; let loader; let _out; @@ -121,6 +131,15 @@ function loadWorkbenchTestingUtilsModule() { }); } +async function loadModules(modules) { + for (const file of modules) { + mocha.suite.emit(Mocha.Suite.constants.EVENT_FILE_PRE_REQUIRE, globalThis, file, mocha); + const m = await new Promise((resolve, reject) => loader.require([file], resolve, reject)); + mocha.suite.emit(Mocha.Suite.constants.EVENT_FILE_REQUIRE, m, file, mocha); + mocha.suite.emit(Mocha.Suite.constants.EVENT_FILE_POST_REQUIRE, globalThis, file, mocha); + } +} + function loadTestModules(opts) { if (opts.run) { @@ -130,9 +149,7 @@ function loadTestModules(opts) { file = file.replace(/\.ts$/, '.js'); return path.relative(_out, file).replace(/\.js$/, ''); }); - return new Promise((resolve, reject) => { - loader.require(modules, resolve, reject); - }); + return loadModules(modules); } const pattern = opts.runGlob || _tests_glob; @@ -146,11 +163,7 @@ function loadTestModules(opts) { const modules = files.map(file => file.replace(/\.js$/, '')); resolve(modules); }); - }).then(modules => { - return new Promise((resolve, reject) => { - loader.require(modules, resolve, reject); - }); - }); + }).then(loadModules); } function loadTests(opts) { @@ -239,6 +252,7 @@ function serializeError(err) { return { message: err.message, stack: err.stack, + snapshotPath: err.snapshotPath, actual: safeStringify({ value: err.actual }), expected: safeStringify({ value: err.expected }), uncaught: err.uncaught, diff --git a/test/unit/fullJsonStreamReporter.js b/test/unit/fullJsonStreamReporter.js index 0f3ff897c8e..07b2315a004 100644 --- a/test/unit/fullJsonStreamReporter.js +++ b/test/unit/fullJsonStreamReporter.js @@ -37,6 +37,7 @@ module.exports = class FullJsonStreamReporter extends BaseRunner { test.expected = err.expected; test.actualJSON = err.actualJSON; test.expectedJSON = err.expectedJSON; + test.snapshotPath = err.snapshotPath; test.err = err.message; test.stack = err.stack || null; writeEvent(['fail', test]); diff --git a/test/unit/node/index.js b/test/unit/node/index.js index 757df426336..b6f326a27d8 100644 --- a/test/unit/node/index.js +++ b/test/unit/node/index.js @@ -9,7 +9,7 @@ process.env.MOCHA_COLORS = '1'; // Force colors (note that this must come before any mocha imports) const assert = require('assert'); -const mocha = require('mocha'); +const Mocha = require('mocha'); const path = require('path'); const fs = require('fs'); const glob = require('glob'); @@ -64,6 +64,14 @@ function main() { globalThis._VSCODE_PRODUCT_JSON = require(`${REPO_ROOT}/product.json`); globalThis._VSCODE_PACKAGE_JSON = require(`${REPO_ROOT}/package.json`); + // Test file operations that are common across platforms. Used for test infra, namely snapshot tests + Object.assign(globalThis, { + __readFileInTests: (/** @type {string} */ path) => fs.promises.readFile(path, 'utf-8'), + __writeFileInTests: (/** @type {string} */ path, /** @type {BufferEncoding} */ contents) => fs.promises.writeFile(path, contents), + __readDirInTests: (/** @type {string} */ path) => fs.promises.readdir(path), + __unlinkInTests: (/** @type {string} */ path) => fs.promises.unlink(path), + __mkdirPInTests: (/** @type {string} */ path) => fs.promises.mkdir(path, { recursive: true }), + }); process.on('uncaughtException', function (e) { console.error(e.stack || e); @@ -127,7 +135,24 @@ function main() { return write.apply(process.stderr, args); }; - /** @type { (callback:(err:any)=>void)=>void } */ + + const runner = new Mocha({ + ui: 'tdd' + }); + + /** + * @param {string[]} modules + */ + async function loadModules(modules) { + for (const file of modules) { + runner.suite.emit(Mocha.Suite.constants.EVENT_FILE_PRE_REQUIRE, globalThis, file, runner); + const m = await new Promise((resolve, reject) => loader([file], resolve, reject)); + runner.suite.emit(Mocha.Suite.constants.EVENT_FILE_REQUIRE, m, file, runner); + runner.suite.emit(Mocha.Suite.constants.EVENT_FILE_POST_REQUIRE, globalThis, file, runner); + } + } + + /** @type { null|((callback:(err:any)=>void)=>void) } */ let loadFunc = null; if (argv.runGlob) { @@ -140,7 +165,7 @@ function main() { return test.replace(/(\.js)|(\.d\.ts)|(\.js\.map)$/, ''); }); - loader(modulesToLoad, () => cb(null), cb); + loadModules(modulesToLoad).then(() => cb(null), cb); }; glob(argv.runGlob, { cwd: src }, function (err, files) { doRun(files); }); @@ -153,7 +178,7 @@ function main() { return path.relative(src, path.resolve(test)).replace(/(\.js)|(\.js\.map)$/, '').replace(/\\/g, '/'); }); loadFunc = (cb) => { - loader(modulesToLoad, () => cb(null), cb); + loadModules(modulesToLoad).then(() => cb(null), cb); }; } else { loadFunc = (cb) => { @@ -165,7 +190,7 @@ function main() { modules.push(file.replace(/\.js$/, '')); } } - loader(modules, function () { cb(null); }, cb); + loadModules(modules).then(() => cb(null), cb); }); }; } @@ -180,7 +205,7 @@ function main() { if (!argv.run && !argv.runGlob) { // set up last test - mocha.suite('Loader', function () { + Mocha.suite('Loader', function () { test('should not explode while loading', function () { assert.ok(!didErr, 'should not explode while loading'); }); @@ -189,7 +214,7 @@ function main() { // report failing test for every unexpected error during any of the tests const unexpectedErrors = []; - mocha.suite('Errors', function () { + Mocha.suite('Errors', function () { test('should not have unexpected errors in tests', function () { if (unexpectedErrors.length) { unexpectedErrors.forEach(function (stack) { @@ -210,7 +235,7 @@ function main() { }); // fire up mocha - mocha.run(); + runner.run(failures => process.exit(failures ? 1 : 0)); }); }); } From 516e6757b985334ea9d28afcd7e544fbfaf6d793 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Tue, 15 Aug 2023 12:09:35 -0700 Subject: [PATCH 051/223] Update src/vs/workbench/contrib/terminal/browser/terminalInstance.ts --- src/vs/workbench/contrib/terminal/browser/terminalInstance.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 00e0ad8d01d..a699b24c929 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -755,7 +755,6 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { const lineDataEventAddon = this._register(new LineDataEventAddon(initialTextWrittenPromise)); lineDataEventAddon.onLineData(e => this._onLineData.fire(e)); this._lineDataEventAddon = lineDataEventAddon; - // Delay the creation of the bell listener to avoid showing the bell when the terminal // starts up or reconnects setTimeout(() => { From 6c7f0cc8837825a4371298c7cfdf591b2c67e1da Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Tue, 15 Aug 2023 12:49:34 -0700 Subject: [PATCH 052/223] Fix telemetry module (#190513) * Fix telemetry module * Update missed package.json --- package.json | 4 +-- remote/package.json | 4 +-- remote/web/package.json | 4 +-- remote/web/yarn.lock | 76 ++++++++++++++------------------------- remote/yarn.lock | 76 ++++++++++++++------------------------- yarn.lock | 78 +++++++++++++++-------------------------- 6 files changed, 88 insertions(+), 154 deletions(-) diff --git a/package.json b/package.json index d1d4d96baca..470bc68794f 100644 --- a/package.json +++ b/package.json @@ -65,8 +65,8 @@ "perf": "node scripts/code-perf.js" }, "dependencies": { - "@microsoft/1ds-core-js": "^4.0.1", - "@microsoft/1ds-post-js": "^4.0.1", + "@microsoft/1ds-core-js": "^3.2.13", + "@microsoft/1ds-post-js": "^3.2.13", "@parcel/watcher": "2.1.0", "@vscode/iconv-lite-umd": "0.7.0", "@vscode/policy-watcher": "^1.1.4", diff --git a/remote/package.json b/remote/package.json index fe40ca68498..0edbe410766 100644 --- a/remote/package.json +++ b/remote/package.json @@ -3,8 +3,8 @@ "version": "0.0.0", "private": true, "dependencies": { - "@microsoft/1ds-core-js": "^4.0.1", - "@microsoft/1ds-post-js": "^4.0.1", + "@microsoft/1ds-core-js": "^3.2.13", + "@microsoft/1ds-post-js": "^3.2.13", "@parcel/watcher": "2.1.0", "@vscode/iconv-lite-umd": "0.7.0", "@vscode/proxy-agent": "^0.17.2", diff --git a/remote/web/package.json b/remote/web/package.json index 268582951b9..9c1b107dd78 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -3,8 +3,8 @@ "version": "0.0.0", "private": true, "dependencies": { - "@microsoft/1ds-core-js": "^4.0.1", - "@microsoft/1ds-post-js": "^4.0.1", + "@microsoft/1ds-core-js": "^3.2.13", + "@microsoft/1ds-post-js": "^3.2.13", "@vscode/iconv-lite-umd": "0.7.0", "@vscode/vscode-languagedetection": "1.0.21", "jschardet": "3.0.0", diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index 6419492377f..a5a1fd6c1ee 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -2,63 +2,41 @@ # yarn lockfile v1 -"@microsoft/1ds-core-js@4.0.1", "@microsoft/1ds-core-js@^4.0.1": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-4.0.1.tgz#6fcf129276c933934d618f7fc3de49613259e3ec" - integrity sha512-BcTKhcRixCqLYEo1xVlQofvvZpgey9qmkzxQd1j9bBZS4iQl9IOmGBzlf/vGSITC7VXtEk2NZOlHQN0llAIfSw== +"@microsoft/1ds-core-js@3.2.13", "@microsoft/1ds-core-js@^3.2.13": + version "3.2.13" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.13.tgz#0c105ed75091bae3f1555c0334704fa9911c58fb" + integrity sha512-CluYTRWcEk0ObG5EWFNWhs87e2qchJUn0p2D21ZUa3PWojPZfPSBs4//WIE0MYV8Qg1Hdif2ZTwlM7TbYUjfAg== dependencies: - "@microsoft/applicationinsights-core-js" "3.0.2" - "@microsoft/applicationinsights-shims" "^3.0.1" - "@microsoft/dynamicproto-js" "^2.0.2" - "@nevware21/ts-async" ">= 0.2.4 < 2.x" - "@nevware21/ts-utils" ">= 0.9.6 < 2.x" + "@microsoft/applicationinsights-core-js" "2.8.15" + "@microsoft/applicationinsights-shims" "^2.0.2" + "@microsoft/dynamicproto-js" "^1.1.7" -"@microsoft/1ds-post-js@^4.0.1": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-4.0.1.tgz#8630927d10518067686104baec98557b74317e4e" - integrity sha512-uyYkG9mxzzj/4HgxVJsgcz9abfakW+Rjrsnor/hXS5I5ezE3YUX+54DLvAh/c6mEgoDI7u8wGkGl3HgCtAYbDQ== +"@microsoft/1ds-post-js@^3.2.13": + version "3.2.13" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.13.tgz#560aacac8a92fdbb79e8c2ebcb293d56e19f51aa" + integrity sha512-HgS574fdD19Bo2vPguyznL4eDw7Pcm1cVNpvbvBLWiW3x4e1FCQ3VMXChWnAxCae8Hb0XqlA2sz332ZobBavTA== dependencies: - "@microsoft/1ds-core-js" "4.0.1" - "@microsoft/applicationinsights-shims" "^3.0.1" - "@microsoft/dynamicproto-js" "^2.0.2" - "@nevware21/ts-async" ">= 0.2.4 < 2.x" - "@nevware21/ts-utils" ">= 0.9.6 < 2.x" + "@microsoft/1ds-core-js" "3.2.13" + "@microsoft/applicationinsights-shims" "^2.0.2" + "@microsoft/dynamicproto-js" "^1.1.7" -"@microsoft/applicationinsights-core-js@3.0.2": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.2.tgz#108e20df8c162bec92b1f66f9de2530a25d9f51a" - integrity sha512-WQhVhzlRlLDrQzn3OShCW/pL3BW5WC57t0oywSknX3q7lMzI3jDg7Ihh0iuIcNTzGCTbDkuqr4d6IjEDWIMtJQ== +"@microsoft/applicationinsights-core-js@2.8.15": + version "2.8.15" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.15.tgz#8fa466474260e01967fe649f14dd9e5ff91dcdc8" + integrity sha512-yYAs9MyjGr2YijQdUSN9mVgT1ijI1FPMgcffpaPmYbHAVbQmF7bXudrBWHxmLzJlwl5rfep+Zgjli2e67lwUqQ== dependencies: - "@microsoft/applicationinsights-shims" "3.0.1" - "@microsoft/dynamicproto-js" "^2.0.2" - "@nevware21/ts-async" ">= 0.2.4 < 2.x" - "@nevware21/ts-utils" ">= 0.9.5 < 2.x" + "@microsoft/applicationinsights-shims" "2.0.2" + "@microsoft/dynamicproto-js" "^1.1.9" -"@microsoft/applicationinsights-shims@3.0.1", "@microsoft/applicationinsights-shims@^3.0.1": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz#3865b73ace8405b9c4618cc5c571f2fe3876f06f" - integrity sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg== - dependencies: - "@nevware21/ts-utils" ">= 0.9.4 < 2.x" - -"@microsoft/dynamicproto-js@^2.0.2": +"@microsoft/applicationinsights-shims@2.0.2", "@microsoft/applicationinsights-shims@^2.0.2": version "2.0.2" - resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz#e57fbec2e7067d48b7e8e1e1c1d354028ef718a6" - integrity sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg== - dependencies: - "@nevware21/ts-utils" ">= 0.9.4 < 2.x" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-2.0.2.tgz#92b36a09375e2d9cb2b4203383b05772be837085" + integrity sha512-PoHEgsnmcqruLNHZ/amACqdJ6YYQpED0KSRe6J7gIJTtpZC1FfFU9b1fmDKDKtFoUSrPzEh1qzO3kmRZP0betg== -"@nevware21/ts-async@>= 0.2.4 < 2.x": - version "0.2.6" - resolved "https://registry.yarnpkg.com/@nevware21/ts-async/-/ts-async-0.2.6.tgz#9fad1c4a56b01430b407d454817f68bb6ace92b0" - integrity sha512-NCUqEZSbsy7LVtKlUScd/eTst6djkWauLlzoIPVKCOxalEBdO8lrgNRIm4Xy68JNudNN5faqa2WA12X8m0BVhA== - dependencies: - "@nevware21/ts-utils" ">= 0.9.7 < 2.x" - -"@nevware21/ts-utils@>= 0.9.4 < 2.x", "@nevware21/ts-utils@>= 0.9.5 < 2.x", "@nevware21/ts-utils@>= 0.9.6 < 2.x", "@nevware21/ts-utils@>= 0.9.7 < 2.x": - version "0.9.8" - resolved "https://registry.yarnpkg.com/@nevware21/ts-utils/-/ts-utils-0.9.8.tgz#6db23211a5c7539d3b4362e273087effcfc7d2f8" - integrity sha512-kZ8s8hcn9jPVX/M7kSsBYrOGlHjqLahmxrG7QeKTk5paeVwfgKdvVCjj5Acb4UGb/ukU1G34U1Z3eb7bbVanyA== +"@microsoft/dynamicproto-js@^1.1.7", "@microsoft/dynamicproto-js@^1.1.9": + version "1.1.9" + resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.9.tgz#7437db7aa061162ee94e4131b69a62b8dad5dea6" + integrity sha512-n1VPsljTSkthsAFYdiWfC+DKzK2WwcRp83Y1YAqdX552BstvsDjft9YXppjUzp11BPsapDoO1LDgrDB0XVsfNQ== "@vscode/iconv-lite-umd@0.7.0": version "0.7.0" diff --git a/remote/yarn.lock b/remote/yarn.lock index 3dab9abaf97..15f25782773 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -2,63 +2,41 @@ # yarn lockfile v1 -"@microsoft/1ds-core-js@4.0.1", "@microsoft/1ds-core-js@^4.0.1": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-4.0.1.tgz#6fcf129276c933934d618f7fc3de49613259e3ec" - integrity sha512-BcTKhcRixCqLYEo1xVlQofvvZpgey9qmkzxQd1j9bBZS4iQl9IOmGBzlf/vGSITC7VXtEk2NZOlHQN0llAIfSw== +"@microsoft/1ds-core-js@3.2.13", "@microsoft/1ds-core-js@^3.2.13": + version "3.2.13" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.13.tgz#0c105ed75091bae3f1555c0334704fa9911c58fb" + integrity sha512-CluYTRWcEk0ObG5EWFNWhs87e2qchJUn0p2D21ZUa3PWojPZfPSBs4//WIE0MYV8Qg1Hdif2ZTwlM7TbYUjfAg== dependencies: - "@microsoft/applicationinsights-core-js" "3.0.2" - "@microsoft/applicationinsights-shims" "^3.0.1" - "@microsoft/dynamicproto-js" "^2.0.2" - "@nevware21/ts-async" ">= 0.2.4 < 2.x" - "@nevware21/ts-utils" ">= 0.9.6 < 2.x" + "@microsoft/applicationinsights-core-js" "2.8.15" + "@microsoft/applicationinsights-shims" "^2.0.2" + "@microsoft/dynamicproto-js" "^1.1.7" -"@microsoft/1ds-post-js@^4.0.1": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-4.0.1.tgz#8630927d10518067686104baec98557b74317e4e" - integrity sha512-uyYkG9mxzzj/4HgxVJsgcz9abfakW+Rjrsnor/hXS5I5ezE3YUX+54DLvAh/c6mEgoDI7u8wGkGl3HgCtAYbDQ== +"@microsoft/1ds-post-js@^3.2.13": + version "3.2.13" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.13.tgz#560aacac8a92fdbb79e8c2ebcb293d56e19f51aa" + integrity sha512-HgS574fdD19Bo2vPguyznL4eDw7Pcm1cVNpvbvBLWiW3x4e1FCQ3VMXChWnAxCae8Hb0XqlA2sz332ZobBavTA== dependencies: - "@microsoft/1ds-core-js" "4.0.1" - "@microsoft/applicationinsights-shims" "^3.0.1" - "@microsoft/dynamicproto-js" "^2.0.2" - "@nevware21/ts-async" ">= 0.2.4 < 2.x" - "@nevware21/ts-utils" ">= 0.9.6 < 2.x" + "@microsoft/1ds-core-js" "3.2.13" + "@microsoft/applicationinsights-shims" "^2.0.2" + "@microsoft/dynamicproto-js" "^1.1.7" -"@microsoft/applicationinsights-core-js@3.0.2": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.2.tgz#108e20df8c162bec92b1f66f9de2530a25d9f51a" - integrity sha512-WQhVhzlRlLDrQzn3OShCW/pL3BW5WC57t0oywSknX3q7lMzI3jDg7Ihh0iuIcNTzGCTbDkuqr4d6IjEDWIMtJQ== +"@microsoft/applicationinsights-core-js@2.8.15": + version "2.8.15" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.15.tgz#8fa466474260e01967fe649f14dd9e5ff91dcdc8" + integrity sha512-yYAs9MyjGr2YijQdUSN9mVgT1ijI1FPMgcffpaPmYbHAVbQmF7bXudrBWHxmLzJlwl5rfep+Zgjli2e67lwUqQ== dependencies: - "@microsoft/applicationinsights-shims" "3.0.1" - "@microsoft/dynamicproto-js" "^2.0.2" - "@nevware21/ts-async" ">= 0.2.4 < 2.x" - "@nevware21/ts-utils" ">= 0.9.5 < 2.x" + "@microsoft/applicationinsights-shims" "2.0.2" + "@microsoft/dynamicproto-js" "^1.1.9" -"@microsoft/applicationinsights-shims@3.0.1", "@microsoft/applicationinsights-shims@^3.0.1": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz#3865b73ace8405b9c4618cc5c571f2fe3876f06f" - integrity sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg== - dependencies: - "@nevware21/ts-utils" ">= 0.9.4 < 2.x" - -"@microsoft/dynamicproto-js@^2.0.2": +"@microsoft/applicationinsights-shims@2.0.2", "@microsoft/applicationinsights-shims@^2.0.2": version "2.0.2" - resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz#e57fbec2e7067d48b7e8e1e1c1d354028ef718a6" - integrity sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg== - dependencies: - "@nevware21/ts-utils" ">= 0.9.4 < 2.x" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-2.0.2.tgz#92b36a09375e2d9cb2b4203383b05772be837085" + integrity sha512-PoHEgsnmcqruLNHZ/amACqdJ6YYQpED0KSRe6J7gIJTtpZC1FfFU9b1fmDKDKtFoUSrPzEh1qzO3kmRZP0betg== -"@nevware21/ts-async@>= 0.2.4 < 2.x": - version "0.2.6" - resolved "https://registry.yarnpkg.com/@nevware21/ts-async/-/ts-async-0.2.6.tgz#9fad1c4a56b01430b407d454817f68bb6ace92b0" - integrity sha512-NCUqEZSbsy7LVtKlUScd/eTst6djkWauLlzoIPVKCOxalEBdO8lrgNRIm4Xy68JNudNN5faqa2WA12X8m0BVhA== - dependencies: - "@nevware21/ts-utils" ">= 0.9.7 < 2.x" - -"@nevware21/ts-utils@>= 0.9.4 < 2.x", "@nevware21/ts-utils@>= 0.9.5 < 2.x", "@nevware21/ts-utils@>= 0.9.6 < 2.x", "@nevware21/ts-utils@>= 0.9.7 < 2.x": - version "0.9.8" - resolved "https://registry.yarnpkg.com/@nevware21/ts-utils/-/ts-utils-0.9.8.tgz#6db23211a5c7539d3b4362e273087effcfc7d2f8" - integrity sha512-kZ8s8hcn9jPVX/M7kSsBYrOGlHjqLahmxrG7QeKTk5paeVwfgKdvVCjj5Acb4UGb/ukU1G34U1Z3eb7bbVanyA== +"@microsoft/dynamicproto-js@^1.1.7", "@microsoft/dynamicproto-js@^1.1.9": + version "1.1.9" + resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.9.tgz#7437db7aa061162ee94e4131b69a62b8dad5dea6" + integrity sha512-n1VPsljTSkthsAFYdiWfC+DKzK2WwcRp83Y1YAqdX552BstvsDjft9YXppjUzp11BPsapDoO1LDgrDB0XVsfNQ== "@parcel/watcher@2.1.0": version "2.1.0" diff --git a/yarn.lock b/yarn.lock index 93c5050827f..88f1223f580 100644 --- a/yarn.lock +++ b/yarn.lock @@ -474,51 +474,41 @@ methods "^1.1.2" path-to-regexp "^6.2.1" -"@microsoft/1ds-core-js@4.0.1", "@microsoft/1ds-core-js@^4.0.1": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-4.0.1.tgz#6fcf129276c933934d618f7fc3de49613259e3ec" - integrity sha512-BcTKhcRixCqLYEo1xVlQofvvZpgey9qmkzxQd1j9bBZS4iQl9IOmGBzlf/vGSITC7VXtEk2NZOlHQN0llAIfSw== +"@microsoft/1ds-core-js@3.2.13", "@microsoft/1ds-core-js@^3.2.13": + version "3.2.13" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.13.tgz#0c105ed75091bae3f1555c0334704fa9911c58fb" + integrity sha512-CluYTRWcEk0ObG5EWFNWhs87e2qchJUn0p2D21ZUa3PWojPZfPSBs4//WIE0MYV8Qg1Hdif2ZTwlM7TbYUjfAg== dependencies: - "@microsoft/applicationinsights-core-js" "3.0.2" - "@microsoft/applicationinsights-shims" "^3.0.1" - "@microsoft/dynamicproto-js" "^2.0.2" - "@nevware21/ts-async" ">= 0.2.4 < 2.x" - "@nevware21/ts-utils" ">= 0.9.6 < 2.x" + "@microsoft/applicationinsights-core-js" "2.8.15" + "@microsoft/applicationinsights-shims" "^2.0.2" + "@microsoft/dynamicproto-js" "^1.1.7" -"@microsoft/1ds-post-js@^4.0.1": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-4.0.1.tgz#8630927d10518067686104baec98557b74317e4e" - integrity sha512-uyYkG9mxzzj/4HgxVJsgcz9abfakW+Rjrsnor/hXS5I5ezE3YUX+54DLvAh/c6mEgoDI7u8wGkGl3HgCtAYbDQ== +"@microsoft/1ds-post-js@^3.2.13": + version "3.2.13" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.13.tgz#560aacac8a92fdbb79e8c2ebcb293d56e19f51aa" + integrity sha512-HgS574fdD19Bo2vPguyznL4eDw7Pcm1cVNpvbvBLWiW3x4e1FCQ3VMXChWnAxCae8Hb0XqlA2sz332ZobBavTA== dependencies: - "@microsoft/1ds-core-js" "4.0.1" - "@microsoft/applicationinsights-shims" "^3.0.1" - "@microsoft/dynamicproto-js" "^2.0.2" - "@nevware21/ts-async" ">= 0.2.4 < 2.x" - "@nevware21/ts-utils" ">= 0.9.6 < 2.x" + "@microsoft/1ds-core-js" "3.2.13" + "@microsoft/applicationinsights-shims" "^2.0.2" + "@microsoft/dynamicproto-js" "^1.1.7" -"@microsoft/applicationinsights-core-js@3.0.2": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.2.tgz#108e20df8c162bec92b1f66f9de2530a25d9f51a" - integrity sha512-WQhVhzlRlLDrQzn3OShCW/pL3BW5WC57t0oywSknX3q7lMzI3jDg7Ihh0iuIcNTzGCTbDkuqr4d6IjEDWIMtJQ== +"@microsoft/applicationinsights-core-js@2.8.15": + version "2.8.15" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.15.tgz#8fa466474260e01967fe649f14dd9e5ff91dcdc8" + integrity sha512-yYAs9MyjGr2YijQdUSN9mVgT1ijI1FPMgcffpaPmYbHAVbQmF7bXudrBWHxmLzJlwl5rfep+Zgjli2e67lwUqQ== dependencies: - "@microsoft/applicationinsights-shims" "3.0.1" - "@microsoft/dynamicproto-js" "^2.0.2" - "@nevware21/ts-async" ">= 0.2.4 < 2.x" - "@nevware21/ts-utils" ">= 0.9.5 < 2.x" + "@microsoft/applicationinsights-shims" "2.0.2" + "@microsoft/dynamicproto-js" "^1.1.9" -"@microsoft/applicationinsights-shims@3.0.1", "@microsoft/applicationinsights-shims@^3.0.1": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz#3865b73ace8405b9c4618cc5c571f2fe3876f06f" - integrity sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg== - dependencies: - "@nevware21/ts-utils" ">= 0.9.4 < 2.x" - -"@microsoft/dynamicproto-js@^2.0.2": +"@microsoft/applicationinsights-shims@2.0.2", "@microsoft/applicationinsights-shims@^2.0.2": version "2.0.2" - resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz#e57fbec2e7067d48b7e8e1e1c1d354028ef718a6" - integrity sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg== - dependencies: - "@nevware21/ts-utils" ">= 0.9.4 < 2.x" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-2.0.2.tgz#92b36a09375e2d9cb2b4203383b05772be837085" + integrity sha512-PoHEgsnmcqruLNHZ/amACqdJ6YYQpED0KSRe6J7gIJTtpZC1FfFU9b1fmDKDKtFoUSrPzEh1qzO3kmRZP0betg== + +"@microsoft/dynamicproto-js@^1.1.7", "@microsoft/dynamicproto-js@^1.1.9": + version "1.1.9" + resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.9.tgz#7437db7aa061162ee94e4131b69a62b8dad5dea6" + integrity sha512-n1VPsljTSkthsAFYdiWfC+DKzK2WwcRp83Y1YAqdX552BstvsDjft9YXppjUzp11BPsapDoO1LDgrDB0XVsfNQ== "@mole-inc/bin-wrapper@^8.0.1": version "8.0.1" @@ -534,18 +524,6 @@ got "^11.8.5" os-filter-obj "^2.0.0" -"@nevware21/ts-async@>= 0.2.4 < 2.x": - version "0.2.6" - resolved "https://registry.yarnpkg.com/@nevware21/ts-async/-/ts-async-0.2.6.tgz#9fad1c4a56b01430b407d454817f68bb6ace92b0" - integrity sha512-NCUqEZSbsy7LVtKlUScd/eTst6djkWauLlzoIPVKCOxalEBdO8lrgNRIm4Xy68JNudNN5faqa2WA12X8m0BVhA== - dependencies: - "@nevware21/ts-utils" ">= 0.9.7 < 2.x" - -"@nevware21/ts-utils@>= 0.9.4 < 2.x", "@nevware21/ts-utils@>= 0.9.5 < 2.x", "@nevware21/ts-utils@>= 0.9.6 < 2.x", "@nevware21/ts-utils@>= 0.9.7 < 2.x": - version "0.9.8" - resolved "https://registry.yarnpkg.com/@nevware21/ts-utils/-/ts-utils-0.9.8.tgz#6db23211a5c7539d3b4362e273087effcfc7d2f8" - integrity sha512-kZ8s8hcn9jPVX/M7kSsBYrOGlHjqLahmxrG7QeKTk5paeVwfgKdvVCjj5Acb4UGb/ukU1G34U1Z3eb7bbVanyA== - "@nodelib/fs.scandir@2.1.3": version "2.1.3" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b" From bd869eb492d7a85abd65862ccd83551690ce1d61 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 15 Aug 2023 14:44:23 -0700 Subject: [PATCH 053/223] testing: add `testResultOutdated` context key (#190522) This can be used in the snapshot scenario to only show the "apply snapshot" button when the result is not outdated (marking the original failing result as outdated once its snapshot is updated). Also cleans up the `InspectSubject` types a little bit. --- .../testing/browser/testingOutputPeek.ts | 175 +++++++++++------- .../testing/common/testingContextKeys.ts | 6 +- 2 files changed, 114 insertions(+), 67 deletions(-) diff --git a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts index 5043900e175..047d8486c0a 100644 --- a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts +++ b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts @@ -121,12 +121,12 @@ class MessageSubject { }; } - constructor(public readonly resultId: string, test: TestResultItem, public readonly taskIndex: number, public readonly messageIndex: number) { + constructor(public readonly result: ITestResult, test: TestResultItem, public readonly taskIndex: number, public readonly messageIndex: number) { this.test = test.item; const messages = test.tasks[taskIndex].messages; this.messageIndex = messageIndex; - const parts = { messageIndex, resultId, taskIndex, testExtId: test.item.extId }; + const parts = { messageIndex, resultId: result.id, taskIndex, testExtId: test.item.extId }; this.expectedUri = buildTestUri({ ...parts, type: TestUriType.ResultExpectedOutput }); this.actualUri = buildTestUri({ ...parts, type: TestUriType.ResultActualOutput }); this.messageUri = buildTestUri({ ...parts, type: TestUriType.ResultMessage }); @@ -140,30 +140,27 @@ class TaskSubject { public readonly outputUri: URI; public readonly revealLocation: undefined; - constructor(public readonly resultId: string, public readonly taskIndex: number) { - this.outputUri = buildTestUri({ resultId, taskIndex, type: TestUriType.TaskOutput }); + constructor(public readonly result: ITestResult, public readonly taskIndex: number) { + this.outputUri = buildTestUri({ resultId: result.id, taskIndex, type: TestUriType.TaskOutput }); } } class TestOutputSubject { + public readonly outputUri: URI; public readonly revealLocation: undefined; - public get outputUri() { - return buildTestUri({ resultId: this.resultId, taskIndex: this.taskIndex, testExtId: this.test.item.extId, type: TestUriType.TestOutput }); - } - - constructor(public readonly resultId: string, public readonly taskIndex: number, public readonly task: ITestRunTask, public readonly test: TestResultItem) { + constructor(public readonly result: ITestResult, public readonly taskIndex: number, public readonly task: ITestRunTask, public readonly test: TestResultItem) { + this.outputUri = buildTestUri({ resultId: this.result.id, taskIndex: this.taskIndex, testExtId: this.test.item.extId, type: TestUriType.TestOutput }); } } type InspectSubject = MessageSubject | TaskSubject | TestOutputSubject; -const equalsSubject = (a: InspectSubject, b: InspectSubject) => - a.resultId === b.resultId && a.taskIndex === b.taskIndex && ( - (a instanceof MessageSubject && b instanceof MessageSubject && a.message === b.message) || - (a instanceof TaskSubject && b instanceof TaskSubject) || - (a instanceof TestOutputSubject && b instanceof TestOutputSubject && a.test === b.test) - ); +const equalsSubject = (a: InspectSubject, b: InspectSubject) => ( + (a instanceof MessageSubject && b instanceof MessageSubject && a.message === b.message) || + (a instanceof TaskSubject && b instanceof TaskSubject && a.result === b.result && a.taskIndex === b.taskIndex) || + (a instanceof TestOutputSubject && b instanceof TestOutputSubject && a.test === b.test && a.taskIndex === b.taskIndex) +); /** Iterates through every message in every result */ function* allMessages(results: readonly ITestResult[]) { @@ -626,7 +623,7 @@ export class TestingOutputPeekController extends Disposable implements IEditorCo let found = false; for (const { messageIndex, taskIndex, result, test } of allMessages(this.testResults.results)) { - if (subject instanceof TaskSubject && result.id === subject.resultId) { + if (subject instanceof TaskSubject && result.id === subject.result.id) { found = true; // open the first message found in the current result } @@ -641,11 +638,11 @@ export class TestingOutputPeekController extends Disposable implements IEditorCo return; } - if (subject instanceof TestOutputSubject && subject.test.item.extId === test.item.extId && subject.taskIndex === taskIndex && subject.resultId === result.id) { + if (subject instanceof TestOutputSubject && subject.test.item.extId === test.item.extId && subject.taskIndex === taskIndex && subject.result.id === result.id) { found = true; } - if (subject instanceof MessageSubject && subject.test.extId === test.item.extId && subject.messageIndex === messageIndex && subject.taskIndex === taskIndex && subject.resultId === result.id) { + if (subject instanceof MessageSubject && subject.test.extId === test.item.extId && subject.messageIndex === messageIndex && subject.taskIndex === taskIndex && subject.result.id === result.id) { found = true; } } @@ -663,20 +660,20 @@ export class TestingOutputPeekController extends Disposable implements IEditorCo let previous: { messageIndex: number; taskIndex: number; result: ITestResult; test: TestResultItem } | undefined; for (const m of allMessages(this.testResults.results)) { if (subject instanceof TaskSubject) { - if (m.result.id === subject.resultId) { + if (m.result.id === subject.result.id) { break; } continue; } if (subject instanceof TestOutputSubject) { - if (m.test.item.extId === subject.test.item.extId && m.result.id === subject.resultId && m.taskIndex === subject.taskIndex) { + if (m.test.item.extId === subject.test.item.extId && m.result.id === subject.result.id && m.taskIndex === subject.taskIndex) { break; } continue; } - if (subject.test.extId === m.test.item.extId && subject.messageIndex === m.messageIndex && subject.taskIndex === m.taskIndex && subject.resultId === m.result.id) { + if (subject.test.extId === m.test.item.extId && subject.messageIndex === m.messageIndex && subject.taskIndex === m.taskIndex && subject.result.id === m.result.id) { break; } @@ -732,25 +729,29 @@ export class TestingOutputPeekController extends Disposable implements IEditorCo return undefined; } + const result = this.testResults.results.find(r => r.id === parts.resultId); + if (!result) { + return; + } + if (parts.type === TestUriType.TaskOutput) { - return new TaskSubject(parts.resultId, parts.taskIndex); + return new TaskSubject(result, parts.taskIndex); } if (parts.type === TestUriType.TestOutput) { - const result = this.testResults.results.find(r => r.id === parts.resultId); - const test = result?.getStateById(parts.testExtId); - const task = result?.tasks[parts.taskIndex]; + const test = result.getStateById(parts.testExtId); + const task = result.tasks[parts.taskIndex]; if (!test || !task) { return; } - return new TestOutputSubject(parts.resultId, parts.taskIndex, task, test); + return new TestOutputSubject(result, parts.taskIndex, task, test); } - const { resultId, testExtId, taskIndex, messageIndex } = parts; - const test = this.testResults.getResult(parts.resultId)?.getStateById(testExtId); + const { testExtId, taskIndex, messageIndex } = parts; + const test = result?.getStateById(testExtId); if (!test || !test.tasks[parts.taskIndex]) { return; } - return new MessageSubject(resultId, test, taskIndex, messageIndex); + return new MessageSubject(result, test, taskIndex, messageIndex); } } @@ -758,7 +759,11 @@ class TestResultsViewContent extends Disposable { private static lastSplitWidth?: number; private readonly didReveal = this._register(new Emitter<{ subject: InspectSubject; preserveFocus: boolean }>()); - private readonly clickMenu = this._register(new MutableDisposable()); + private readonly currentSubjectStore = this._register(new DisposableStore()); + private messageContextKeyService!: IContextKeyService; + private contextKeyTestMessage!: IContextKey; + private contextKeyResultOutdated!: IContextKey; + private dimension?: dom.Dimension; private splitView!: SplitView; private messageContainer!: HTMLElement; @@ -797,6 +802,10 @@ class TestResultsViewContent extends Disposable { this._register(this.instantiationService.createInstance(PlainTextMessagePeek, this.editor, messageContainer)), ]; + this.messageContextKeyService = this._register(this.contextKeyService.createScoped(this.messageContainer)); + this.contextKeyTestMessage = TestingContextKeys.testMessageContext.bindTo(this.messageContextKeyService); + this.contextKeyResultOutdated = TestingContextKeys.testResultOutdated.bindTo(this.messageContextKeyService); + const treeContainer = dom.append(containerElement, dom.$('.test-output-peek-tree')); const tree = this._register(this.instantiationService.createInstance( OutputPeekTree, @@ -860,21 +869,45 @@ class TestResultsViewContent extends Disposable { return this.contentProvidersUpdateLimiter.queue(async () => { await Promise.all(this.contentProviders.map(p => p.update(opts.subject))); - if (opts.subject instanceof MessageSubject) { - const contextOverlay = this.contextKeyService.createOverlay([[TestingContextKeys.testMessageContext.key, opts.subject.contextValue]]); - this.clickMenu.value = this.instantiationService - .createChild(new ServiceCollection([IContextKeyService, contextOverlay])) - .createInstance(FloatingClickMenu, { - container: this.messageContainer, - menuId: MenuId.TestMessageContent, - getActionArg: () => (opts.subject as MessageSubject).context, - }); - } else { - this.clickMenu.clear(); - } + this.currentSubjectStore.clear(); + this.populateFloatingClick(opts.subject); }); } + private populateFloatingClick(subject: InspectSubject) { + if (!(subject instanceof MessageSubject)) { + return; + } + + this.currentSubjectStore.add(toDisposable(() => { + this.contextKeyResultOutdated.reset(); + this.contextKeyTestMessage.reset(); + })); + + this.contextKeyTestMessage.set(subject.contextValue || ''); + if (subject.result instanceof LiveTestResult) { + this.contextKeyResultOutdated.set(subject.result.getStateById(subject.test.extId)?.retired ?? false); + this.currentSubjectStore.add(subject.result.onChange(ev => { + if (ev.item.item.extId === subject.test.extId) { + this.contextKeyResultOutdated.set(ev.item.retired ?? false); + } + })); + } else { + this.contextKeyResultOutdated.set(true); + } + + + this.currentSubjectStore.add( + this.instantiationService + .createChild(new ServiceCollection([IContextKeyService, this.messageContextKeyService])) + .createInstance(FloatingClickMenu, { + container: this.messageContainer, + menuId: MenuId.TestMessageContent, + getActionArg: () => (subject as MessageSubject).context, + }) + ); + } + public onLayoutBody(height: number, width: number) { this.dimension = new dom.Dimension(width, height); this.splitView.layout(width); @@ -1049,10 +1082,7 @@ export class TestResultsView extends ViewPane { return; } - this.content.reveal({ - preserveFocus, - subject: new TaskSubject(result.id, 0), - }); + this.content.reveal({ preserveFocus, subject: new TaskSubject(result, 0) }); } protected override renderBody(container: HTMLElement): void { @@ -1062,7 +1092,7 @@ export class TestResultsView extends ViewPane { const [lastResult] = this.resultService.results; if (lastResult && lastResult.tasks.length) { - this.content.reveal({ preserveFocus: true, subject: new TaskSubject(lastResult.id, 0) }); + this.content.reveal({ preserveFocus: true, subject: new TaskSubject(lastResult, 0) }); } } @@ -1329,7 +1359,6 @@ class TerminalMessagePeek extends Disposable implements IPeekOutputRenderer { constructor( private readonly container: HTMLElement, private readonly isInPeekView: boolean, - @ITestResultService private readonly resultService: ITestResultService, @ITerminalService private readonly terminalService: ITerminalService, @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, @IWorkspaceContextService private readonly workspaceContext: IWorkspaceContextService, @@ -1445,11 +1474,7 @@ class TerminalMessagePeek extends Disposable implements IPeekOutputRenderer { doInitialWrite: (target: T, result: LiveTestResult) => Iterable; doListenForMoreData: (target: T, result: LiveTestResult, terminal: IDetachedXtermTerminal) => IDisposable | undefined; }) { - const result = this.resultService.getResult(opts.subject.resultId); - if (!result) { - return this.clear(); - } - + const result = opts.subject.result; const target = opts.getTarget(result); if (!target) { return this.clear(); @@ -1639,13 +1664,19 @@ class TestResultElement implements ITreeElement { } class TestCaseElement implements ITreeElement { - public readonly changeEmitter = new Emitter(); - public readonly onDidChange = this.changeEmitter.event; public readonly type = 'test'; public readonly context = this.test.item.extId; public readonly id = `${this.results.id}/${this.test.item.extId}`; public readonly description?: string; + public get onDidChange() { + if (!(this.results instanceof LiveTestResult)) { + return Event.None; + } + + return Event.filter(this.results.onChange, e => e.item.item.extId === this.test.item.extId); + } + public get state() { return this.test.tasks[this.taskIndex].state; } @@ -1663,7 +1694,7 @@ class TestCaseElement implements ITreeElement { } public get outputSubject() { - return new TestOutputSubject(this.results.id, this.taskIndex, this.task, this.test); + return new TestOutputSubject(this.results, this.taskIndex, this.task, this.test); } constructor( @@ -1710,10 +1741,18 @@ class TestMessageElement implements ITreeElement { public readonly uri: URI; public readonly location?: IRichLocation; public readonly description?: string; - public readonly onDidChange = Event.None; public readonly contextValue?: string; public readonly message: ITestMessage; + public get onDidChange() { + if (!(this.result instanceof LiveTestResult)) { + return Event.None; + } + + // rerender when the test case changes so it gets retired events + return Event.filter(this.result.onChange, e => e.item.item.extId === this.test.item.extId); + } + public get context(): ITestMessageMenuArgs { return { $mid: MarshalledId.TestMessageMenuArgs, @@ -1874,7 +1913,7 @@ class OutputPeekTree extends Disposable { const disposable = new DisposableStore(); disposable.add(result.onNewTask(() => { if (result.tasks.length === 1) { - this.requestReveal.fire(new TaskSubject(result.id, 0)); // reveal the first task in new runs + this.requestReveal.fire(new TaskSubject(result, 0)); // reveal the first task in new runs } if (this.tree.hasElement(resultNode)) { @@ -1898,7 +1937,6 @@ class OutputPeekTree extends Disposable { if (e.reason === TestResultItemChangeReason.NewMessage && e.message.type === TestMessageType.Error) { this.tree.setChildren(itemNode, getTestChildren(result, e.item, index), { diffIdentityProvider }); } - itemNode.changeEmitter.fire(); return; } @@ -1954,10 +1992,10 @@ class OutputPeekTree extends Disposable { if (subject instanceof TaskSubject) { const resultItem = this.tree.getNode(null).children.find(c => { if (c.element instanceof TaskElement) { - return c.element.results.id === subject.resultId && c.element.index === subject.taskIndex; + return c.element.results.id === subject.result.id && c.element.index === subject.taskIndex; } if (c.element instanceof TestResultElement) { - return c.element.id === subject.resultId; + return c.element.id === subject.result.id; } return false; }); @@ -1993,7 +2031,7 @@ class OutputPeekTree extends Disposable { this._register(this.tree.onDidOpen(async e => { if (e.element instanceof TestMessageElement) { - this.requestReveal.fire(new MessageSubject(e.element.result.id, e.element.test, e.element.taskIndex, e.element.messageIndex)); + this.requestReveal.fire(new MessageSubject(e.element.result, e.element.test, e.element.taskIndex, e.element.messageIndex)); } })); @@ -2164,7 +2202,7 @@ class TreeActionsProvider { localize('testing.showResultOutput', "Show Result Output"), ThemeIcon.asClassName(Codicon.terminal), undefined, - () => this.requestReveal.fire(new TaskSubject(element.results.id, element.index)), + () => this.requestReveal.fire(new TaskSubject(element.results, element.index)), )); } @@ -2176,7 +2214,7 @@ class TreeActionsProvider { localize('testing.showResultOutput', "Show Result Output"), ThemeIcon.asClassName(Codicon.terminal), undefined, - () => this.requestReveal.fire(new TaskSubject(element.value.id, 0)), + () => this.requestReveal.fire(new TaskSubject(element.value, 0)), )); } @@ -2199,10 +2237,15 @@ class TreeActionsProvider { } } + if (element instanceof TestCaseElement || element instanceof TestMessageElement) { + contextKeys.push( + [TestingContextKeys.testResultOutdated.key, element.test.retired], + ...getTestItemContextOverlay(element.test, capabilities), + ); + } + if (element instanceof TestCaseElement) { const extId = element.test.item.extId; - contextKeys.push(...getTestItemContextOverlay(element.test, capabilities)); - primary.push(new Action( 'testing.outputPeek.goToFile', localize('testing.goToFile', "Go to Source"), diff --git a/src/vs/workbench/contrib/testing/common/testingContextKeys.ts b/src/vs/workbench/contrib/testing/common/testingContextKeys.ts index fd8c90d0ce6..eccdf3fc5a0 100644 --- a/src/vs/workbench/contrib/testing/common/testingContextKeys.ts +++ b/src/vs/workbench/contrib/testing/common/testingContextKeys.ts @@ -59,7 +59,11 @@ export namespace TestingContextKeys { description: localize('testing.testItemIsHidden', 'Boolean indicating whether the test item is hidden') }); export const testMessageContext = new RawContextKey('testMessage', undefined, { - type: 'boolean', + type: 'string', description: localize('testing.testMessage', 'Value set in `testMessage.contextValue`, available in editor/content and testing/message/context') }); + export const testResultOutdated = new RawContextKey('testResultOutdated', undefined, { + type: 'boolean', + description: localize('testing.testResultOutdated', 'Value available in editor/content and testing/message/context when the result is outdated') + }); } From b40e0444212b8e75679e817f10cc27418436d6ca Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 15 Aug 2023 14:44:44 -0700 Subject: [PATCH 054/223] fix build failure (#190523) Didn't realize we minified private identifiers when running unit tests too --- src/vs/base/test/common/snapshot.ts | 2 +- src/vs/base/test/node/snapshot.test.ts | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/vs/base/test/common/snapshot.ts b/src/vs/base/test/common/snapshot.ts index f1104cf65dc..72bbfb8b593 100644 --- a/src/vs/base/test/common/snapshot.ts +++ b/src/vs/base/test/common/snapshot.ts @@ -31,8 +31,8 @@ export interface ISnapshotOptions { */ export class SnapshotContext { private nextIndex = 0; + protected snapshotsDir: URI; private readonly namePrefix: string; - private readonly snapshotsDir: URI; private readonly usedNames = new Set(); constructor(private readonly test: Mocha.Test | undefined) { diff --git a/src/vs/base/test/node/snapshot.test.ts b/src/vs/base/test/node/snapshot.test.ts index 0f36e39a91d..6c12ae70dd3 100644 --- a/src/vs/base/test/node/snapshot.test.ts +++ b/src/vs/base/test/node/snapshot.test.ts @@ -29,9 +29,12 @@ suite('snapshot', () => { }); const makeContext = (test: Partial | undefined) => { - const ctx = new SnapshotContext(test as Mocha.Test); - (ctx as any as { snapshotsDir: URI }).snapshotsDir = URI.file(testDir); - return ctx; + return new class extends SnapshotContext { + constructor() { + super(test as Mocha.Test); + this.snapshotsDir = URI.file(testDir); + } + }; }; const snapshotFileTree = async () => { From 39b7db410aa21065e4c711aa6eb8814fc1f4dfda Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Tue, 15 Aug 2023 14:56:00 -0700 Subject: [PATCH 055/223] Update src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com> --- .../browser/terminal.accessibility.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts index 6d127d0cb16..20ad723f4cf 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts @@ -42,7 +42,7 @@ class TextAreaSyncContribution extends DisposableStore implements ITerminalContr } xtermReady(xterm: IXtermTerminal & { raw: Terminal }): void { const addon = this._instantiationService.createInstance(TextAreaSyncAddon, this._instance.capabilities); - this._xterm?.raw.loadAddon(addon); + xterm.raw.loadAddon(addon); addon.activate(xterm.raw); } } From e98311981b5218778d1175faea47388ee22012c0 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 15 Aug 2023 15:11:35 -0700 Subject: [PATCH 056/223] add private update dev mode function --- src/tsconfig.monaco.json | 3 ++- .../browser/terminal.accessibility.contribution.ts | 1 - .../browser/terminal.developer.contribution.ts | 13 +++++++------ 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/tsconfig.monaco.json b/src/tsconfig.monaco.json index 256f406fb6c..f089be5f60f 100644 --- a/src/tsconfig.monaco.json +++ b/src/tsconfig.monaco.json @@ -32,6 +32,7 @@ "vs/platform/files/browser/htmlFileSystemProvider.ts", "vs/platform/files/browser/webFileSystemAccess.ts", "vs/platform/telemetry/*", - "vs/platform/assignment/*" + "vs/platform/assignment/*", + "vs/platform/terminal/*" ] } diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts index 20ad723f4cf..b69943c18de 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts @@ -28,7 +28,6 @@ import type { Terminal } from 'xterm'; class TextAreaSyncContribution extends DisposableStore implements ITerminalContribution { static readonly ID = 'terminal.textAreaSync'; - private _xterm: IXtermTerminal & { raw: Terminal } | undefined; static get(instance: ITerminalInstance): TextAreaSyncContribution | null { return instance.getContribution(TextAreaSyncContribution.ID); } diff --git a/src/vs/workbench/contrib/terminalContrib/developer/browser/terminal.developer.contribution.ts b/src/vs/workbench/contrib/terminalContrib/developer/browser/terminal.developer.contribution.ts index e1c4ee3662c..4d828a105ce 100644 --- a/src/vs/workbench/contrib/terminalContrib/developer/browser/terminal.developer.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/developer/browser/terminal.developer.contribution.ts @@ -110,7 +110,6 @@ registerTerminalAction({ class DevModeContribution extends DisposableStore implements ITerminalContribution { static readonly ID = 'terminal.devMode'; - devModeClass = 'dev-mode'; private _xterm: IXtermTerminal & { raw: Terminal } | undefined; static get(instance: ITerminalInstance): DevModeContribution | null { return instance.getContribution(DevModeContribution.ID); @@ -123,16 +122,18 @@ class DevModeContribution extends DisposableStore implements ITerminalContributi super(); this.add(this._configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(TerminalSettingId.DevMode)) { - const devMode: boolean = this._configurationService.getValue(TerminalSettingId.DevMode) || false; - this._xterm?.raw.element?.classList.toggle(this.devModeClass, devMode); + this._updateDevMode(); } })); } xtermReady(xterm: IXtermTerminal & { raw: Terminal }): void { this._xterm = xterm; - if (this._configurationService.getValue(TerminalSettingId.DevMode)) { - this._xterm.raw.element?.classList.add(this.devModeClass); - } + this._updateDevMode(); + } + + private _updateDevMode() { + const devMode: boolean = this._configurationService.getValue(TerminalSettingId.DevMode) || false; + this._xterm?.raw.element?.classList.toggle('dev-mode', devMode); } } registerTerminalContribution(DevModeContribution.ID, DevModeContribution); From 8693dc991df836c5a5cacf188033b38fe3ece38b Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 15 Aug 2023 15:15:51 -0700 Subject: [PATCH 057/223] more explicitly exclude --- src/tsconfig.monaco.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tsconfig.monaco.json b/src/tsconfig.monaco.json index f089be5f60f..39a5f911fb1 100644 --- a/src/tsconfig.monaco.json +++ b/src/tsconfig.monaco.json @@ -33,6 +33,6 @@ "vs/platform/files/browser/webFileSystemAccess.ts", "vs/platform/telemetry/*", "vs/platform/assignment/*", - "vs/platform/terminal/*" + "vs/platform/terminal/common/capabilities/" ] } From 1a35da805f0744ce8e88dd6471e6b172da721ccf Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 15 Aug 2023 15:58:44 -0700 Subject: [PATCH 058/223] fix #186307 --- .../browser/inlineCompletionsController.ts | 24 +++++++++- .../browser/accessibility.contribution.ts | 3 +- .../browser/accessibilityConfiguration.ts | 5 +++ .../browser/accessibilityContributions.ts | 44 +++++++++++++++++++ .../accessibility/browser/accessibleView.ts | 2 +- 5 files changed, 75 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts index 61787b29ac8..6d2d4aa8460 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { alert } from 'vs/base/browser/ui/aria/aria'; +import { alert, status } from 'vs/base/browser/ui/aria/aria'; import { Event } from 'vs/base/common/event'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { ITransaction, autorun, constObservable, disposableObservableValue, observableFromEvent, observableValue, transaction } from 'vs/base/common/observable'; @@ -22,11 +22,13 @@ import { InlineCompletionContextKeys } from 'vs/editor/contrib/inlineCompletions import { InlineCompletionsHintsWidget, InlineSuggestionHintsContentWidget } from 'vs/editor/contrib/inlineCompletions/browser/inlineCompletionsHintsWidget'; import { InlineCompletionsModel, VersionIdChangeReason } from 'vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel'; import { SuggestWidgetAdaptor } from 'vs/editor/contrib/inlineCompletions/browser/suggestWidgetInlineCompletionProvider'; +import { localize } from 'vs/nls'; import { AudioCue, IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; export class InlineCompletionsController extends Disposable { static ID = 'editor.contrib.inlineCompletionsController'; @@ -73,6 +75,7 @@ export class InlineCompletionsController extends Disposable { @ILanguageFeatureDebounceService private readonly debounceService: ILanguageFeatureDebounceService, @ILanguageFeaturesService private readonly languageFeaturesService: ILanguageFeaturesService, @IAudioCueService private readonly audioCueService: IAudioCueService, + @IKeybindingService private readonly _keybindingService: IKeybindingService ) { super(); @@ -191,6 +194,7 @@ export class InlineCompletionsController extends Disposable { this.audioCueService.playAudioCue(AudioCue.inlineSuggestion).then(() => { if (this.editor.getOption(EditorOption.screenReaderAnnounceInlineSuggestion)) { alert(state.ghostText.renderForScreenReader(lineText)); + this.provideScreenReaderHint(); } }); } @@ -199,6 +203,24 @@ export class InlineCompletionsController extends Disposable { this._register(new InlineCompletionsHintsWidget(this.editor, this.model, this.instantiationService)); } + private provideScreenReaderHint(): void { + const showHoverKeybinding = this._keybindingService.lookupKeybinding('editor.action.showHover'); + const accessibleViewKeybinding = this._keybindingService.lookupKeybinding('editor.action.accessibleView'); + if (this.configurationService.getValue('accessibility.verbosity.inlineCompletions')) { + let hint: string | undefined; + if (showHoverKeybinding && accessibleViewKeybinding) { + hint = localize('showBothHints', "View more actions ({0}) or inspect this in the accessible view ({1})", showHoverKeybinding.getAriaLabel(), accessibleViewKeybinding.getAriaLabel()); + } else if (showHoverKeybinding) { + hint = localize('showHoverHint', "View more actions ({0})", showHoverKeybinding.getAriaLabel()); + } else if (accessibleViewKeybinding) { + hint = localize('showAccessibleViewHint', "Inspect this in the accessible view ({0})", accessibleViewKeybinding.getAriaLabel()); + } + if (hint) { + status(hint); + } + } + } + /** * Copies over the relevant state from the text model to observables. * This solves all kind of eventing issues, as we make sure we always operate on the latest state, diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibility.contribution.ts b/src/vs/workbench/contrib/accessibility/browser/accessibility.contribution.ts index 81deacd41ea..4725fbf0d9d 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibility.contribution.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibility.contribution.ts @@ -10,7 +10,7 @@ import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle import { Registry } from 'vs/platform/registry/common/platform'; import { IAccessibleViewService, AccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; import { UnfocusedViewDimmingContribution } from 'vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution'; -import { EditorAccessibilityHelpContribution, HoverAccessibleViewContribution, NotificationAccessibleViewContribution } from 'vs/workbench/contrib/accessibility/browser/accessibilityContributions'; +import { EditorAccessibilityHelpContribution, HoverAccessibleViewContribution, InlineCompletionsAccessibleViewContribution, NotificationAccessibleViewContribution } from 'vs/workbench/contrib/accessibility/browser/accessibilityContributions'; registerAccessibilityConfiguration(); registerSingleton(IAccessibleViewService, AccessibleViewService, InstantiationType.Delayed); @@ -22,3 +22,4 @@ workbenchRegistry.registerWorkbenchContribution(UnfocusedViewDimmingContribution const workbenchContributionsRegistry = Registry.as(WorkbenchExtensions.Workbench); workbenchContributionsRegistry.registerWorkbenchContribution(HoverAccessibleViewContribution, LifecyclePhase.Eventually); workbenchContributionsRegistry.registerWorkbenchContribution(NotificationAccessibleViewContribution, LifecyclePhase.Eventually); +workbenchContributionsRegistry.registerWorkbenchContribution(InlineCompletionsAccessibleViewContribution, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts index 516a0f8e840..74316d108c3 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts @@ -20,6 +20,7 @@ export const enum AccessibilityVerbositySettingId { DiffEditor = 'accessibility.verbosity.diffEditor', Chat = 'accessibility.verbosity.panelChat', InlineChat = 'accessibility.verbosity.inlineChat', + InlineCompletions = 'accessibility.verbosity.inlineCompletions', KeybindingsEditor = 'accessibility.verbosity.keybindingsEditor', Notebook = 'accessibility.verbosity.notebook', Editor = 'accessibility.verbosity.editor', @@ -55,6 +56,10 @@ const configuration: IConfigurationNode = { description: localize('verbosity.interactiveEditor.description', 'Provide information about how to access the inline editor chat accessibility help menu and alert with hints which describe how to use the feature when the input is focused'), ...baseProperty }, + [AccessibilityVerbositySettingId.InlineCompletions]: { + description: localize('verbosity.inlineCompletions.description', 'Provide information about how to access the inline completions hover and accessible view'), + ...baseProperty + }, [AccessibilityVerbositySettingId.KeybindingsEditor]: { description: localize('verbosity.keybindingsEditor.description', 'Provide information about how to change a keybinding in the keybindings editor when a row is focused'), ...baseProperty diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts index 71deb808ff8..78d4993fcdd 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts @@ -35,6 +35,9 @@ import { IAction } from 'vs/base/common/actions'; import { INotificationViewItem } from 'vs/workbench/common/notifications'; import { ThemeIcon } from 'vs/base/common/themables'; import { Codicon } from 'vs/base/common/codicons'; +import { InlineCompletionsController } from 'vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController'; +import { InlineCompletionContextKeys } from 'vs/editor/contrib/inlineCompletions/browser/inlineCompletionContextKeys'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; export class EditorAccessibilityHelpContribution extends Disposable { static ID: 'editorAccessibilityHelpContribution'; @@ -276,3 +279,44 @@ export function alertFocusChange(index: number | undefined, length: number | und return; } +export class InlineCompletionsAccessibleViewContribution extends Disposable { + static ID: 'inlineCompletionsAccessibleViewContribution'; + private _options: IAccessibleViewOptions = { + ariaLabel: localize('inlineCompletionsAccessibleView', "Inline Completions Accessible View"), language: 'typescript', type: AccessibleViewType.View + }; + constructor() { + super(); + this._register(AccessibleViewAction.addImplementation(95, 'inline-completions', accessor => { + const accessibleViewService = accessor.get(IAccessibleViewService); + const codeEditorService = accessor.get(ICodeEditorService); + const editor = codeEditorService.getActiveCodeEditor() || codeEditorService.getFocusedCodeEditor(); + if (!editor) { + return false; + } + const model = InlineCompletionsController.get(editor)?.model.get(); + const state = model?.state.get(); + if (!state) { + return false; + } + const lineText = model?.textModel.getLineContent(state.ghostText.lineNumber); + if (!lineText) { + return false; + } + const content = InlineCompletionsController.get(editor)?.model.get()?.ghostText.get()?.renderForScreenReader(lineText); + if (!content) { + return false; + } + accessibleViewService.show({ + verbositySettingKey: AccessibilityVerbositySettingId.InlineCompletions, + provideContent() { return content; }, + onClose() { + + }, + options: this._options + }); + return true; + }, ContextKeyExpr.and(InlineCompletionContextKeys.inlineSuggestionVisible, EditorContextKeys.focus, EditorContextKeys.hasCodeActionsProvider) + ) + ); + } +} diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index 133b9c6615f..90ca7caa0bb 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -295,7 +295,7 @@ class AccessibleView extends Disposable { } } - const fragment = message + provider.provideContent() + readMoreLink + disableHelpHint + localize('exit-tip', 'Exit this dialog via the Escape key.'); + const fragment = message + provider.provideContent() + readMoreLink + disableHelpHint + localize('exit-tip', '\nExit this dialog via the Escape key.'); this._getTextModel(URI.from({ path: `accessible-view-${provider.verbositySettingKey}`, scheme: 'accessible-view', fragment })).then((model) => { if (!model) { From 02c388ce8896104e1560f009954e93361089f099 Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Tue, 15 Aug 2023 23:01:29 +0000 Subject: [PATCH 059/223] Update scoped environment collection API --- .../api/common/extHostExtensionService.ts | 1 - .../api/common/extHostTerminalService.ts | 17 ++++++++----- ...scode.proposed.envCollectionWorkspace.d.ts | 25 +++++++++++-------- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 34e70b79107..0c35ea254f3 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -532,7 +532,6 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme return that.extensionRuntime; }, get environmentVariableCollection() { return that._extHostTerminalService.getEnvironmentVariableCollection(extensionDescription); }, - getEnvironmentVariableCollection(scope?: vscode.EnvironmentVariableScope) { return that._extHostTerminalService.getEnvironmentVariableCollection(extensionDescription, scope); }, get messagePassingProtocol() { if (!messagePassingProtocol) { if (!messagePort) { diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index d91e8fe990f..01d9c9a9201 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -51,8 +51,13 @@ export interface IExtHostTerminalService extends ExtHostTerminalServiceShape, ID registerLinkProvider(provider: vscode.TerminalLinkProvider): vscode.Disposable; registerProfileProvider(extension: IExtensionDescription, id: string, provider: vscode.TerminalProfileProvider): vscode.Disposable; registerTerminalQuickFixProvider(id: string, extensionId: string, provider: vscode.TerminalQuickFixProvider): vscode.Disposable; - getEnvironmentVariableCollection(extension: IExtensionDescription, scope?: vscode.EnvironmentVariableScope): vscode.EnvironmentVariableCollection; + getEnvironmentVariableCollection(extension: IExtensionDescription): IEnvironmentVariableCollection; } + +interface IEnvironmentVariableCollection extends vscode.EnvironmentVariableCollection { + getScoped(scope: vscode.EnvironmentVariableScope): vscode.EnvironmentVariableCollection; +} + export interface ITerminalInternalOptions { isFeatureTerminal?: boolean; useShellEnvironment?: boolean; @@ -850,13 +855,13 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I return index; } - public getEnvironmentVariableCollection(extension: IExtensionDescription, scope?: vscode.EnvironmentVariableScope): vscode.EnvironmentVariableCollection { + public getEnvironmentVariableCollection(extension: IExtensionDescription): IEnvironmentVariableCollection { let collection = this._environmentVariableCollections.get(extension.identifier.value); if (!collection) { collection = new UnifiedEnvironmentVariableCollection(extension); this._setEnvironmentVariableCollection(extension.identifier.value, collection); } - return collection.getScopedEnvironmentVariableCollection(scope); + return collection.getScopedEnvironmentVariableCollection(undefined); } private _syncEnvironmentVariableCollection(extensionIdentifier: string, collection: UnifiedEnvironmentVariableCollection): void { @@ -923,7 +928,7 @@ class UnifiedEnvironmentVariableCollection { this.map = new Map(serialized); } - getScopedEnvironmentVariableCollection(scope: vscode.EnvironmentVariableScope | undefined): vscode.EnvironmentVariableCollection { + getScopedEnvironmentVariableCollection(scope: vscode.EnvironmentVariableScope | undefined): IEnvironmentVariableCollection { if (this._extension && scope) { // TODO: This should be removed when the env var extension API(s) are stabilized checkProposedApiEnabled(this._extension, 'envCollectionWorkspace'); @@ -1066,7 +1071,7 @@ class UnifiedEnvironmentVariableCollection { } } -class ScopedEnvironmentVariableCollection implements vscode.EnvironmentVariableCollection { +class ScopedEnvironmentVariableCollection implements IEnvironmentVariableCollection { public get persistent(): boolean { return this.collection.persistent; } public set persistent(value: boolean) { this.collection.persistent = value; @@ -1081,7 +1086,7 @@ class ScopedEnvironmentVariableCollection implements vscode.EnvironmentVariableC ) { } - getScopedEnvironmentVariableCollection(scope: vscode.EnvironmentVariableScope | undefined) { + getScoped(scope: vscode.EnvironmentVariableScope | undefined) { return this.collection.getScopedEnvironmentVariableCollection(scope); } diff --git a/src/vscode-dts/vscode.proposed.envCollectionWorkspace.d.ts b/src/vscode-dts/vscode.proposed.envCollectionWorkspace.d.ts index 73bd11df4e2..0f1f519a6bd 100644 --- a/src/vscode-dts/vscode.proposed.envCollectionWorkspace.d.ts +++ b/src/vscode-dts/vscode.proposed.envCollectionWorkspace.d.ts @@ -9,25 +9,30 @@ declare module 'vscode' { export interface ExtensionContext { /** - * Gets the extension's environment variable collection for this workspace, enabling changes - * to be applied to terminal environment variables. - * - * @deprecated Use {@link getEnvironmentVariableCollection} instead. + * Gets the extension's global environment variable collection for this workspace, enabling changes to be + * applied to terminal environment variables. */ - readonly environmentVariableCollection: EnvironmentVariableCollection; + readonly environmentVariableCollection: GlobalEnvironmentVariableCollection; + } + + interface GlobalEnvironmentVariableCollection extends EnvironmentVariableCollection { /** - * Gets the extension's environment variable collection for this scope, enabling changes - * to be applied to terminal environment variables. + * Gets scope-specific environment variable collection for the extension. This enables alterations to + * terminal environment variables solely within the designated scope, and is applied in addition to (and + * after) the global collection. + * + * Each object obtained through this method is isolated and does not impact objects for other scopes, + * including the global collection. * * @param scope The scope to which the environment variable collection applies to. */ - getEnvironmentVariableCollection(scope?: EnvironmentVariableScope): EnvironmentVariableCollection; + getScoped(scope: EnvironmentVariableScope): EnvironmentVariableCollection; } export type EnvironmentVariableScope = { /** - * Any specific workspace folder to get collection for. If unspecified, collection applicable to all workspace folders is returned. - */ + * Any specific workspace folder to get collection for. If unspecified, collection applicable to all workspace folders is returned. + */ workspaceFolder?: WorkspaceFolder; }; } From 4c2daf27fa92c886fd38ea4700ed4138c5880525 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 15 Aug 2023 16:03:41 -0700 Subject: [PATCH 060/223] focus editor on close --- .../contrib/accessibility/browser/accessibilityContributions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts index 78d4993fcdd..870dba52a51 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts @@ -310,7 +310,7 @@ export class InlineCompletionsAccessibleViewContribution extends Disposable { verbositySettingKey: AccessibilityVerbositySettingId.InlineCompletions, provideContent() { return content; }, onClose() { - + editor.focus(); }, options: this._options }); From 61011c827c7722dc2b49a84c675e6009cf1e5a65 Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Tue, 15 Aug 2023 23:18:31 +0000 Subject: [PATCH 061/223] Fix compile errors --- .../vscode.proposed.envCollectionWorkspace.d.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/vscode-dts/vscode.proposed.envCollectionWorkspace.d.ts b/src/vscode-dts/vscode.proposed.envCollectionWorkspace.d.ts index 0f1f519a6bd..43f77834b64 100644 --- a/src/vscode-dts/vscode.proposed.envCollectionWorkspace.d.ts +++ b/src/vscode-dts/vscode.proposed.envCollectionWorkspace.d.ts @@ -7,15 +7,15 @@ declare module 'vscode' { // https://github.com/microsoft/vscode/issues/171173 - export interface ExtensionContext { - /** - * Gets the extension's global environment variable collection for this workspace, enabling changes to be - * applied to terminal environment variables. - */ - readonly environmentVariableCollection: GlobalEnvironmentVariableCollection; - } + // export interface ExtensionContext { + // /** + // * Gets the extension's global environment variable collection for this workspace, enabling changes to be + // * applied to terminal environment variables. + // */ + // readonly environmentVariableCollection: GlobalEnvironmentVariableCollection; + // } - interface GlobalEnvironmentVariableCollection extends EnvironmentVariableCollection { + export interface GlobalEnvironmentVariableCollection extends EnvironmentVariableCollection { /** * Gets scope-specific environment variable collection for the extension. This enables alterations to * terminal environment variables solely within the designated scope, and is applied in addition to (and From 7c577637e862ad563a57d324347c4bf4e727f5ea Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Tue, 15 Aug 2023 23:31:07 +0000 Subject: [PATCH 062/223] Fix test --- .../src/singlefolder-tests/terminal.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts index f8c8b806853..76408d2de77 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { deepStrictEqual, doesNotThrow, equal, ok, strictEqual, throws } from 'assert'; -import { commands, ConfigurationTarget, Disposable, env, EnvironmentVariableCollection, EnvironmentVariableMutator, EnvironmentVariableMutatorOptions, EnvironmentVariableMutatorType, EnvironmentVariableScope, EventEmitter, ExtensionContext, extensions, ExtensionTerminalOptions, Pseudoterminal, Terminal, TerminalDimensions, TerminalExitReason, TerminalOptions, TerminalState, UIKind, Uri, window, workspace } from 'vscode'; +import { commands, ConfigurationTarget, Disposable, env, EnvironmentVariableMutator, EnvironmentVariableMutatorOptions, EnvironmentVariableMutatorType, EventEmitter, ExtensionContext, extensions, ExtensionTerminalOptions, GlobalEnvironmentVariableCollection, Pseudoterminal, Terminal, TerminalDimensions, TerminalExitReason, TerminalOptions, TerminalState, UIKind, Uri, window, workspace } from 'vscode'; import { assertNoRpc, poll } from '../utils'; // Disable terminal tests: @@ -913,10 +913,10 @@ import { assertNoRpc, poll } from '../utils'; test('get and forEach should work (scope)', () => { // TODO: Remove cast once `envCollectionWorkspace` API is finalized. - const collection = extensionContext.environmentVariableCollection as (EnvironmentVariableCollection & { getScopedEnvironmentVariableCollection(scope: EnvironmentVariableScope): EnvironmentVariableCollection }); + const collection = extensionContext.environmentVariableCollection as GlobalEnvironmentVariableCollection; disposables.push({ dispose: () => collection.clear() }); const scope = { workspaceFolder: { uri: Uri.file('workspace1'), name: 'workspace1', index: 0 } }; - const scopedCollection = collection.getScopedEnvironmentVariableCollection(scope); + const scopedCollection = collection.getScoped(scope); scopedCollection.replace('A', 'scoped~a2~'); scopedCollection.append('B', 'scoped~b2~'); scopedCollection.prepend('C', 'scoped~c2~'); @@ -928,7 +928,7 @@ import { assertNoRpc, poll } from '../utils'; applyAtProcessCreation: true, applyAtShellIntegration: false }; - const expectedScopedCollection = collection.getScopedEnvironmentVariableCollection(scope); + const expectedScopedCollection = collection.getScoped(scope); deepStrictEqual(expectedScopedCollection.get('A'), { value: 'scoped~a2~', type: EnvironmentVariableMutatorType.Replace, options: defaultOptions }); deepStrictEqual(expectedScopedCollection.get('B'), { value: 'scoped~b2~', type: EnvironmentVariableMutatorType.Append, options: defaultOptions }); deepStrictEqual(expectedScopedCollection.get('C'), { value: 'scoped~c2~', type: EnvironmentVariableMutatorType.Prepend, options: defaultOptions }); From 004189ef85c8896e694671cc6bedf7ed28eeed31 Mon Sep 17 00:00:00 2001 From: tisilent Date: Wed, 16 Aug 2023 17:15:51 +0800 Subject: [PATCH 063/223] fallback to this.inputValue if initialInput is undefined --- .../terminalContrib/find/browser/terminalFindWidget.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/find/browser/terminalFindWidget.ts b/src/vs/workbench/contrib/terminalContrib/find/browser/terminalFindWidget.ts index f730c159463..efb9b621568 100644 --- a/src/vs/workbench/contrib/terminalContrib/find/browser/terminalFindWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/find/browser/terminalFindWidget.ts @@ -80,17 +80,18 @@ export class TerminalFindWidget extends SimpleFindWidget { override reveal(): void { const initialInput = this._instance.hasSelection() && !this._instance.selection!.includes('\n') ? this._instance.selection : undefined; + const inputValue = initialInput ?? this.inputValue; const xterm = this._instance.xterm; - if (xterm && initialInput && initialInput !== '') { + if (xterm && inputValue && inputValue !== '') { // trigger highlight all matches - this._findPreviousWithEvent(xterm, initialInput, { incremental: true, regex: this._getRegexValue(), wholeWord: this._getWholeWordValue(), caseSensitive: this._getCaseSensitiveValue() }).then(foundMatch => { + this._findPreviousWithEvent(xterm, inputValue, { incremental: true, regex: this._getRegexValue(), wholeWord: this._getWholeWordValue(), caseSensitive: this._getCaseSensitiveValue() }).then(foundMatch => { this.updateButtons(foundMatch); this._register(Event.once(xterm.onDidChangeSelection)(() => xterm.clearActiveSearchDecoration())); }); } this.updateButtons(false); - super.reveal(initialInput); + super.reveal(inputValue); this._findWidgetVisible.set(true); } From 01029a44a2f6b3eef401849612b4a08b532c62bb Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 16 Aug 2023 13:39:11 +0200 Subject: [PATCH 064/223] Fixes monaco editor type check --- src/tsconfig.monaco.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tsconfig.monaco.json b/src/tsconfig.monaco.json index 39a5f911fb1..9710f4f0ba1 100644 --- a/src/tsconfig.monaco.json +++ b/src/tsconfig.monaco.json @@ -33,6 +33,7 @@ "vs/platform/files/browser/webFileSystemAccess.ts", "vs/platform/telemetry/*", "vs/platform/assignment/*", - "vs/platform/terminal/common/capabilities/" + "vs/platform/terminal/*", + "vs/platform/externalTerminal/*", ] } From 9a8352107ab5895460f5f9dce523c03987958187 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 16 Aug 2023 17:04:27 +0200 Subject: [PATCH 065/223] Wait to processes outside port forwarding until restore --- .../services/remote/common/tunnelModel.ts | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/services/remote/common/tunnelModel.ts b/src/vs/workbench/services/remote/common/tunnelModel.ts index 5a23fcaf61f..a5dc6002794 100644 --- a/src/vs/workbench/services/remote/common/tunnelModel.ts +++ b/src/vs/workbench/services/remote/common/tunnelModel.ts @@ -404,6 +404,8 @@ export class TunnelModel extends Disposable { public readonly configPortsAttributes: PortsAttributes; private restoreListener: DisposableStore | undefined = undefined; private knownPortsRestoreValue: string | undefined; + private restoreComplete = false; + private onRestoreComplete: Emitter = new Emitter(); private unrestoredExtensionTunnels: Map = new Map(); private sessionCachedProperties: Map> = new Map(); @@ -558,7 +560,7 @@ export class TunnelModel extends Disposable { const alreadyForwarded = mapHasAddressLocalhostOrAllInterfaces(this.detected, tunnel.remoteHost, tunnel.remotePort); // Extension forwarded ports should only be updated, not restored. if ((tunnel.source.source !== TunnelSource.Extension && !alreadyForwarded) || (tunnel.source.source === TunnelSource.Extension && alreadyForwarded)) { - await this.forward({ + await this.doForward({ remote: { host: tunnel.remoteHost, port: tunnel.remotePort }, local: tunnel.localPort, name: tunnel.name, @@ -573,6 +575,9 @@ export class TunnelModel extends Disposable { } } + this.restoreComplete = true; + this.onRestoreComplete.fire(); + if (!this.restoreListener) { // It's possible that at restore time the value hasn't synced. const key = await this.getStorageKey(); @@ -620,6 +625,13 @@ export class TunnelModel extends Disposable { } async forward(tunnelProperties: TunnelProperties, attributes?: Attributes | null): Promise { + if (!this.restoreComplete && this.environmentService.remoteAuthority) { + await Event.toPromise(this.onRestoreComplete.event); + } + return this.doForward(tunnelProperties, attributes); + } + + private async doForward(tunnelProperties: TunnelProperties, attributes?: Attributes | null): Promise { await this.extensionService.activateByEvent(ACTIVATION_EVENT); const existingTunnel = mapHasAddressLocalhostOrAllInterfaces(this.forwarded, tunnelProperties.remote.host, tunnelProperties.remote.port); @@ -721,7 +733,7 @@ export class TunnelModel extends Disposable { } case MergedAttributeAction.Reopen: { await this.close(existingTunnel.remoteHost, existingTunnel.remotePort, TunnelCloseReason.User); - await this.forward(tunnelProperties, attributes); + await this.doForward(tunnelProperties, attributes); } } @@ -874,7 +886,7 @@ export class TunnelModel extends Disposable { for (const forwarded of tunnels) { const attributes = allAttributes.get(forwarded.remotePort); if ((attributes?.protocol || (forwarded.protocol !== TunnelProtocol.Http)) && (attributes?.protocol !== forwarded.protocol)) { - await this.forward({ + await this.doForward({ remote: { host: forwarded.remoteHost, port: forwarded.remotePort }, local: forwarded.localPort, name: forwarded.name, From b0abdbeadc01b0f922284843a5bdc1594b19201a Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 16 Aug 2023 17:13:04 +0200 Subject: [PATCH 066/223] use stopWatch for measuring MP query duration --- .../common/extensionGalleryService.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts index 2fcc28f621f..9d6e9c4739a 100644 --- a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts @@ -26,6 +26,7 @@ import { asJson, asTextOrError, IRequestService, isSuccess } from 'vs/platform/r import { resolveMarketplaceHeaders } from 'vs/platform/externalServices/common/marketplace'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { StopWatch } from 'vs/base/common/stopwatch'; const CURRENT_TARGET_PLATFORM = isWeb ? TargetPlatform.WEB : getTargetPlatform(platform, arch); const ACTIVITY_HEADER_NAME = 'X-Market-Search-Activity-Id'; @@ -874,14 +875,14 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi } if (needAllVersions.size) { - const startTime = new Date().getTime(); + const stopWatch = new StopWatch(); const query = new Query() .withFlags(flags & ~Flags.IncludeLatestVersionOnly, Flags.IncludeVersions) .withPage(1, needAllVersions.size) .withFilter(FilterType.ExtensionId, ...needAllVersions.keys()); const { extensions } = await this.queryGalleryExtensions(query, criteria, token); this.telemetryService.publicLog2('galleryService:additionalQuery', { - duration: new Date().getTime() - startTime, + duration: stopWatch.elapsed(), count: needAllVersions.size }); for (const extension of extensions) { @@ -952,7 +953,7 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi 'Content-Length': String(data.length), }; - const startTime = new Date().getTime(); + const stopWatch = new StopWatch(); let context: IRequestContext | undefined, error: any, total: number = 0; try { @@ -991,7 +992,7 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi this.telemetryService.publicLog2('galleryService:query', { ...query.telemetryData, requestBodySize: String(data.length), - duration: new Date().getTime() - startTime, + duration: stopWatch.elapsed(), success: !!context && isSuccess(context), responseBodySize: context?.res.headers['Content-Length'], statusCode: context ? String(context.res.statusCode) : undefined, From b95ec3ebe6595252fd7ef7853778222982012e42 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 16 Aug 2023 17:06:57 +0200 Subject: [PATCH 067/223] Fixes ts config --- src/tsconfig.monaco.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tsconfig.monaco.json b/src/tsconfig.monaco.json index 9710f4f0ba1..b7c5d7468b5 100644 --- a/src/tsconfig.monaco.json +++ b/src/tsconfig.monaco.json @@ -34,6 +34,6 @@ "vs/platform/telemetry/*", "vs/platform/assignment/*", "vs/platform/terminal/*", - "vs/platform/externalTerminal/*", + "vs/platform/externalTerminal/*" ] } From dea0b0c294f8dce44f4b79883a9ef4db923127d9 Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Wed, 16 Aug 2023 08:47:25 -0700 Subject: [PATCH 068/223] Tweak untitled editor chat hint wording (#190597) --- .../browser/untitledTextEditorHint/untitledTextEditorHint.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/codeEditor/browser/untitledTextEditorHint/untitledTextEditorHint.ts b/src/vs/workbench/contrib/codeEditor/browser/untitledTextEditorHint/untitledTextEditorHint.ts index 628d7e9677b..b2dd6a3cdea 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/untitledTextEditorHint/untitledTextEditorHint.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/untitledTextEditorHint/untitledTextEditorHint.ts @@ -154,7 +154,7 @@ class UntitledTextEditorHintContentWidget implements IContentWidget { const providerName = (providers.length === 1 ? providers[0].label : undefined) ?? this.productService.nameShort; const inlineChatId = 'inlineChat.start'; - let ariaLabel = `Ask ${providerName} to do something or start typing to dismiss.`; + let ariaLabel = `Ask ${providerName} something or start typing to dismiss.`; const handleClick = () => { this.telemetryService.publicLog2('workbenchActionExecuted', { From 28f2011cd72ad009b09aae4afe97644d270cb4d9 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 16 Aug 2023 09:53:56 -0700 Subject: [PATCH 069/223] simplify a line --- .../contrib/accessibility/browser/accessibilityContributions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts index 870dba52a51..5d19ae5cff7 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts @@ -302,7 +302,7 @@ export class InlineCompletionsAccessibleViewContribution extends Disposable { if (!lineText) { return false; } - const content = InlineCompletionsController.get(editor)?.model.get()?.ghostText.get()?.renderForScreenReader(lineText); + const content = state.ghostText.renderForScreenReader(lineText); if (!content) { return false; } From 7b12bf16a190935ccde62d2321047e53464a480d Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 16 Aug 2023 10:06:21 -0700 Subject: [PATCH 070/223] don't inform abt the hover --- .../browser/inlineCompletionsController.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts index 6d2d4aa8460..1facaac00f3 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts @@ -204,18 +204,11 @@ export class InlineCompletionsController extends Disposable { } private provideScreenReaderHint(): void { - const showHoverKeybinding = this._keybindingService.lookupKeybinding('editor.action.showHover'); const accessibleViewKeybinding = this._keybindingService.lookupKeybinding('editor.action.accessibleView'); if (this.configurationService.getValue('accessibility.verbosity.inlineCompletions')) { let hint: string | undefined; - if (showHoverKeybinding && accessibleViewKeybinding) { - hint = localize('showBothHints', "View more actions ({0}) or inspect this in the accessible view ({1})", showHoverKeybinding.getAriaLabel(), accessibleViewKeybinding.getAriaLabel()); - } else if (showHoverKeybinding) { - hint = localize('showHoverHint', "View more actions ({0})", showHoverKeybinding.getAriaLabel()); - } else if (accessibleViewKeybinding) { + if (accessibleViewKeybinding) { hint = localize('showAccessibleViewHint', "Inspect this in the accessible view ({0})", accessibleViewKeybinding.getAriaLabel()); - } - if (hint) { status(hint); } } From f0b5e0d912a4d695d5cb777665b153c5ece9919d Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 16 Aug 2023 10:13:26 -0700 Subject: [PATCH 071/223] alert w hint --- .../browser/inlineCompletionsController.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts index 1facaac00f3..5870bd01e2d 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { alert, status } from 'vs/base/browser/ui/aria/aria'; +import { alert } from 'vs/base/browser/ui/aria/aria'; import { Event } from 'vs/base/common/event'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { ITransaction, autorun, constObservable, disposableObservableValue, observableFromEvent, observableValue, transaction } from 'vs/base/common/observable'; @@ -193,8 +193,7 @@ export class InlineCompletionsController extends Disposable { const lineText = model.textModel.getLineContent(state.ghostText.lineNumber); this.audioCueService.playAudioCue(AudioCue.inlineSuggestion).then(() => { if (this.editor.getOption(EditorOption.screenReaderAnnounceInlineSuggestion)) { - alert(state.ghostText.renderForScreenReader(lineText)); - this.provideScreenReaderHint(); + this.provideScreenReaderUpdate(state.ghostText.renderForScreenReader(lineText)); } }); } @@ -203,14 +202,14 @@ export class InlineCompletionsController extends Disposable { this._register(new InlineCompletionsHintsWidget(this.editor, this.model, this.instantiationService)); } - private provideScreenReaderHint(): void { + private provideScreenReaderUpdate(content: string): void { const accessibleViewKeybinding = this._keybindingService.lookupKeybinding('editor.action.accessibleView'); if (this.configurationService.getValue('accessibility.verbosity.inlineCompletions')) { let hint: string | undefined; if (accessibleViewKeybinding) { hint = localize('showAccessibleViewHint', "Inspect this in the accessible view ({0})", accessibleViewKeybinding.getAriaLabel()); - status(hint); } + hint ? alert(content + ', ' + hint) : alert(content); } } From 72ba6124f0eab92427cd35ef38973e8d6edbf3a6 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 16 Aug 2023 10:20:49 -0700 Subject: [PATCH 072/223] use correct toolbar aria label for type --- .../accessibility/browser/accessibleView.ts | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index 90ca7caa0bb..9b5ebff9d4e 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -123,7 +123,7 @@ class AccessibleView extends Disposable { const codeEditorWidgetOptions: ICodeEditorWidgetOptions = { contributions: EditorExtensionsRegistry.getEditorContributions().filter(c => c.id !== CodeActionController.ID) }; - this._toolbar = this._register(_instantiationService.createInstance(WorkbenchToolBar, this._editorContainer, { ariaLabel: localize('accessibleViewToolbar', "Accessible View Toolbar"), orientation: ActionsOrientation.HORIZONTAL })); + this._toolbar = this._register(_instantiationService.createInstance(WorkbenchToolBar, this._editorContainer, { orientation: ActionsOrientation.HORIZONTAL })); this._toolbar.context = { viewId: 'accessibleView' }; const toolbarElt = this._toolbar.getElement(); toolbarElt.tabIndex = 0; @@ -309,16 +309,13 @@ class AccessibleView extends Disposable { model.setLanguage(provider.options.language ?? 'markdown'); container.appendChild(this._editorContainer); let ariaLabel = ''; - let helpHint = ''; + let actionsHint = ''; const verbose = this._configurationService.getValue(provider.verbositySettingKey); if (verbose && provider.options.type === AccessibleViewType.View && !showAccessibleViewHelp) { - const accessibilityHelpKeybinding = this._keybindingService.lookupKeybinding(AccessibilityCommandId.OpenAccessibilityHelp)?.getLabel(); - if (accessibilityHelpKeybinding) { - helpHint = localize('ariaAccessibilityHelp', "Use {0} for accessibility help", accessibilityHelpKeybinding); - } + actionsHint = localize('ariaAccessibleViewActions', "Use Shift+Tab to explore actions such as disabling this hint."); } - if (helpHint) { - ariaLabel = provider.options.ariaLabel ? localize('helpAriaKb', "{0}, {1}", provider.options.ariaLabel, helpHint) : localize('accessible-view', "Accessible View, {0}", helpHint); + if (actionsHint) { + ariaLabel = provider.options.ariaLabel ? localize('actionsAriaKb', "{0}, {1}", provider.options.ariaLabel, actionsHint) : localize('accessible-view', "Accessible View, {0}", actionsHint); } else { ariaLabel = provider.options.ariaLabel ? provider.options.ariaLabel : localize('helpAriaNoKb', "Accessible View"); } @@ -326,7 +323,7 @@ class AccessibleView extends Disposable { this._editorWidget.updateOptions({ ariaLabel }); this._editorWidget.focus(); }); - this._updateToolbar(provider.actions); + this._updateToolbar(provider.actions, provider.options.type); const disposableStore = new DisposableStore(); disposableStore.add(this._editorWidget.onKeyUp((e) => provider.onKeyDown?.(e))); @@ -355,8 +352,9 @@ class AccessibleView extends Disposable { return disposableStore; } - private _updateToolbar(providedActions?: IAction[]): void { + private _updateToolbar(providedActions?: IAction[], type?: AccessibleViewType): void { this._toolbar.setActions([]); + this._toolbar.setAriaLabel(type === AccessibleViewType.Help ? localize('accessibleHelpToolbar', 'Accessibility Help') : localize('accessibleViewToolbar', "Accessible View")); const menuActions: IAction[] = []; const toolbarMenu = this._register(this._menuService.createMenu(MenuId.AccessibleView, this._contextKeyService)); createAndFillInActionBarActions(toolbarMenu, {}, menuActions); From 8cc9d27ac8dcc6d633166943132725b622db3fda Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 16 Aug 2023 10:21:45 -0700 Subject: [PATCH 073/223] fix another bug --- .../workbench/contrib/accessibility/browser/accessibleView.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index 9b5ebff9d4e..cd134d921de 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -410,7 +410,7 @@ class AccessibleView extends Disposable { private _getAccessibleViewHelpDialogContent(providerHasSymbols?: boolean): string { const navigationHint = this._getNavigationHint(); const goToSymbolHint = this._getGoToSymbolHint(providerHasSymbols); - const toolbarHint = localize('toolbar', "Navigate to the toolbar ({0} or Shift+Tab)"); + const toolbarHint = localize('toolbar', "Navigate to the toolbar (Shift+Tab))"); let hint = localize('intro', "In the accessible view, you can:\n"); if (navigationHint) { From 71e3343e41b622470b05098b670380ebacfaaa0a Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 16 Aug 2023 10:48:22 -0700 Subject: [PATCH 074/223] use editorOption --- src/vs/editor/common/config/editorOptions.ts | 10 +++++++++- .../editor/common/standalone/standaloneEnums.ts | 3 ++- .../browser/inlineCompletionsController.ts | 17 ++++++++++------- src/vs/monaco.d.ts | 8 +++++++- 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 6895706d518..eee7baf03c6 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -730,6 +730,11 @@ export interface IEditorOptions { * Controls whether the editor receives tabs or defers them to the workbench for navigation. */ tabFocusMode?: boolean; + + /** + * Controls whether the accessibility hint should be provided to screen reader users when an inline completion is shown. + */ + inlineCompletionsAccessibilityVerbose?: boolean; } /** @@ -5143,7 +5148,8 @@ export const enum EditorOption { layoutInfo, wrappingInfo, defaultColorDecorators, - colorDecoratorsActivatedOn + colorDecoratorsActivatedOn, + inlineCompletionsAccessibilityVerbose } export const EditorOptions = { @@ -5734,6 +5740,8 @@ export const EditorOptions = { )), suggest: register(new EditorSuggest()), inlineSuggest: register(new InlineEditorSuggest()), + inlineCompletionsAccessibilityVerbose: register(new EditorBooleanOption(EditorOption.inlineCompletionsAccessibilityVerbose, 'inlineCompletionsAccessibilityVerbose', false, + { description: nls.localize('inlineCompletionsAccessibilityVerbose', "Controls whether the accessibility hint should be provided to screen reader users when an inline completion is shown.") })), suggestFontSize: register(new EditorIntOption( EditorOption.suggestFontSize, 'suggestFontSize', 0, 0, 1000, diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts index d92f0c1272e..7cc3add3df1 100644 --- a/src/vs/editor/common/standalone/standaloneEnums.ts +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -318,7 +318,8 @@ export enum EditorOption { layoutInfo = 142, wrappingInfo = 143, defaultColorDecorators = 144, - colorDecoratorsActivatedOn = 145 + colorDecoratorsActivatedOn = 145, + inlineCompletionsAccessibilityVerbose = 146 } /** diff --git a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts index 5870bd01e2d..c4e567e07ce 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts @@ -80,7 +80,6 @@ export class InlineCompletionsController extends Disposable { super(); this._register(new InlineCompletionContextKeys(this.contextKeyService, this.model)); - this._register(Event.runAndSubscribe(editor.onDidChangeModel, () => transaction(tx => { /** @description onDidChangeModel */ this.model.set(undefined, tx); @@ -200,17 +199,21 @@ export class InlineCompletionsController extends Disposable { })); this._register(new InlineCompletionsHintsWidget(this.editor, this.model, this.instantiationService)); + this._register(this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('accessibility.verbosity.inlineCompletions')) { + this.editor.updateOptions({ inlineCompletionsAccessibilityVerbose: this.configurationService.getValue('accessibility.verbosity.inlineCompletions') }); + } + })); + this.editor.updateOptions({ inlineCompletionsAccessibilityVerbose: this.configurationService.getValue('accessibility.verbosity.inlineCompletions') }); } private provideScreenReaderUpdate(content: string): void { const accessibleViewKeybinding = this._keybindingService.lookupKeybinding('editor.action.accessibleView'); - if (this.configurationService.getValue('accessibility.verbosity.inlineCompletions')) { - let hint: string | undefined; - if (accessibleViewKeybinding) { - hint = localize('showAccessibleViewHint', "Inspect this in the accessible view ({0})", accessibleViewKeybinding.getAriaLabel()); - } - hint ? alert(content + ', ' + hint) : alert(content); + let hint: string | undefined; + if (accessibleViewKeybinding && this.editor.getOption(EditorOption.inlineCompletionsAccessibilityVerbose)) { + hint = localize('showAccessibleViewHint', "Inspect this in the accessible view ({0})", accessibleViewKeybinding.getAriaLabel()); } + hint ? alert(content + ', ' + hint) : alert(content); } /** diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 8914c4f0036..8fbfcd493ae 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3888,6 +3888,10 @@ declare namespace monaco.editor { * Controls whether the editor receives tabs or defers them to the workbench for navigation. */ tabFocusMode?: boolean; + /** + * Controls whether the accessibility hint should be provided to screen reader users when an inline completion is shown. + */ + inlineCompletionsAccessibilityVerbose?: boolean; } export interface IDiffEditorBaseOptions { @@ -5024,7 +5028,8 @@ declare namespace monaco.editor { layoutInfo = 142, wrappingInfo = 143, defaultColorDecorators = 144, - colorDecoratorsActivatedOn = 145 + colorDecoratorsActivatedOn = 145, + inlineCompletionsAccessibilityVerbose = 146 } export const EditorOptions: { @@ -5148,6 +5153,7 @@ declare namespace monaco.editor { stopRenderingLineAfter: IEditorOption; suggest: IEditorOption>>; inlineSuggest: IEditorOption>>; + inlineCompletionsAccessibilityVerbose: IEditorOption; suggestFontSize: IEditorOption; suggestLineHeight: IEditorOption; suggestOnTriggerCharacters: IEditorOption; From c0dd3cd00cd633452506445dd79037b91f6d6bd6 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 16 Aug 2023 10:57:28 -0700 Subject: [PATCH 075/223] fix tests --- src/vs/editor/test/browser/testCodeEditor.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/test/browser/testCodeEditor.ts b/src/vs/editor/test/browser/testCodeEditor.ts index acca9e7cde3..28a17729768 100644 --- a/src/vs/editor/test/browser/testCodeEditor.ts +++ b/src/vs/editor/test/browser/testCodeEditor.ts @@ -45,7 +45,8 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { BrandedService, IInstantiationService, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; -import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { MockContextKeyService, MockKeybindingService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { ILogService, NullLogService } from 'vs/platform/log/common/log'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; @@ -176,6 +177,7 @@ export function createCodeEditorServices(disposables: DisposableStore, services: }; define(IAccessibilityService, TestAccessibilityService); + define(IKeybindingService, MockKeybindingService); define(IClipboardService, TestClipboardService); define(IEditorWorkerService, TestEditorWorkerService); defineInstance(IOpenerService, NullOpenerService); From 3f8a55b32c494288c3b1b4bb7c8e26cf1d899fad Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 16 Aug 2023 11:11:20 -0700 Subject: [PATCH 076/223] `trimLeft` -> `trimStart` (#190601) These should be the same but the `trimLeft` name has been deprecated --- extensions/git/src/git.ts | 4 ++-- .../contrib/linesOperations/browser/moveLinesCommand.ts | 8 ++++---- src/vs/workbench/contrib/search/browser/searchModel.ts | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index ab03a354d11..62bdf24ebcb 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -481,7 +481,7 @@ export class Git { const result = await this.exec(pathInsidePossibleRepository, ['rev-parse', '--show-toplevel']); // Keep trailing spaces which are part of the directory name - const repositoryRootPath = path.normalize(result.stdout.trimLeft().replace(/[\r\n]+$/, '')); + const repositoryRootPath = path.normalize(result.stdout.trimStart().replace(/[\r\n]+$/, '')); if (isWindows) { // On Git 2.25+ if you call `rev-parse --show-toplevel` on a mapped drive, instead of getting the mapped @@ -528,7 +528,7 @@ export class Git { !isDescendant(pathInsidePossibleRepository, repositoryRootPath) && this.compareGitVersionTo('2.31.0') !== -1) { const relativePathResult = await this.exec(pathInsidePossibleRepository, ['rev-parse', '--path-format=relative', '--show-toplevel',]); - return path.resolve(pathInsidePossibleRepository, relativePathResult.stdout.trimLeft().replace(/[\r\n]+$/, '')); + return path.resolve(pathInsidePossibleRepository, relativePathResult.stdout.trimStart().replace(/[\r\n]+$/, '')); } return repositoryRootPath; diff --git a/src/vs/editor/contrib/linesOperations/browser/moveLinesCommand.ts b/src/vs/editor/contrib/linesOperations/browser/moveLinesCommand.ts index 6193ce53e26..8db880a15ad 100644 --- a/src/vs/editor/contrib/linesOperations/browser/moveLinesCommand.ts +++ b/src/vs/editor/contrib/linesOperations/browser/moveLinesCommand.ts @@ -117,7 +117,7 @@ export class MoveLinesCommand implements ICommand { const oldIndentation = strings.getLeadingWhitespace(model.getLineContent(movingLineNumber)); const newSpaceCnt = movingLineMatchResult + indentUtils.getSpaceCnt(oldIndentation, tabSize); const newIndentation = indentUtils.generateIndent(newSpaceCnt, tabSize, insertSpaces); - insertingText = newIndentation + this.trimLeft(movingLineText); + insertingText = newIndentation + this.trimStart(movingLineText); } else { // no enter rule matches, let's check indentatin rules then. virtualModel.getLineContent = (lineNumber: number) => { @@ -141,7 +141,7 @@ export class MoveLinesCommand implements ICommand { const oldSpaceCnt = indentUtils.getSpaceCnt(oldIndentation, tabSize); if (newSpaceCnt !== oldSpaceCnt) { const newIndentation = indentUtils.generateIndent(newSpaceCnt, tabSize, insertSpaces); - insertingText = newIndentation + this.trimLeft(movingLineText); + insertingText = newIndentation + this.trimStart(movingLineText); } } } @@ -272,7 +272,7 @@ export class MoveLinesCommand implements ICommand { enterPrefix = indentConverter.unshiftIndent(enter.indentation) + enter.appendText; } const movingLineText = model.getLineContent(line); - if (this.trimLeft(movingLineText).indexOf(this.trimLeft(enterPrefix)) >= 0) { + if (this.trimStart(movingLineText).indexOf(this.trimStart(enterPrefix)) >= 0) { const oldIndentation = strings.getLeadingWhitespace(model.getLineContent(line)); let newIndentation = strings.getLeadingWhitespace(enterPrefix); const indentMetadataOfMovelingLine = getIndentMetadata(model, line, this._languageConfigurationService); @@ -354,7 +354,7 @@ export class MoveLinesCommand implements ICommand { return this.parseEnterResult(model, indentConverter, tabSize, line, enter); } - private trimLeft(str: string) { + private trimStart(str: string) { return str.replace(/^\s+/, ''); } diff --git a/src/vs/workbench/contrib/search/browser/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchModel.ts index 81920160a6c..8f9dd5cdcb6 100644 --- a/src/vs/workbench/contrib/search/browser/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchModel.ts @@ -94,7 +94,7 @@ export class Match { after = this._oneLinePreviewText.substring(this._rangeInPreviewText.endColumn - 1); before = lcut(before, 26); - before = before.trimLeft(); + before = before.trimStart(); let charsRemaining = Match.MAX_PREVIEW_CHARS - before.length; inside = inside.substr(0, charsRemaining); From 719a6a6cdfe132852ab26434e5e80fae16a44941 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 16 Aug 2023 12:02:40 -0700 Subject: [PATCH 077/223] debug: adopt external changes (#190602) For #183449 Also adds the leak detector to debug tests and fixes some usages of it (#190503) --- .../contrib/debug/browser/debugService.ts | 2 +- .../contrib/debug/browser/debugSession.ts | 52 ++++++------ .../contrib/debug/common/debugModel.ts | 39 +++++---- .../contrib/debug/common/debugStorage.ts | 41 +++++++-- .../debug/test/browser/baseDebugView.test.ts | 23 ++--- .../debug/test/browser/breakpoints.test.ts | 83 +++++++++++++------ .../debug/test/browser/callStack.test.ts | 57 ++++++++----- .../test/browser/debugANSIHandling.test.ts | 27 +++--- .../browser/debugConfigurationManager.test.ts | 18 ++-- .../debug/test/browser/debugHover.test.ts | 17 +++- .../debug/test/browser/debugMemory.test.ts | 3 + .../debug/test/browser/debugSource.test.ts | 9 +- .../debug/test/browser/debugUtils.test.ts | 5 +- .../debug/test/browser/debugViewModel.test.ts | 13 +-- .../debug/test/browser/linkDetector.test.ts | 17 ++-- .../debug/test/browser/mockDebugModel.ts | 8 +- .../test/browser/rawDebugSession.test.ts | 18 +++- .../contrib/debug/test/browser/repl.test.ts | 55 +++++++----- .../contrib/debug/test/browser/watch.test.ts | 15 +++- .../test/common/abstractDebugAdapter.test.ts | 3 + .../debug/test/common/debugModel.test.ts | 12 ++- .../contrib/debug/test/common/mockDebug.ts | 35 +------- 22 files changed, 355 insertions(+), 197 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 9ffa5e8600f..0fad52f9739 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -120,7 +120,7 @@ export class DebugService implements IDebugService { this.disposables.add(this.adapterManager); this.configurationManager = this.instantiationService.createInstance(ConfigurationManager, this.adapterManager); this.disposables.add(this.configurationManager); - this.debugStorage = this.instantiationService.createInstance(DebugStorage); + this.debugStorage = this.disposables.add(this.instantiationService.createInstance(DebugStorage)); this.chosenEnvironments = this.debugStorage.loadChosenEnvironments(); diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index 13decde4682..2e2b758fc43 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -40,7 +40,7 @@ import { IHostService } from 'vs/workbench/services/host/browser/host'; import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; -export class DebugSession implements IDebugSession { +export class DebugSession implements IDebugSession, IDisposable { parentSession: IDebugSession | undefined; private _subId: string | undefined; @@ -52,7 +52,7 @@ export class DebugSession implements IDebugSession { private threads = new Map(); private threadIds: number[] = []; private cancellationMap = new Map(); - private rawListeners: IDisposable[] = []; + private rawListeners = new DisposableStore(); private fetchThreadsScheduler: RunOnceScheduler | undefined; private passFocusScheduler: RunOnceScheduler; private lastContinuedThreadId: number | undefined; @@ -103,14 +103,11 @@ export class DebugSession implements IDebugSession { this.repl = (this.parentSession as DebugSession).repl; } - const toDispose = new DisposableStore(); + const toDispose = this.rawListeners.add(new DisposableStore()); const replListener = toDispose.add(new MutableDisposable()); replListener.value = this.repl.onDidChangeElements(() => this._onDidChangeREPLElements.fire()); if (lifecycleService) { - toDispose.add(lifecycleService.onWillShutdown(() => { - this.shutdown(); - dispose(toDispose); - })); + toDispose.add(lifecycleService.onWillShutdown(() => this.shutdown())); } const compoundRoot = this._options.compoundRoot; @@ -950,7 +947,7 @@ export class DebugSession implements IDebugSession { return; } - this.rawListeners.push(this.raw.onDidInitialize(async () => { + this.rawListeners.add(this.raw.onDidInitialize(async () => { aria.status(localize('debuggingStarted', "Debugging started.")); const sendConfigurationDone = async () => { if (this.raw && this.raw.capabilities.supportsConfigurationDoneRequest) { @@ -976,7 +973,7 @@ export class DebugSession implements IDebugSession { })); const statusQueue = new Queue(); - this.rawListeners.push(this.raw.onDidStop(async event => { + this.rawListeners.add(this.raw.onDidStop(async event => { statusQueue.queue(async () => { this.passFocusScheduler.cancel(); this.stoppedDetails.push(event.body); @@ -1026,7 +1023,7 @@ export class DebugSession implements IDebugSession { }); })); - this.rawListeners.push(this.raw.onDidThread(event => { + this.rawListeners.add(this.raw.onDidThread(event => { statusQueue.queue(async () => { if (event.body.reason === 'started') { // debounce to reduce threadsRequest frequency and improve performance @@ -1034,7 +1031,7 @@ export class DebugSession implements IDebugSession { this.fetchThreadsScheduler = new RunOnceScheduler(() => { this.fetchThreads(); }, 100); - this.rawListeners.push(this.fetchThreadsScheduler); + this.rawListeners.add(this.fetchThreadsScheduler); } if (!this.fetchThreadsScheduler.isScheduled()) { this.fetchThreadsScheduler.schedule(); @@ -1052,7 +1049,7 @@ export class DebugSession implements IDebugSession { }); })); - this.rawListeners.push(this.raw.onDidTerminateDebugee(async event => { + this.rawListeners.add(this.raw.onDidTerminateDebugee(async event => { aria.status(localize('debuggingStopped', "Debugging stopped.")); if (event.body && event.body.restart) { await this.debugService.restartSession(this, event.body.restart); @@ -1061,14 +1058,14 @@ export class DebugSession implements IDebugSession { } })); - this.rawListeners.push(this.raw.onDidContinued(event => { + this.rawListeners.add(this.raw.onDidContinued(event => { statusQueue.queue(async () => { const threadId = event.body.allThreadsContinued !== false ? undefined : event.body.threadId; if (typeof threadId === 'number') { this.stoppedDetails = this.stoppedDetails.filter(sd => sd.threadId !== threadId); const tokens = this.cancellationMap.get(threadId); this.cancellationMap.delete(threadId); - tokens?.forEach(t => t.cancel()); + tokens?.forEach(t => t.dispose(true)); } else { this.stoppedDetails = []; this.cancelAllRequests(); @@ -1082,7 +1079,7 @@ export class DebugSession implements IDebugSession { })); const outputQueue = new Queue(); - this.rawListeners.push(this.raw.onDidOutput(async event => { + this.rawListeners.add(this.raw.onDidOutput(async event => { const outputSeverity = event.body.category === 'stderr' ? Severity.Error : event.body.category === 'console' ? Severity.Warning : Severity.Info; // When a variables event is received, execute immediately to obtain the variables value #126967 @@ -1161,7 +1158,7 @@ export class DebugSession implements IDebugSession { }); })); - this.rawListeners.push(this.raw.onDidBreakpoint(event => { + this.rawListeners.add(this.raw.onDidBreakpoint(event => { const id = event.body && event.body.breakpoint ? event.body.breakpoint.id : undefined; const breakpoint = this.model.getBreakpoints().find(bp => bp.getIdFromAdapter(this.getId()) === id); const functionBreakpoint = this.model.getFunctionBreakpoints().find(bp => bp.getIdFromAdapter(this.getId()) === id); @@ -1216,30 +1213,30 @@ export class DebugSession implements IDebugSession { } })); - this.rawListeners.push(this.raw.onDidLoadedSource(event => { + this.rawListeners.add(this.raw.onDidLoadedSource(event => { this._onDidLoadedSource.fire({ reason: event.body.reason, source: this.getSource(event.body.source) }); })); - this.rawListeners.push(this.raw.onDidCustomEvent(event => { + this.rawListeners.add(this.raw.onDidCustomEvent(event => { this._onDidCustomEvent.fire(event); })); - this.rawListeners.push(this.raw.onDidProgressStart(event => { + this.rawListeners.add(this.raw.onDidProgressStart(event => { this._onDidProgressStart.fire(event); })); - this.rawListeners.push(this.raw.onDidProgressUpdate(event => { + this.rawListeners.add(this.raw.onDidProgressUpdate(event => { this._onDidProgressUpdate.fire(event); })); - this.rawListeners.push(this.raw.onDidProgressEnd(event => { + this.rawListeners.add(this.raw.onDidProgressEnd(event => { this._onDidProgressEnd.fire(event); })); - this.rawListeners.push(this.raw.onDidInvalidateMemory(event => { + this.rawListeners.add(this.raw.onDidInvalidateMemory(event => { this._onDidInvalidMemory.fire(event); })); - this.rawListeners.push(this.raw.onDidInvalidated(async event => { + this.rawListeners.add(this.raw.onDidInvalidated(async event => { if (!(event.body.areas && event.body.areas.length === 1 && (event.body.areas[0] === 'variables' || event.body.areas[0] === 'watch'))) { // If invalidated event only requires to update variables or watch, do that, otherwise refatch threads https://github.com/microsoft/vscode/issues/106745 this.cancelAllRequests(); @@ -1253,7 +1250,7 @@ export class DebugSession implements IDebugSession { } })); - this.rawListeners.push(this.raw.onDidExitAdapter(event => this.onDidExitAdapter(event))); + this.rawListeners.add(this.raw.onDidExitAdapter(event => this.onDidExitAdapter(event))); } private onDidExitAdapter(event?: AdapterEndEvent): void { @@ -1280,6 +1277,11 @@ export class DebugSession implements IDebugSession { this._onDidChangeState.fire(); } + public dispose() { + this.cancelAllRequests(); + dispose(this.rawListeners); + } + //---- sources getSourceForUri(uri: URI): Source | undefined { @@ -1325,7 +1327,7 @@ export class DebugSession implements IDebugSession { } private cancelAllRequests(): void { - this.cancellationMap.forEach(tokens => tokens.forEach(t => t.cancel())); + this.cancellationMap.forEach(tokens => tokens.forEach(t => t.dispose(true))); this.cancellationMap.clear(); } diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index 98bae97b4e1..50640fcf7e5 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -26,6 +26,7 @@ import { DisassemblyViewInput } from 'vs/workbench/contrib/debug/common/disassem import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { ILogService } from 'vs/platform/log/common/log'; +import { autorun } from 'vs/base/common/observable'; interface IDebugProtocolVariableWithContext extends DebugProtocol.Variable { __vscodeVariableMenuContext?: string; @@ -1173,19 +1174,19 @@ export class ThreadAndSessionIds implements ITreeElement { } } -export class DebugModel implements IDebugModel { +export class DebugModel extends Disposable implements IDebugModel { private sessions: IDebugSession[]; private schedulers = new Map }>(); private breakpointsActivated = true; - private readonly _onDidChangeBreakpoints = new Emitter(); - private readonly _onDidChangeCallStack = new Emitter(); - private readonly _onDidChangeWatchExpressions = new Emitter(); - private breakpoints: Breakpoint[]; - private functionBreakpoints: FunctionBreakpoint[]; - private exceptionBreakpoints: ExceptionBreakpoint[]; - private dataBreakpoints: DataBreakpoint[]; - private watchExpressions: Expression[]; + private readonly _onDidChangeBreakpoints = this._register(new Emitter()); + private readonly _onDidChangeCallStack = this._register(new Emitter()); + private readonly _onDidChangeWatchExpressions = this._register(new Emitter()); + private breakpoints!: Breakpoint[]; + private functionBreakpoints!: FunctionBreakpoint[]; + private exceptionBreakpoints!: ExceptionBreakpoint[]; + private dataBreakpoints!: DataBreakpoint[]; + private watchExpressions!: Expression[]; private instructionBreakpoints: InstructionBreakpoint[]; constructor( @@ -1194,11 +1195,21 @@ export class DebugModel implements IDebugModel { @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, @ILogService private readonly logService: ILogService ) { - this.breakpoints = debugStorage.loadBreakpoints(); - this.functionBreakpoints = debugStorage.loadFunctionBreakpoints(); - this.exceptionBreakpoints = debugStorage.loadExceptionBreakpoints(); - this.dataBreakpoints = debugStorage.loadDataBreakpoints(); - this.watchExpressions = debugStorage.loadWatchExpressions(); + super(); + + this._register(autorun(reader => { + this.breakpoints = debugStorage.breakpoints.read(reader); + this.functionBreakpoints = debugStorage.functionBreakpoints.read(reader); + this.exceptionBreakpoints = debugStorage.exceptionBreakpoints.read(reader); + this.dataBreakpoints = debugStorage.dataBreakpoints.read(reader); + this._onDidChangeBreakpoints.fire(undefined); + })); + + this._register(autorun(reader => { + this.watchExpressions = debugStorage.watchExpressions.read(reader); + this._onDidChangeWatchExpressions.fire(undefined); + })); + this.instructionBreakpoints = []; this.sessions = []; } diff --git a/src/vs/workbench/contrib/debug/common/debugStorage.ts b/src/vs/workbench/contrib/debug/common/debugStorage.ts index 1265ad5fa13..1f11c827a16 100644 --- a/src/vs/workbench/contrib/debug/common/debugStorage.ts +++ b/src/vs/workbench/contrib/debug/common/debugStorage.ts @@ -10,6 +10,8 @@ import { IEvaluate, IExpression, IDebugModel } from 'vs/workbench/contrib/debug/ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { ILogService } from 'vs/platform/log/common/log'; +import { observableValue } from 'vs/base/common/observable'; +import { Disposable } from 'vs/base/common/lifecycle'; const DEBUG_BREAKPOINTS_KEY = 'debug.breakpoint'; const DEBUG_FUNCTION_BREAKPOINTS_KEY = 'debug.functionbreakpoint'; @@ -19,13 +21,38 @@ const DEBUG_WATCH_EXPRESSIONS_KEY = 'debug.watchexpressions'; const DEBUG_CHOSEN_ENVIRONMENTS_KEY = 'debug.chosenenvironment'; const DEBUG_UX_STATE_KEY = 'debug.uxstate'; -export class DebugStorage { +export class DebugStorage extends Disposable { + public readonly breakpoints = observableValue('debugBreakpoints', this.loadBreakpoints()); + public readonly functionBreakpoints = observableValue('debugFunctionBreakpoints', this.loadFunctionBreakpoints()); + public readonly exceptionBreakpoints = observableValue('debugExceptionBreakpoints', this.loadExceptionBreakpoints()); + public readonly dataBreakpoints = observableValue('debugDataBreakpoints', this.loadDataBreakpoints()); + public readonly watchExpressions = observableValue('debugWatchExpressions', this.loadWatchExpressions()); + constructor( @IStorageService private readonly storageService: IStorageService, @ITextFileService private readonly textFileService: ITextFileService, @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, @ILogService private readonly logService: ILogService - ) { } + ) { + super(); + + this._register(storageService.onDidChangeValue(StorageScope.WORKSPACE, undefined, this._store)(e => { + if (e.external) { + switch (e.key) { + case DEBUG_BREAKPOINTS_KEY: + return this.breakpoints.set(this.loadBreakpoints(), undefined); + case DEBUG_FUNCTION_BREAKPOINTS_KEY: + return this.functionBreakpoints.set(this.loadFunctionBreakpoints(), undefined); + case DEBUG_EXCEPTION_BREAKPOINTS_KEY: + return this.exceptionBreakpoints.set(this.loadExceptionBreakpoints(), undefined); + case DEBUG_DATA_BREAKPOINTS_KEY: + return this.dataBreakpoints.set(this.loadDataBreakpoints(), undefined); + case DEBUG_WATCH_EXPRESSIONS_KEY: + return this.watchExpressions.set(this.loadWatchExpressions(), undefined); + } + } + })); + } loadDebugUxState(): 'simple' | 'default' { return this.storageService.get(DEBUG_UX_STATE_KEY, StorageScope.WORKSPACE, 'default') as 'simple' | 'default'; @@ -35,7 +62,7 @@ export class DebugStorage { this.storageService.store(DEBUG_UX_STATE_KEY, value, StorageScope.WORKSPACE, StorageTarget.MACHINE); } - loadBreakpoints(): Breakpoint[] { + private loadBreakpoints(): Breakpoint[] { let result: Breakpoint[] | undefined; try { result = JSON.parse(this.storageService.get(DEBUG_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((breakpoint: any) => { @@ -46,7 +73,7 @@ export class DebugStorage { return result || []; } - loadFunctionBreakpoints(): FunctionBreakpoint[] { + private loadFunctionBreakpoints(): FunctionBreakpoint[] { let result: FunctionBreakpoint[] | undefined; try { result = JSON.parse(this.storageService.get(DEBUG_FUNCTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((fb: any) => { @@ -57,7 +84,7 @@ export class DebugStorage { return result || []; } - loadExceptionBreakpoints(): ExceptionBreakpoint[] { + private loadExceptionBreakpoints(): ExceptionBreakpoint[] { let result: ExceptionBreakpoint[] | undefined; try { result = JSON.parse(this.storageService.get(DEBUG_EXCEPTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((exBreakpoint: any) => { @@ -68,7 +95,7 @@ export class DebugStorage { return result || []; } - loadDataBreakpoints(): DataBreakpoint[] { + private loadDataBreakpoints(): DataBreakpoint[] { let result: DataBreakpoint[] | undefined; try { result = JSON.parse(this.storageService.get(DEBUG_DATA_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((dbp: any) => { @@ -79,7 +106,7 @@ export class DebugStorage { return result || []; } - loadWatchExpressions(): Expression[] { + private loadWatchExpressions(): Expression[] { let result: Expression[] | undefined; try { result = JSON.parse(this.storageService.get(DEBUG_WATCH_EXPRESSIONS_KEY, StorageScope.WORKSPACE, '[]')).map((watchStoredData: { name: string; id: string }) => { diff --git a/src/vs/workbench/contrib/debug/test/browser/baseDebugView.test.ts b/src/vs/workbench/contrib/debug/test/browser/baseDebugView.test.ts index 10161571d0a..9164b7458a8 100644 --- a/src/vs/workbench/contrib/debug/test/browser/baseDebugView.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/baseDebugView.test.ts @@ -4,20 +4,21 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { renderExpressionValue, renderVariable, renderViewTree } from 'vs/workbench/contrib/debug/browser/baseDebugView'; import * as dom from 'vs/base/browser/dom'; -import { Expression, Variable, Scope, StackFrame, Thread } from 'vs/workbench/contrib/debug/common/debugModel'; import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; -import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { isWindows } from 'vs/base/common/platform'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; -import { workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; -import { createTestSession } from 'vs/workbench/contrib/debug/test/browser/callStack.test'; +import { renderExpressionValue, renderVariable, renderViewTree } from 'vs/workbench/contrib/debug/browser/baseDebugView'; +import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector'; import { isStatusbarInDebugMode } from 'vs/workbench/contrib/debug/browser/statusbarColorProvider'; import { State } from 'vs/workbench/contrib/debug/common/debug'; -import { isWindows } from 'vs/base/common/platform'; +import { Expression, Scope, StackFrame, Thread, Variable } from 'vs/workbench/contrib/debug/common/debugModel'; +import { createTestSession } from 'vs/workbench/contrib/debug/test/browser/callStack.test'; import { createMockDebugModel } from 'vs/workbench/contrib/debug/test/browser/mockDebugModel'; -import { DisposableStore } from 'vs/base/common/lifecycle'; import { MockSession } from 'vs/workbench/contrib/debug/test/common/mockDebug'; +import { workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; const $ = dom.$; suite('Debug - Base Debug View', () => { @@ -37,6 +38,8 @@ suite('Debug - Base Debug View', () => { disposables.dispose(); }); + ensureNoDisposablesAreLeakedInTestSuite(); + test('render view tree', () => { const container = $('.container'); const treeContainer = renderViewTree(container); @@ -132,9 +135,9 @@ suite('Debug - Base Debug View', () => { }); test('statusbar in debug mode', () => { - const model = createMockDebugModel(); - const session = createTestSession(model); - const session2 = createTestSession(model, undefined, { suppressDebugStatusbar: true }); + const model = createMockDebugModel(disposables); + const session = disposables.add(createTestSession(model)); + const session2 = disposables.add(createTestSession(model, undefined, { suppressDebugStatusbar: true })); assert.strictEqual(isStatusbarInDebugMode(State.Inactive, []), false); assert.strictEqual(isStatusbarInDebugMode(State.Initializing, [session]), false); assert.strictEqual(isStatusbarInDebugMode(State.Running, [session]), true); diff --git a/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts b/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts index e5db72a8eaa..db19f575765 100644 --- a/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts @@ -4,24 +4,28 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { URI as uri } from 'vs/base/common/uri'; -import { DebugModel, Breakpoint } from 'vs/workbench/contrib/debug/common/debugModel'; -import { getExpandedBodySize, getBreakpointMessageAndIcon } from 'vs/workbench/contrib/debug/browser/breakpointsView'; -import { DisposableStore, dispose } from 'vs/base/common/lifecycle'; -import { Range } from 'vs/editor/common/core/range'; -import { IBreakpointData, IBreakpointUpdateData, IDebugService, State } from 'vs/workbench/contrib/debug/common/debug'; -import { createBreakpointDecorations } from 'vs/workbench/contrib/debug/browser/breakpointEditorContribution'; -import { OverviewRulerLane } from 'vs/editor/common/model'; import { MarkdownString } from 'vs/base/common/htmlContent'; -import { createTextModel } from 'vs/editor/test/common/testTextModel'; -import { createTestSession } from 'vs/workbench/contrib/debug/test/browser/callStack.test'; -import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { DisposableStore, dispose } from 'vs/base/common/lifecycle'; +import { URI as uri } from 'vs/base/common/uri'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { Range } from 'vs/editor/common/core/range'; import { ILanguageService } from 'vs/editor/common/languages/language'; +import { OverviewRulerLane } from 'vs/editor/common/model'; import { LanguageService } from 'vs/editor/common/services/languageService'; -import { createMockDebugModel } from 'vs/workbench/contrib/debug/test/browser/mockDebugModel'; -import { MockDebugService } from 'vs/workbench/contrib/debug/test/common/mockDebug'; +import { createTextModel } from 'vs/editor/test/common/testTextModel'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { NullLogService } from 'vs/platform/log/common/log'; +import { StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; +import { createBreakpointDecorations } from 'vs/workbench/contrib/debug/browser/breakpointEditorContribution'; +import { getBreakpointMessageAndIcon, getExpandedBodySize } from 'vs/workbench/contrib/debug/browser/breakpointsView'; +import { IBreakpointData, IBreakpointUpdateData, IDebugService, State } from 'vs/workbench/contrib/debug/common/debug'; +import { Breakpoint, DebugModel } from 'vs/workbench/contrib/debug/common/debugModel'; +import { createTestSession } from 'vs/workbench/contrib/debug/test/browser/callStack.test'; +import { createMockDebugModel, mockUriIdentityService } from 'vs/workbench/contrib/debug/test/browser/mockDebugModel'; +import { MockDebugService, MockDebugStorage } from 'vs/workbench/contrib/debug/test/common/mockDebug'; +import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; -function addBreakpointsAndCheckEvents(model: DebugModel, uri: uri, data: IBreakpointData[]): void { +function addBreakpointsAndCheckEvents(model: DebugModel, uri: uri, data: IBreakpointData[]) { let eventCount = 0; const toDispose = model.onDidChangeBreakpoints(e => { assert.strictEqual(e?.sessionOnly, false); @@ -37,22 +41,26 @@ function addBreakpointsAndCheckEvents(model: DebugModel, uri: uri, data: IBreakp assert.strictEqual((e!.added![i] as Breakpoint).lineNumber, data[i].lineNumber); } }); - model.addBreakpoints(uri, data); + const bps = model.addBreakpoints(uri, data); assert.strictEqual(eventCount, 1); + return bps; } suite('Debug - Breakpoints', () => { let model: DebugModel; - const disposables = new DisposableStore(); + let disposables: DisposableStore; setup(() => { - model = createMockDebugModel(); + disposables = new DisposableStore(); + model = createMockDebugModel(disposables); }); teardown(() => { - disposables.clear(); + disposables.dispose(); }); + ensureNoDisposablesAreLeakedInTestSuite(); + // Breakpoints test('simple', () => { @@ -175,7 +183,7 @@ suite('Debug - Breakpoints', () => { const modelUri = uri.file('/myfolder/myfile.js'); addBreakpointsAndCheckEvents(model, modelUri, [{ lineNumber: 5, enabled: true, condition: 'x > 5' }, { lineNumber: 10, enabled: false }]); const breakpoints = model.getBreakpoints(); - const session = createTestSession(model); + const session = disposables.add(createTestSession(model)); const data = new Map(); assert.strictEqual(breakpoints[0].lineNumber, 5); @@ -187,7 +195,7 @@ suite('Debug - Breakpoints', () => { assert.strictEqual(breakpoints[0].lineNumber, 5); assert.strictEqual(breakpoints[1].lineNumber, 50); - const session2 = createTestSession(model); + const session2 = disposables.add(createTestSession(model)); const data2 = new Map(); data2.set(breakpoints[0].getId(), { verified: true, line: 100 }); data2.set(breakpoints[1].getId(), { verified: true, line: 500 }); @@ -212,7 +220,7 @@ suite('Debug - Breakpoints', () => { test('exception breakpoints', () => { let eventCount = 0; - model.onDidChangeBreakpoints(() => eventCount++); + disposables.add(model.onDidChangeBreakpoints(() => eventCount++)); model.setExceptionBreakpointsForSession("session-id-1", [{ filter: 'uncaught', label: 'UNCAUGHT', default: true }]); assert.strictEqual(eventCount, 1); let exceptionBreakpoints = model.getExceptionBreakpointsForSession("session-id-1"); @@ -245,7 +253,7 @@ suite('Debug - Breakpoints', () => { test('exception breakpoints multiple sessions', () => { let eventCount = 0; - model.onDidChangeBreakpoints(() => eventCount++); + disposables.add(model.onDidChangeBreakpoints(() => eventCount++)); model.setExceptionBreakpointsForSession("session-id-4", [{ filter: 'uncaught', label: 'UNCAUGHT', default: true }, { filter: 'caught', label: 'CAUGHT' }]); model.setExceptionBreakpointFallbackSession("session-id-4"); @@ -286,7 +294,7 @@ suite('Debug - Breakpoints', () => { test('instruction breakpoints', () => { let eventCount = 0; - model.onDidChangeBreakpoints(() => eventCount++); + disposables.add(model.onDidChangeBreakpoints(() => eventCount++)); //address: string, offset: number, condition?: string, hitCondition?: string model.addInstructionBreakpoint('0xCCCCFFFF', 0); @@ -308,7 +316,7 @@ suite('Debug - Breakpoints', () => { test('data breakpoints', () => { let eventCount = 0; - model.onDidChangeBreakpoints(() => eventCount++); + disposables.add(model.onDidChangeBreakpoints(() => eventCount++)); model.addDataBreakpoint('label', 'id', true, ['read'], 'read'); model.addDataBreakpoint('second', 'secondId', false, ['readWrite'], 'readWrite'); @@ -432,4 +440,31 @@ suite('Debug - Breakpoints', () => { textModel.dispose(); instantiationService.dispose(); }); + + test('updates when storage changes', () => { + const storage1 = disposables.add(new TestStorageService()); + const debugStorage1 = disposables.add(new MockDebugStorage(storage1)); + const model1 = disposables.add(new DebugModel(debugStorage1, { isDirty: (e: any) => false }, mockUriIdentityService, new NullLogService())); + + // 1. create breakpoints in the first model + const modelUri = uri.file('/myfolder/my file first.js'); + const first = [ + { lineNumber: 1, enabled: true, condition: 'x > 5' }, + { lineNumber: 2, column: 4, enabled: false }, + ]; + + addBreakpointsAndCheckEvents(model1, modelUri, first); + debugStorage1.storeBreakpoints(model1); + const stored = storage1.get('debug.breakpoint', StorageScope.WORKSPACE); + + // 2. hydrate a new model and ensure external breakpoints get applied + const storage2 = disposables.add(new TestStorageService()); + const model2 = disposables.add(new DebugModel(disposables.add(new MockDebugStorage(storage2)), { isDirty: (e: any) => false }, mockUriIdentityService, new NullLogService())); + storage2.store('debug.breakpoint', stored, StorageScope.WORKSPACE, StorageTarget.USER, /* external= */ true); + assert.deepStrictEqual(model2.getBreakpoints().map(b => b.getId()), model1.getBreakpoints().map(b => b.getId())); + + // 3. ensure non-external changes are ignored + storage2.store('debug.breakpoint', '[]', StorageScope.WORKSPACE, StorageTarget.USER, /* external= */ false); + assert.deepStrictEqual(model2.getBreakpoints().map(b => b.getId()), model1.getBreakpoints().map(b => b.getId())); + }); }); diff --git a/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts b/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts index c6158426173..9a9f2a09d63 100644 --- a/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts @@ -4,24 +4,26 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { DebugModel, StackFrame, Thread } from 'vs/workbench/contrib/debug/common/debugModel'; import * as sinon from 'sinon'; -import { createMockDebugModel, mockUriIdentityService } from 'vs/workbench/contrib/debug/test/browser/mockDebugModel'; -import { Source } from 'vs/workbench/contrib/debug/common/debugSource'; -import { DebugSession } from 'vs/workbench/contrib/debug/browser/debugSession'; -import { Range } from 'vs/editor/common/core/range'; -import { IDebugSessionOptions, State, IDebugService } from 'vs/workbench/contrib/debug/common/debug'; -import { createDecorationsForStackFrame } from 'vs/workbench/contrib/debug/browser/callStackEditorContribution'; -import { Constants } from 'vs/base/common/uint'; -import { getContext, getContextForContributedActions, getSpecificSourceName } from 'vs/workbench/contrib/debug/browser/callStackView'; -import { getStackFrameThreadAndSessionToFocus } from 'vs/workbench/contrib/debug/browser/debugService'; -import { generateUuid } from 'vs/base/common/uuid'; -import { debugStackframe, debugStackframeFocused } from 'vs/workbench/contrib/debug/browser/debugIcons'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { ThemeIcon } from 'vs/base/common/themables'; +import { Constants } from 'vs/base/common/uint'; +import { generateUuid } from 'vs/base/common/uuid'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { Range } from 'vs/editor/common/core/range'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; -import { MockRawSession } from 'vs/workbench/contrib/debug/test/common/mockDebug'; import { NullLogService } from 'vs/platform/log/common/log'; +import { createDecorationsForStackFrame } from 'vs/workbench/contrib/debug/browser/callStackEditorContribution'; +import { getContext, getContextForContributedActions, getSpecificSourceName } from 'vs/workbench/contrib/debug/browser/callStackView'; +import { debugStackframe, debugStackframeFocused } from 'vs/workbench/contrib/debug/browser/debugIcons'; +import { getStackFrameThreadAndSessionToFocus } from 'vs/workbench/contrib/debug/browser/debugService'; +import { DebugSession } from 'vs/workbench/contrib/debug/browser/debugSession'; +import { IDebugService, IDebugSessionOptions, State } from 'vs/workbench/contrib/debug/common/debug'; +import { DebugModel, StackFrame, Thread } from 'vs/workbench/contrib/debug/common/debugModel'; +import { Source } from 'vs/workbench/contrib/debug/common/debugSource'; +import { createMockDebugModel, mockUriIdentityService } from 'vs/workbench/contrib/debug/test/browser/mockDebugModel'; +import { MockRawSession } from 'vs/workbench/contrib/debug/test/common/mockDebug'; const mockWorkspaceContextService = { getWorkspace: () => { @@ -70,22 +72,28 @@ function createTwoStackFrames(session: DebugSession): { firstStackFrame: StackFr suite('Debug - CallStack', () => { let model: DebugModel; let mockRawSession: MockRawSession; + let disposables: DisposableStore; setup(() => { - model = createMockDebugModel(); + disposables = new DisposableStore(); + model = createMockDebugModel(disposables); mockRawSession = new MockRawSession(); }); teardown(() => { + disposables.dispose(); sinon.restore(); }); + ensureNoDisposablesAreLeakedInTestSuite(); + // Threads test('threads simple', () => { const threadId = 1; const threadName = 'firstThread'; const session = createTestSession(model); + disposables.add(session); model.addSession(session); assert.strictEqual(model.getSessions(true).length, 1); @@ -113,6 +121,7 @@ suite('Debug - CallStack', () => { // Add the threads const session = createTestSession(model); + disposables.add(session); model.addSession(session); session['raw'] = mockRawSession; @@ -193,6 +202,7 @@ suite('Debug - CallStack', () => { // Add the threads const session = createTestSession(model); + disposables.add(session); model.addSession(session); session['raw'] = mockRawSession; @@ -246,6 +256,7 @@ suite('Debug - CallStack', () => { const runningThreadName = 'runningThread'; const stoppedReason = 'breakpoint'; const session = createTestSession(model); + disposables.add(session); model.addSession(session); session['raw'] = mockRawSession; @@ -318,6 +329,7 @@ suite('Debug - CallStack', () => { test('stack frame get specific source name', () => { const session = createTestSession(model); + disposables.add(session); model.addSession(session); const { firstStackFrame, secondStackFrame } = createTwoStackFrames(session); @@ -327,6 +339,7 @@ suite('Debug - CallStack', () => { test('stack frame toString()', () => { const session = createTestSession(model); + disposables.add(session); const thread = new Thread(session, 'mockthread', 1); const firstSource = new Source({ name: 'internalModule.js', @@ -342,17 +355,17 @@ suite('Debug - CallStack', () => { }); test('debug child sessions are added in correct order', () => { - const session = createTestSession(model); + const session = disposables.add(createTestSession(model)); model.addSession(session); - const secondSession = createTestSession(model, 'mockSession2'); + const secondSession = disposables.add(createTestSession(model, 'mockSession2')); model.addSession(secondSession); - const firstChild = createTestSession(model, 'firstChild', { parentSession: session }); + const firstChild = disposables.add(createTestSession(model, 'firstChild', { parentSession: session })); model.addSession(firstChild); - const secondChild = createTestSession(model, 'secondChild', { parentSession: session }); + const secondChild = disposables.add(createTestSession(model, 'secondChild', { parentSession: session })); model.addSession(secondChild); - const thirdSession = createTestSession(model, 'mockSession3'); + const thirdSession = disposables.add(createTestSession(model, 'mockSession3')); model.addSession(thirdSession); - const anotherChild = createTestSession(model, 'secondChild', { parentSession: secondSession }); + const anotherChild = disposables.add(createTestSession(model, 'secondChild', { parentSession: secondSession })); model.addSession(anotherChild); const sessions = model.getSessions(); @@ -366,6 +379,7 @@ suite('Debug - CallStack', () => { test('decorations', () => { const session = createTestSession(model); + disposables.add(session); model.addSession(session); const { firstStackFrame, secondStackFrame } = createTwoStackFrames(session); let decorations = createDecorationsForStackFrame(firstStackFrame, true, false); @@ -398,6 +412,7 @@ suite('Debug - CallStack', () => { test('contexts', () => { const session = createTestSession(model); + disposables.add(session); model.addSession(session); const { firstStackFrame, secondStackFrame } = createTwoStackFrames(session); let context = getContext(firstStackFrame); @@ -436,8 +451,10 @@ suite('Debug - CallStack', () => { return State.Stopped; } }(generateUuid(), { resolved: { name: 'stoppedSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, undefined, undefined!, undefined!, undefined!, undefined!, undefined!, mockWorkspaceContextService, undefined!, undefined!, undefined!, mockUriIdentityService, new TestInstantiationService(), undefined!, undefined!, new NullLogService()); + disposables.add(session); const runningSession = createTestSession(model); + disposables.add(runningSession); model.addSession(runningSession); model.addSession(session); diff --git a/src/vs/workbench/contrib/debug/test/browser/debugANSIHandling.test.ts b/src/vs/workbench/contrib/debug/test/browser/debugANSIHandling.test.ts index c3b5d88bf80..baade0af6d2 100644 --- a/src/vs/workbench/contrib/debug/test/browser/debugANSIHandling.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/debugANSIHandling.test.ts @@ -4,20 +4,21 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { generateUuid } from 'vs/base/common/uuid'; -import { appendStylizedStringToContainer, handleANSIOutput, calcANSI8bitColor } from 'vs/workbench/contrib/debug/browser/debugANSIHandling'; -import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; -import { workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; -import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector'; import { Color, RGBA } from 'vs/base/common/color'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { TestThemeService, TestColorTheme } from 'vs/platform/theme/test/common/testThemeService'; -import { ansiColorMap } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; -import { DebugModel } from 'vs/workbench/contrib/debug/common/debugModel'; -import { DebugSession } from 'vs/workbench/contrib/debug/browser/debugSession'; -import { createTestSession } from 'vs/workbench/contrib/debug/test/browser/callStack.test'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { generateUuid } from 'vs/base/common/uuid'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { TestColorTheme, TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; +import { appendStylizedStringToContainer, calcANSI8bitColor, handleANSIOutput } from 'vs/workbench/contrib/debug/browser/debugANSIHandling'; +import { DebugSession } from 'vs/workbench/contrib/debug/browser/debugSession'; +import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector'; +import { DebugModel } from 'vs/workbench/contrib/debug/common/debugModel'; +import { createTestSession } from 'vs/workbench/contrib/debug/test/browser/callStack.test'; import { createMockDebugModel } from 'vs/workbench/contrib/debug/test/browser/mockDebugModel'; +import { ansiColorMap } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; +import { workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; suite('Debug - ANSI Handling', () => { @@ -32,7 +33,7 @@ suite('Debug - ANSI Handling', () => { */ setup(() => { disposables = new DisposableStore(); - model = createMockDebugModel(); + model = createMockDebugModel(disposables); session = createTestSession(model); const instantiationService: TestInstantiationService = workbenchInstantiationService(undefined, disposables); @@ -50,6 +51,8 @@ suite('Debug - ANSI Handling', () => { disposables.dispose(); }); + ensureNoDisposablesAreLeakedInTestSuite(); + test('appendStylizedStringToContainer', () => { const root: HTMLSpanElement = document.createElement('span'); let child: Node; diff --git a/src/vs/workbench/contrib/debug/test/browser/debugConfigurationManager.test.ts b/src/vs/workbench/contrib/debug/test/browser/debugConfigurationManager.test.ts index 287c489a781..a63840db4e6 100644 --- a/src/vs/workbench/contrib/debug/test/browser/debugConfigurationManager.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/debugConfigurationManager.test.ts @@ -5,27 +5,28 @@ import * as assert from 'assert'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { DisposableStore } from 'vs/base/common/lifecycle'; import { Event } from 'vs/base/common/event'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService'; import { FileService } from 'vs/platform/files/common/fileService'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { NullLogService } from 'vs/platform/log/common/log'; import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService'; import { ConfigurationManager } from 'vs/workbench/contrib/debug/browser/debugConfigurationManager'; import { DebugConfigurationProviderTriggerKind, IAdapterManager, IConfig, IDebugAdapterExecutable, IDebugSession } from 'vs/workbench/contrib/debug/common/debug'; +import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { TestHistoryService, TestQuickInputService } from 'vs/workbench/test/browser/workbenchTestServices'; import { TestContextService, TestExtensionService, TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; -import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; -import { URI } from 'vs/base/common/uri'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; suite('debugConfigurationManager', () => { const configurationProviderType = 'custom-type'; let _debugConfigurationManager: ConfigurationManager; - const disposables = new DisposableStore(); + let disposables: DisposableStore; const adapterManager = { getDebugAdapterDescriptor(session: IDebugSession, config: IConfig): Promise { @@ -47,6 +48,7 @@ suite('debugConfigurationManager', () => { const configurationService = new TestConfigurationService(); setup(() => { + disposables = new DisposableStore(); const fileService = disposables.add(new FileService(new NullLogService())); const instantiationService = disposables.add(new TestInstantiationService(new ServiceCollection([IPreferencesService, preferencesService], [IConfigurationService, configurationService]))); _debugConfigurationManager = new ConfigurationManager( @@ -62,6 +64,10 @@ suite('debugConfigurationManager', () => { new ContextKeyService(configurationService)); }); + teardown(() => disposables.dispose()); + + ensureNoDisposablesAreLeakedInTestSuite(); + test('resolves configuration based on type', async () => { disposables.add(_debugConfigurationManager.registerDebugConfigurationProvider({ type: configurationProviderType, diff --git a/src/vs/workbench/contrib/debug/test/browser/debugHover.test.ts b/src/vs/workbench/contrib/debug/test/browser/debugHover.test.ts index 62e17b6dcc2..f71a916f0c0 100644 --- a/src/vs/workbench/contrib/debug/test/browser/debugHover.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/debugHover.test.ts @@ -4,6 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { NullLogService } from 'vs/platform/log/common/log'; import { findExpressionInStackFrame } from 'vs/workbench/contrib/debug/browser/debugHover'; import type { IExpression, IScope } from 'vs/workbench/contrib/debug/common/debug'; @@ -13,9 +15,20 @@ import { createTestSession } from 'vs/workbench/contrib/debug/test/browser/callS import { createMockDebugModel, mockUriIdentityService } from 'vs/workbench/contrib/debug/test/browser/mockDebugModel'; suite('Debug - Hover', () => { + let disposables: DisposableStore; + setup(() => { + disposables = new DisposableStore(); + }); + + teardown(() => { + disposables.dispose(); + }); + + ensureNoDisposablesAreLeakedInTestSuite(); + test('find expression in stack frame', async () => { - const model = createMockDebugModel(); - const session = createTestSession(model); + const model = createMockDebugModel(disposables); + const session = disposables.add(createTestSession(model)); const thread = new class extends Thread { public override getCallStack(): StackFrame[] { diff --git a/src/vs/workbench/contrib/debug/test/browser/debugMemory.test.ts b/src/vs/workbench/contrib/debug/test/browser/debugMemory.test.ts index 499580f8416..7647820f3d0 100644 --- a/src/vs/workbench/contrib/debug/test/browser/debugMemory.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/debugMemory.test.ts @@ -7,6 +7,7 @@ import * as assert from 'assert'; import { decodeBase64, encodeBase64, VSBuffer } from 'vs/base/common/buffer'; import { Emitter } from 'vs/base/common/event'; import { mockObject, MockObject } from 'vs/base/test/common/mock'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { MemoryRangeType } from 'vs/workbench/contrib/debug/common/debug'; import { MemoryRegion } from 'vs/workbench/contrib/debug/common/debugModel'; import { MockSession } from 'vs/workbench/contrib/debug/test/common/mockDebug'; @@ -20,6 +21,8 @@ suite('Debug - Memory', () => { success: true, }; + ensureNoDisposablesAreLeakedInTestSuite(); + suite('MemoryRegion', () => { let memory: VSBuffer; let unreadable: number; diff --git a/src/vs/workbench/contrib/debug/test/browser/debugSource.test.ts b/src/vs/workbench/contrib/debug/test/browser/debugSource.test.ts index be735728c90..672ae3c3061 100644 --- a/src/vs/workbench/contrib/debug/test/browser/debugSource.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/debugSource.test.ts @@ -4,14 +4,17 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { URI as uri } from 'vs/base/common/uri'; -import { Source } from 'vs/workbench/contrib/debug/common/debugSource'; import { isWindows } from 'vs/base/common/platform'; -import { mockUriIdentityService } from 'vs/workbench/contrib/debug/test/browser/mockDebugModel'; +import { URI as uri } from 'vs/base/common/uri'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { NullLogService } from 'vs/platform/log/common/log'; +import { Source } from 'vs/workbench/contrib/debug/common/debugSource'; +import { mockUriIdentityService } from 'vs/workbench/contrib/debug/test/browser/mockDebugModel'; suite('Debug - Source', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + test('from raw source', () => { const source = new Source({ name: 'zz', diff --git a/src/vs/workbench/contrib/debug/test/browser/debugUtils.test.ts b/src/vs/workbench/contrib/debug/test/browser/debugUtils.test.ts index 3b2b6003495..585497db087 100644 --- a/src/vs/workbench/contrib/debug/test/browser/debugUtils.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/debugUtils.test.ts @@ -4,10 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { formatPII, getExactExpressionStartAndEnd, getVisibleAndSorted } from 'vs/workbench/contrib/debug/common/debugUtils'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { IConfig } from 'vs/workbench/contrib/debug/common/debug'; +import { formatPII, getExactExpressionStartAndEnd, getVisibleAndSorted } from 'vs/workbench/contrib/debug/common/debugUtils'; suite('Debug - Utils', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + test('formatPII', () => { assert.strictEqual(formatPII('Foo Bar', false, {}), 'Foo Bar'); assert.strictEqual(formatPII('Foo {key} Bar', false, {}), 'Foo {key} Bar'); diff --git a/src/vs/workbench/contrib/debug/test/browser/debugViewModel.test.ts b/src/vs/workbench/contrib/debug/test/browser/debugViewModel.test.ts index 3b232aab21f..d42de786cb5 100644 --- a/src/vs/workbench/contrib/debug/test/browser/debugViewModel.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/debugViewModel.test.ts @@ -4,13 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { ViewModel } from 'vs/workbench/contrib/debug/common/debugViewModel'; -import { StackFrame, Expression, Thread } from 'vs/workbench/contrib/debug/common/debugModel'; -import { mockUriIdentityService } from 'vs/workbench/contrib/debug/test/browser/mockDebugModel'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; -import { Source } from 'vs/workbench/contrib/debug/common/debugSource'; -import { MockSession } from 'vs/workbench/contrib/debug/test/common/mockDebug'; import { NullLogService } from 'vs/platform/log/common/log'; +import { Expression, StackFrame, Thread } from 'vs/workbench/contrib/debug/common/debugModel'; +import { Source } from 'vs/workbench/contrib/debug/common/debugSource'; +import { ViewModel } from 'vs/workbench/contrib/debug/common/debugViewModel'; +import { mockUriIdentityService } from 'vs/workbench/contrib/debug/test/browser/mockDebugModel'; +import { MockSession } from 'vs/workbench/contrib/debug/test/common/mockDebug'; suite('Debug - View Model', () => { let model: ViewModel; @@ -19,6 +20,8 @@ suite('Debug - View Model', () => { model = new ViewModel(new MockContextKeyService()); }); + ensureNoDisposablesAreLeakedInTestSuite(); + test('focused stack frame', () => { assert.strictEqual(model.focusedStackFrame, undefined); assert.strictEqual(model.focusedThread, undefined); diff --git a/src/vs/workbench/contrib/debug/test/browser/linkDetector.test.ts b/src/vs/workbench/contrib/debug/test/browser/linkDetector.test.ts index 3e7ed14e6f8..a452e1a0e97 100644 --- a/src/vs/workbench/contrib/debug/test/browser/linkDetector.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/linkDetector.test.ts @@ -4,14 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; -import { workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; -import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector'; -import { isWindows } from 'vs/base/common/platform'; -import { WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; -import { URI } from 'vs/base/common/uri'; -import { ITunnelService } from 'vs/platform/tunnel/common/tunnel'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { isWindows } from 'vs/base/common/platform'; +import { URI } from 'vs/base/common/uri'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { ITunnelService } from 'vs/platform/tunnel/common/tunnel'; +import { WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector'; +import { workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; suite('Debug - Link Detector', () => { @@ -32,6 +33,8 @@ suite('Debug - Link Detector', () => { disposables.dispose(); }); + ensureNoDisposablesAreLeakedInTestSuite(); + /** * Assert that a given Element is an anchor element. * diff --git a/src/vs/workbench/contrib/debug/test/browser/mockDebugModel.ts b/src/vs/workbench/contrib/debug/test/browser/mockDebugModel.ts index 29c7e61f148..9e736c215b1 100644 --- a/src/vs/workbench/contrib/debug/test/browser/mockDebugModel.ts +++ b/src/vs/workbench/contrib/debug/test/browser/mockDebugModel.ts @@ -3,15 +3,19 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { DisposableStore } from 'vs/base/common/lifecycle'; import { NullLogService } from 'vs/platform/log/common/log'; import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService'; import { DebugModel } from 'vs/workbench/contrib/debug/common/debugModel'; import { MockDebugStorage } from 'vs/workbench/contrib/debug/test/common/mockDebug'; import { TestFileService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; const fileService = new TestFileService(); export const mockUriIdentityService = new UriIdentityService(fileService); -export function createMockDebugModel(): DebugModel { - return new DebugModel(new MockDebugStorage(), { isDirty: (e: any) => false }, mockUriIdentityService, new NullLogService()); +export function createMockDebugModel(disposable: DisposableStore): DebugModel { + const storage = disposable.add(new TestStorageService()); + const debugStorage = disposable.add(new MockDebugStorage(storage)); + return disposable.add(new DebugModel(debugStorage, { isDirty: (e: any) => false }, mockUriIdentityService, new NullLogService())); } diff --git a/src/vs/workbench/contrib/debug/test/browser/rawDebugSession.test.ts b/src/vs/workbench/contrib/debug/test/browser/rawDebugSession.test.ts index 1ccdc5bcefe..465438ad2c5 100644 --- a/src/vs/workbench/contrib/debug/test/browser/rawDebugSession.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/rawDebugSession.test.ts @@ -4,7 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { mock, mockObject } from 'vs/base/test/common/mock'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -14,13 +16,24 @@ import { IDebugger } from 'vs/workbench/contrib/debug/common/debug'; import { MockDebugAdapter } from 'vs/workbench/contrib/debug/test/common/mockDebug'; suite('RawDebugSession', () => { + let disposables: DisposableStore; + setup(() => { + disposables = new DisposableStore(); + }); + + teardown(() => { + disposables.dispose(); + }); + + ensureNoDisposablesAreLeakedInTestSuite(); + function createTestObjects() { const debugAdapter = new MockDebugAdapter(); const dbgr = mockObject()({ type: 'mock-debug' }); - new RawDebugSession( + const session = new RawDebugSession( debugAdapter, dbgr as any as IDebugger, 'sessionId', @@ -29,6 +42,9 @@ suite('RawDebugSession', () => { new (mock()), new (mock()), new (mock())); + disposables.add(session); + disposables.add(debugAdapter); + return { debugAdapter, dbgr }; } diff --git a/src/vs/workbench/contrib/debug/test/browser/repl.test.ts b/src/vs/workbench/contrib/debug/test/browser/repl.test.ts index a04f2ed1963..b1dbc030a48 100644 --- a/src/vs/workbench/contrib/debug/test/browser/repl.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/repl.test.ts @@ -5,30 +5,41 @@ import * as assert from 'assert'; -import severity from 'vs/base/common/severity'; -import { DebugModel, StackFrame, Thread } from 'vs/workbench/contrib/debug/common/debugModel'; -import { createMockDebugModel } from 'vs/workbench/contrib/debug/test/browser/mockDebugModel'; -import { ReplOutputElement, RawObjectReplElement, ReplEvaluationInput, ReplModel, ReplEvaluationResult, ReplGroup, ReplVariableElement } from 'vs/workbench/contrib/debug/common/replModel'; -import { RawDebugSession } from 'vs/workbench/contrib/debug/browser/rawDebugSession'; -import { timeout } from 'vs/base/common/async'; -import { createTestSession } from 'vs/workbench/contrib/debug/test/browser/callStack.test'; -import { ReplFilter } from 'vs/workbench/contrib/debug/browser/replFilter'; import { TreeVisibility } from 'vs/base/browser/ui/tree/tree'; +import { timeout } from 'vs/base/common/async'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import severity from 'vs/base/common/severity'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { RawDebugSession } from 'vs/workbench/contrib/debug/browser/rawDebugSession'; +import { ReplFilter } from 'vs/workbench/contrib/debug/browser/replFilter'; +import { DebugModel, StackFrame, Thread } from 'vs/workbench/contrib/debug/common/debugModel'; +import { RawObjectReplElement, ReplEvaluationInput, ReplEvaluationResult, ReplGroup, ReplModel, ReplOutputElement, ReplVariableElement } from 'vs/workbench/contrib/debug/common/replModel'; +import { createTestSession } from 'vs/workbench/contrib/debug/test/browser/callStack.test'; +import { createMockDebugModel } from 'vs/workbench/contrib/debug/test/browser/mockDebugModel'; import { MockDebugAdapter, MockRawSession } from 'vs/workbench/contrib/debug/test/common/mockDebug'; suite('Debug - REPL', () => { let model: DebugModel; let rawSession: MockRawSession; const configurationService = new TestConfigurationService({ debug: { console: { collapseIdenticalLines: true } } }); + let disposables: DisposableStore; + setup(() => { - model = createMockDebugModel(); + disposables = new DisposableStore(); + model = createMockDebugModel(disposables); rawSession = new MockRawSession(); }); + teardown(() => { + disposables.dispose(); + }); + + ensureNoDisposablesAreLeakedInTestSuite(); + test('repl output', () => { - const session = createTestSession(model); + const session = disposables.add(createTestSession(model)); const repl = new ReplModel(configurationService); repl.appendToRepl(session, { output: 'first line\n', sev: severity.Error }); repl.appendToRepl(session, { output: 'second line ', sev: severity.Error }); @@ -86,7 +97,7 @@ suite('Debug - REPL', () => { }); test('repl output count', () => { - const session = createTestSession(model); + const session = disposables.add(createTestSession(model)); const repl = new ReplModel(configurationService); repl.appendToRepl(session, { output: 'first line\n', sev: severity.Info }); repl.appendToRepl(session, { output: 'first line\n', sev: severity.Info }); @@ -108,14 +119,14 @@ suite('Debug - REPL', () => { test('repl merging', () => { // 'mergeWithParent' should be ignored when there is no parent. - const parent = createTestSession(model, 'parent', { repl: 'mergeWithParent' }); - const child1 = createTestSession(model, 'child1', { parentSession: parent, repl: 'separate' }); - const child2 = createTestSession(model, 'child2', { parentSession: parent, repl: 'mergeWithParent' }); - const grandChild = createTestSession(model, 'grandChild', { parentSession: child2, repl: 'mergeWithParent' }); - const child3 = createTestSession(model, 'child3', { parentSession: parent }); + const parent = disposables.add(createTestSession(model, 'parent', { repl: 'mergeWithParent' })); + const child1 = disposables.add(createTestSession(model, 'child1', { parentSession: parent, repl: 'separate' })); + const child2 = disposables.add(createTestSession(model, 'child2', { parentSession: parent, repl: 'mergeWithParent' })); + const grandChild = disposables.add(createTestSession(model, 'grandChild', { parentSession: child2, repl: 'mergeWithParent' })); + const child3 = disposables.add(createTestSession(model, 'child3', { parentSession: parent })); let parentChanges = 0; - parent.onDidChangeReplElements(() => ++parentChanges); + disposables.add(parent.onDidChangeReplElements(() => ++parentChanges)); parent.appendToRepl({ output: '1\n', sev: severity.Info }); assert.strictEqual(parentChanges, 1); @@ -151,7 +162,7 @@ suite('Debug - REPL', () => { }); test('repl expressions', () => { - const session = createTestSession(model); + const session = disposables.add(createTestSession(model)); assert.strictEqual(session.getReplElements().length, 0); model.addSession(session); @@ -173,11 +184,11 @@ suite('Debug - REPL', () => { }); test('repl ordering', async () => { - const session = createTestSession(model); + const session = disposables.add(createTestSession(model)); model.addSession(session); const adapter = new MockDebugAdapter(); - const raw = new RawDebugSession(adapter, undefined!, '', '', undefined!, undefined!, undefined!, undefined!,); + const raw = disposables.add(new RawDebugSession(adapter, undefined!, '', '', undefined!, undefined!, undefined!, undefined!,)); session.initializeForTest(raw); await session.addReplExpression(undefined, 'before.1'); @@ -195,7 +206,7 @@ suite('Debug - REPL', () => { }); test('repl groups', async () => { - const session = createTestSession(model); + const session = disposables.add(createTestSession(model)); const repl = new ReplModel(configurationService); repl.appendToRepl(session, { output: 'first global line', sev: severity.Info }); @@ -233,7 +244,7 @@ suite('Debug - REPL', () => { }); test('repl filter', async () => { - const session = createTestSession(model); + const session = disposables.add(createTestSession(model)); const repl = new ReplModel(configurationService); const replFilter = new ReplFilter(); diff --git a/src/vs/workbench/contrib/debug/test/browser/watch.test.ts b/src/vs/workbench/contrib/debug/test/browser/watch.test.ts index 5ae664ed05d..17fbff3be13 100644 --- a/src/vs/workbench/contrib/debug/test/browser/watch.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/watch.test.ts @@ -4,7 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { Expression, DebugModel } from 'vs/workbench/contrib/debug/common/debugModel'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { DebugModel, Expression } from 'vs/workbench/contrib/debug/common/debugModel'; import { createMockDebugModel } from 'vs/workbench/contrib/debug/test/browser/mockDebugModel'; // Expressions @@ -19,13 +21,20 @@ function assertWatchExpressions(watchExpressions: Expression[], expectedName: st } suite('Debug - Watch', () => { - let model: DebugModel; + let disposables: DisposableStore; setup(() => { - model = createMockDebugModel(); + disposables = new DisposableStore(); + model = createMockDebugModel(disposables); }); + teardown(() => { + disposables.dispose(); + }); + + ensureNoDisposablesAreLeakedInTestSuite(); + test('watch expressions', () => { assert.strictEqual(model.getWatchExpressions().length, 0); model.addWatchExpression('console'); diff --git a/src/vs/workbench/contrib/debug/test/common/abstractDebugAdapter.test.ts b/src/vs/workbench/contrib/debug/test/common/abstractDebugAdapter.test.ts index f4e048c7b1e..2246b7fa248 100644 --- a/src/vs/workbench/contrib/debug/test/common/abstractDebugAdapter.test.ts +++ b/src/vs/workbench/contrib/debug/test/common/abstractDebugAdapter.test.ts @@ -5,9 +5,12 @@ import * as assert from 'assert'; import { timeout } from 'vs/base/common/async'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { MockDebugAdapter } from 'vs/workbench/contrib/debug/test/common/mockDebug'; suite('Debug - AbstractDebugAdapter', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + suite('event ordering', () => { let adapter: MockDebugAdapter; let output: string[]; diff --git a/src/vs/workbench/contrib/debug/test/common/debugModel.test.ts b/src/vs/workbench/contrib/debug/test/common/debugModel.test.ts index 779f301a4eb..a39f590b616 100644 --- a/src/vs/workbench/contrib/debug/test/common/debugModel.test.ts +++ b/src/vs/workbench/contrib/debug/test/common/debugModel.test.ts @@ -5,12 +5,17 @@ import * as assert from 'assert'; import { DeferredPromise } from 'vs/base/common/async'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { mockObject } from 'vs/base/test/common/mock'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { NullLogService } from 'vs/platform/log/common/log'; import { DebugModel, ExceptionBreakpoint, FunctionBreakpoint, Thread } from 'vs/workbench/contrib/debug/common/debugModel'; import { MockDebugStorage } from 'vs/workbench/contrib/debug/test/common/mockDebug'; +import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; suite('DebugModel', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + suite('FunctionBreakpoint', () => { test('Id is saved', () => { const fbp = new FunctionBreakpoint('function', true, 'hit condition', 'condition', 'log message'); @@ -42,7 +47,10 @@ suite('DebugModel', () => { }); fakeThread.getId.returns(1); - const model = new DebugModel(new MockDebugStorage(), { isDirty: (e: any) => false }, undefined!, new NullLogService()); + const disposable = new DisposableStore(); + const storage = disposable.add(new TestStorageService()); + const model = new DebugModel(disposable.add(new MockDebugStorage(storage)), { isDirty: (e: any) => false }, undefined!, new NullLogService()); + disposable.add(model); let top1Resolved = false; let whole1Resolved = false; @@ -70,6 +78,8 @@ suite('DebugModel', () => { await wholeStackDeferred.complete(); await result1.wholeCallStack; await result2.wholeCallStack; + + disposable.dispose(); }); }); }); diff --git a/src/vs/workbench/contrib/debug/test/common/mockDebug.ts b/src/vs/workbench/contrib/debug/test/common/mockDebug.ts index 5e8430947cf..30353a78a35 100644 --- a/src/vs/workbench/contrib/debug/test/common/mockDebug.ts +++ b/src/vs/workbench/contrib/debug/test/common/mockDebug.ts @@ -10,11 +10,11 @@ import { URI as uri } from 'vs/base/common/uri'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { ITextModel } from 'vs/editor/common/model'; import { NullLogService } from 'vs/platform/log/common/log'; +import { IStorageService } from 'vs/platform/storage/common/storage'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { AbstractDebugAdapter } from 'vs/workbench/contrib/debug/common/abstractDebugAdapter'; -import { AdapterEndEvent, IAdapterManager, IBreakpoint, IBreakpointData, IBreakpointUpdateData, IConfig, IConfigurationManager, IDataBreakpoint, IDebugger, IDebugModel, IDebugService, IDebugSession, IDebugSessionOptions, IEvaluate, IExceptionBreakpoint, IExceptionInfo, IExpression, IFunctionBreakpoint, IInstructionBreakpoint, ILaunch, IMemoryRegion, INewReplElementData, IRawModelUpdate, IRawStoppedDetails, IReplElement, IStackFrame, IThread, IViewModel, LoadedSourceEvent, State } from 'vs/workbench/contrib/debug/common/debug'; +import { AdapterEndEvent, IAdapterManager, IBreakpoint, IBreakpointData, IBreakpointUpdateData, IConfig, IConfigurationManager, IDataBreakpoint, IDebugModel, IDebugService, IDebugSession, IDebugSessionOptions, IDebugger, IExceptionBreakpoint, IExceptionInfo, IFunctionBreakpoint, IInstructionBreakpoint, ILaunch, IMemoryRegion, INewReplElementData, IRawModelUpdate, IRawStoppedDetails, IReplElement, IStackFrame, IThread, IViewModel, LoadedSourceEvent, State } from 'vs/workbench/contrib/debug/common/debug'; import { DebugCompoundRoot } from 'vs/workbench/contrib/debug/common/debugCompoundRoot'; -import { Breakpoint, DataBreakpoint, ExceptionBreakpoint, Expression, FunctionBreakpoint } from 'vs/workbench/contrib/debug/common/debugModel'; import { Source } from 'vs/workbench/contrib/debug/common/debugSource'; import { DebugStorage } from 'vs/workbench/contrib/debug/common/debugStorage'; @@ -666,34 +666,7 @@ export class MockDebugAdapter extends AbstractDebugAdapter { export class MockDebugStorage extends DebugStorage { - constructor() { - super(undefined as any, undefined as any, undefined as any, new NullLogService()); + constructor(storageService: IStorageService) { + super(storageService, undefined as any, undefined as any, new NullLogService()); } - - override loadBreakpoints(): Breakpoint[] { - return []; - } - - override loadFunctionBreakpoints(): FunctionBreakpoint[] { - return []; - } - - override loadExceptionBreakpoints(): ExceptionBreakpoint[] { - return []; - - } - - override loadDataBreakpoints(): DataBreakpoint[] { - return []; - - } - - override loadWatchExpressions(): Expression[] { - return []; - - } - - override storeWatchExpressions(_watchExpressions: (IExpression & IEvaluate)[]): void { } - - override storeBreakpoints(_debugModel: IDebugModel): void { } } From 74b272c93c9a25c81e782a461bfb4743acc4af3b Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 16 Aug 2023 12:08:56 -0700 Subject: [PATCH 078/223] get next/previous and accepting to work --- .../browser/inlineCompletionsController.ts | 5 +- .../browser/accessibilityContributions.ts | 79 ++++++++++++------- 2 files changed, 55 insertions(+), 29 deletions(-) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts index c4e567e07ce..51b2d4fc562 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts @@ -149,7 +149,7 @@ export class InlineCompletionsController extends Disposable { this._register(this.editor.onDidBlurEditorWidget(() => { // This is a hidden setting very useful for debugging - if (this.configurationService.getValue('editor.inlineSuggest.keepOnBlur') || + if (this.contextKeyService.getContextKeyValue('accessibleViewIsShown') || this.configurationService.getValue('editor.inlineSuggest.keepOnBlur') || editor.getOption(EditorOption.inlineSuggest).keepOnBlur) { return; } @@ -208,9 +208,10 @@ export class InlineCompletionsController extends Disposable { } private provideScreenReaderUpdate(content: string): void { + const accessibleViewShowing = this.contextKeyService.getContextKeyValue('accessibleViewIsShown'); const accessibleViewKeybinding = this._keybindingService.lookupKeybinding('editor.action.accessibleView'); let hint: string | undefined; - if (accessibleViewKeybinding && this.editor.getOption(EditorOption.inlineCompletionsAccessibilityVerbose)) { + if (!accessibleViewShowing && accessibleViewKeybinding && this.editor.getOption(EditorOption.inlineCompletionsAccessibilityVerbose)) { hint = localize('showAccessibleViewHint', "Inspect this in the accessible view ({0})", accessibleViewKeybinding.getAriaLabel()); } hint ? alert(content + ', ' + hint) : alert(content); diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts index 5d19ae5cff7..db3e3233ae0 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts @@ -282,39 +282,64 @@ export function alertFocusChange(index: number | undefined, length: number | und export class InlineCompletionsAccessibleViewContribution extends Disposable { static ID: 'inlineCompletionsAccessibleViewContribution'; private _options: IAccessibleViewOptions = { - ariaLabel: localize('inlineCompletionsAccessibleView', "Inline Completions Accessible View"), language: 'typescript', type: AccessibleViewType.View + ariaLabel: localize('inlineCompletionsAccessibleView', "Inline Completions Accessible View"), type: AccessibleViewType.View }; constructor() { super(); this._register(AccessibleViewAction.addImplementation(95, 'inline-completions', accessor => { const accessibleViewService = accessor.get(IAccessibleViewService); const codeEditorService = accessor.get(ICodeEditorService); - const editor = codeEditorService.getActiveCodeEditor() || codeEditorService.getFocusedCodeEditor(); - if (!editor) { - return false; - } - const model = InlineCompletionsController.get(editor)?.model.get(); - const state = model?.state.get(); - if (!state) { - return false; - } - const lineText = model?.textModel.getLineContent(state.ghostText.lineNumber); - if (!lineText) { - return false; - } - const content = state.ghostText.renderForScreenReader(lineText); - if (!content) { - return false; - } - accessibleViewService.show({ - verbositySettingKey: AccessibilityVerbositySettingId.InlineCompletions, - provideContent() { return content; }, - onClose() { - editor.focus(); - }, - options: this._options - }); - return true; + const show = () => { + const editor = codeEditorService.getActiveCodeEditor() || codeEditorService.getFocusedCodeEditor(); + if (!editor) { + return false; + } + const model = InlineCompletionsController.get(editor)?.model.get(); + const state = model?.state.get(); + if (!model || !state) { + return false; + } + const lineText = model.textModel.getLineContent(state.ghostText.lineNumber); + if (!lineText) { + return false; + } + + const ghostText = state.ghostText.renderForScreenReader(lineText); + if (!ghostText) { + return false; + } + this._options.language = editor.getModel()?.getLanguageId() ?? undefined; + accessibleViewService.show({ + verbositySettingKey: AccessibilityVerbositySettingId.InlineCompletions, + provideContent() { return lineText + ghostText; }, + onClose() { + editor.focus(); + }, + next() { + model.next().then(() => show()); + }, + previous() { + model.previous().then(() => show()); + }, + actions: [ + { + id: 'inlineCompletions.accept', + label: localize('inlineCompletions.accept', "Accept Completion"), + tooltip: localize('inlineCompletions.accept', "Accept Completion"), + run: () => { + alert('Accepted'); + model.accept(editor); + editor.focus(); + }, + class: ThemeIcon.asClassName(Codicon.check), + enabled: true + } + ], + options: this._options + }); + return true; + }; + return show(); }, ContextKeyExpr.and(InlineCompletionContextKeys.inlineSuggestionVisible, EditorContextKeys.focus, EditorContextKeys.hasCodeActionsProvider) ) ); From 4047859b0d493a371ccea1ba89af74f7af93c9ad Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 16 Aug 2023 12:23:49 -0700 Subject: [PATCH 079/223] Implement vscode command terminal quick fixes Part of #162950 --- .../api/browser/mainThreadTerminalService.ts | 7 +++-- .../workbench/api/common/extHost.api.impl.ts | 1 - src/vs/workbench/api/common/extHostTypes.ts | 5 +-- .../quickFix/browser/quickFix.ts | 14 ++++++--- .../quickFix/browser/quickFixAddon.ts | 31 +++++++++++++++---- .../browser/terminalQuickFixBuiltinActions.ts | 18 +++++------ ...ode.proposed.terminalQuickFixProvider.d.ts | 5 --- 7 files changed, 50 insertions(+), 31 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts index daab5df909d..295cafe4de0 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts @@ -20,10 +20,9 @@ import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteA import { OperatingSystem, OS } from 'vs/base/common/platform'; import { TerminalEditorLocationOptions } from 'vscode'; import { Promises } from 'vs/base/common/async'; -import { TerminalQuickFixType } from 'vs/workbench/api/common/extHostTypes'; import { ISerializableEnvironmentDescriptionMap, ISerializableEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariable'; import { ITerminalLinkProviderService } from 'vs/workbench/contrib/terminalContrib/links/browser/links'; -import { ITerminalQuickFixService, ITerminalQuickFix } from 'vs/workbench/contrib/terminalContrib/quickFix/browser/quickFix'; +import { ITerminalQuickFixService, ITerminalQuickFix, TerminalQuickFixType } from 'vs/workbench/contrib/terminalContrib/quickFix/browser/quickFix'; @extHostNamedCustomer(MainContext.MainThreadTerminalService) @@ -448,10 +447,12 @@ export function getOutputMatchForLines(lines: string[], outputMatcher: ITerminal } function parseQuickFix(id: string, source: string, fix: TerminalQuickFix): ITerminalQuickFix { - let type = TerminalQuickFixType.Command; + let type = TerminalQuickFixType.TerminalCommand; if ('uri' in fix) { fix.uri = URI.revive(fix.uri); type = TerminalQuickFixType.Opener; + } else if ('id' in fix) { + type = TerminalQuickFixType.VscodeCommand; } return { id, type, source, ...fix }; } diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 1673b845c0c..291edaf4e33 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1478,7 +1478,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I TerminalLocation: extHostTypes.TerminalLocation, TerminalProfile: extHostTypes.TerminalProfile, TerminalExitReason: extHostTypes.TerminalExitReason, - TerminalQuickFixType: extHostTypes.TerminalQuickFixType, TextDocumentSaveReason: extHostTypes.TextDocumentSaveReason, TextEdit: extHostTypes.TextEdit, SnippetTextEdit: extHostTypes.SnippetTextEdit, diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index be6ebc586b2..2f2bba71433 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -52,8 +52,9 @@ export enum TerminalOutputAnchor { } export enum TerminalQuickFixType { - Command = 0, - Opener = 1 + TerminalCommand = 0, + Opener = 1, + Command = 3 } @es5ClassCompat diff --git a/src/vs/workbench/contrib/terminalContrib/quickFix/browser/quickFix.ts b/src/vs/workbench/contrib/terminalContrib/quickFix/browser/quickFix.ts index 382d5144b23..8a13380b971 100644 --- a/src/vs/workbench/contrib/terminalContrib/quickFix/browser/quickFix.ts +++ b/src/vs/workbench/contrib/terminalContrib/quickFix/browser/quickFix.ts @@ -29,7 +29,7 @@ export interface ITerminalQuickFixProviderSelector { provider: ITerminalQuickFixProvider; } -export type TerminalQuickFixActionInternal = IAction | ITerminalQuickFixCommandAction | ITerminalQuickFixOpenerAction; +export type TerminalQuickFixActionInternal = IAction | ITerminalQuickFixExecuteTerminalCommandAction | ITerminalQuickFixOpenerAction; export type TerminalQuickFixCallback = (matchResult: ITerminalCommandMatchResult) => TerminalQuickFixActionInternal[] | TerminalQuickFixActionInternal | undefined; export type TerminalQuickFixCallbackExtension = (terminalCommand: ITerminalCommand, lines: string[] | undefined, option: ITerminalQuickFixOptions, token: CancellationToken) => Promise; @@ -44,9 +44,10 @@ export interface ITerminalQuickFixProvider { } export enum TerminalQuickFixType { - Command = 0, + TerminalCommand = 0, Opener = 1, - Port = 2 + Port = 2, + VscodeCommand = 3 } export interface ITerminalQuickFixOptions { @@ -63,8 +64,8 @@ export interface ITerminalQuickFix { source: string; } -export interface ITerminalQuickFixCommandAction extends ITerminalQuickFix { - type: TerminalQuickFixType.Command; +export interface ITerminalQuickFixExecuteTerminalCommandAction extends ITerminalQuickFix { + type: TerminalQuickFixType.TerminalCommand; terminalCommand: string; // TODO: Should this depend on whether alt is held? addNewLine?: boolean; @@ -73,6 +74,9 @@ export interface ITerminalQuickFixOpenerAction extends ITerminalQuickFix { type: TerminalQuickFixType.Opener; uri: URI; } +export interface ITerminalQuickFixCommandAction extends ITerminalQuickFix { + title: string; +} export interface ITerminalCommandMatchResult { commandLine: string; diff --git a/src/vs/workbench/contrib/terminalContrib/quickFix/browser/quickFixAddon.ts b/src/vs/workbench/contrib/terminalContrib/quickFix/browser/quickFixAddon.ts index 765d03c2141..0631a1e4e04 100644 --- a/src/vs/workbench/contrib/terminalContrib/quickFix/browser/quickFixAddon.ts +++ b/src/vs/workbench/contrib/terminalContrib/quickFix/browser/quickFixAddon.ts @@ -28,12 +28,13 @@ import { IAnchor } from 'vs/base/browser/ui/contextview/contextview'; import { ILabelService } from 'vs/platform/label/common/label'; import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; -import { ITerminalQuickFixInternalOptions, ITerminalQuickFixResolvedExtensionOptions, ITerminalQuickFix, ITerminalQuickFixCommandAction, ITerminalQuickFixOpenerAction, ITerminalQuickFixOptions, ITerminalQuickFixProviderSelector, ITerminalQuickFixService, ITerminalQuickFixUnresolvedExtensionOptions, TerminalQuickFixType } from 'vs/workbench/contrib/terminalContrib/quickFix/browser/quickFix'; +import { ITerminalQuickFixInternalOptions, ITerminalQuickFixResolvedExtensionOptions, ITerminalQuickFix, ITerminalQuickFixExecuteTerminalCommandAction, ITerminalQuickFixOpenerAction, ITerminalQuickFixOptions, ITerminalQuickFixProviderSelector, ITerminalQuickFixService, ITerminalQuickFixUnresolvedExtensionOptions, TerminalQuickFixType, ITerminalQuickFixCommandAction } from 'vs/workbench/contrib/terminalContrib/quickFix/browser/quickFix'; import { ITerminalCommandSelector } from 'vs/platform/terminal/common/terminal'; import { ActionListItemKind, IActionListItem } from 'vs/platform/actionWidget/browser/actionList'; import { CodeActionKind } from 'vs/editor/contrib/codeAction/common/types'; import { Codicon } from 'vs/base/common/codicons'; import { ThemeIcon } from 'vs/base/common/themables'; +import { ICommandService } from 'vs/platform/commands/common/commands'; const quickFixSelectors = [ DecorationSelector.QuickFix, @@ -75,6 +76,7 @@ export class TerminalQuickFixAddon extends Disposable implements ITerminalAddon, private readonly _aliases: string[][] | undefined, private readonly _capabilities: ITerminalCapabilityStore, @ITerminalQuickFixService private readonly _quickFixService: ITerminalQuickFixService, + @ICommandService private readonly _commandService: ICommandService, @IConfigurationService private readonly _configurationService: IConfigurationService, @IAudioCueService private readonly _audioCueService: IAudioCueService, @IOpenerService private readonly _openerService: IOpenerService, @@ -192,7 +194,7 @@ export class TerminalQuickFixAddon extends Disposable implements ITerminalAddon, await this._extensionService.activateByEvent(`onTerminalQuickFixRequest:${id}`); return this._quickFixService.providers.get(id)?.provideTerminalQuickFixes(command, lines, { type: 'resolved', commandLineMatcher: selector.commandLineMatcher, outputMatcher: selector.outputMatcher, commandExitResult: selector.commandExitResult, id: selector.id }, new CancellationTokenSource().token); }; - const result = await getQuickFixesForCommand(aliases, terminal, command, this._commandListeners, this._openerService, this._labelService, this._onDidRequestRerunCommand, resolver); + const result = await getQuickFixesForCommand(aliases, terminal, command, this._commandListeners, this._commandService, this._openerService, this._labelService, this._onDidRequestRerunCommand, resolver); if (!result) { return; } @@ -293,6 +295,7 @@ export async function getQuickFixesForCommand( terminal: Terminal, terminalCommand: ITerminalCommand, quickFixOptions: Map, + commandService: ICommandService, openerService: IOpenerService, labelService: ILabelService, onDidRequestRerunCommand?: Emitter<{ command: string; addNewLine?: boolean }>, @@ -339,15 +342,15 @@ export async function getQuickFixesForCommand( let action: ITerminalAction | undefined; if ('type' in quickFix) { switch (quickFix.type) { - case TerminalQuickFixType.Command: { - const fix = quickFix as ITerminalQuickFixCommandAction; + case TerminalQuickFixType.TerminalCommand: { + const fix = quickFix as ITerminalQuickFixExecuteTerminalCommandAction; if (commandQuickFixSet.has(fix.terminalCommand)) { continue; } commandQuickFixSet.add(fix.terminalCommand); const label = localize('quickFix.command', 'Run: {0}', fix.terminalCommand); action = { - type: TerminalQuickFixType.Command, + type: TerminalQuickFixType.TerminalCommand, class: undefined, source: quickFix.source, id: quickFix.id, @@ -405,6 +408,20 @@ export async function getQuickFixesForCommand( }; break; } + case TerminalQuickFixType.VscodeCommand: { + const fix = quickFix as ITerminalQuickFixCommandAction; + action = { + source: quickFix.source, + type: fix.type, + id: fix.id, + label: fix.title, + class: undefined, + enabled: true, + run: () => commandService.executeCommand(fix.id), + tooltip: fix.title + }; + break; + } } if (action) { fixes.push(action); @@ -477,9 +494,11 @@ function getQuickFixIcon(quickFix: TerminalQuickFixItem): ThemeIcon { const isUrl = (quickFix.action.uri.scheme === Schemas.http || quickFix.action.uri.scheme === Schemas.https); return isUrl ? Codicon.linkExternal : Codicon.goToFile; } - case TerminalQuickFixType.Command: + case TerminalQuickFixType.TerminalCommand: return Codicon.run; case TerminalQuickFixType.Port: return Codicon.debugDisconnect; + case TerminalQuickFixType.VscodeCommand: + return Codicon.lightbulb; } } diff --git a/src/vs/workbench/contrib/terminalContrib/quickFix/browser/terminalQuickFixBuiltinActions.ts b/src/vs/workbench/contrib/terminalContrib/quickFix/browser/terminalQuickFixBuiltinActions.ts index 0bc357b08b4..386eb5153fb 100644 --- a/src/vs/workbench/contrib/terminalContrib/quickFix/browser/terminalQuickFixBuiltinActions.ts +++ b/src/vs/workbench/contrib/terminalContrib/quickFix/browser/terminalQuickFixBuiltinActions.ts @@ -6,7 +6,7 @@ import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; -import { ITerminalQuickFixInternalOptions, ITerminalCommandMatchResult, ITerminalQuickFixCommandAction, TerminalQuickFixActionInternal, TerminalQuickFixType } from 'vs/workbench/contrib/terminalContrib/quickFix/browser/quickFix'; +import { ITerminalQuickFixInternalOptions, ITerminalCommandMatchResult, ITerminalQuickFixExecuteTerminalCommandAction, TerminalQuickFixActionInternal, TerminalQuickFixType } from 'vs/workbench/contrib/terminalContrib/quickFix/browser/quickFix'; export const GitCommandLineRegex = /git/; export const GitPushCommandLineRegex = /git\s+push/; @@ -49,7 +49,7 @@ export function gitSimilar(): ITerminalQuickFixInternalOptions { if (fixedCommand) { actions.push({ id: 'Git Similar', - type: TerminalQuickFixType.Command, + type: TerminalQuickFixType.TerminalCommand, terminalCommand: matchResult.commandLine.replace(/git\s+[^\s]+/, () => `git ${fixedCommand}`), addNewLine: true, source: QuickFixSource.Builtin @@ -79,7 +79,7 @@ export function gitTwoDashes(): ITerminalQuickFixInternalOptions { return; } return { - type: TerminalQuickFixType.Command, + type: TerminalQuickFixType.TerminalCommand, id: 'Git Two Dashes', terminalCommand: matchResult.commandLine.replace(` -${problemArg}`, () => ` --${problemArg}`), addNewLine: true, @@ -175,7 +175,7 @@ export function gitPushSetUpstream(): ITerminalQuickFixInternalOptions { } if (fixedCommand) { actions.push({ - type: TerminalQuickFixType.Command, + type: TerminalQuickFixType.TerminalCommand, id: 'Git Push Set Upstream', terminalCommand: fixedCommand, addNewLine: true, @@ -268,11 +268,11 @@ export function pwshGeneralError(): ITerminalQuickFixInternalOptions { if (!suggestions) { return; } - const result: ITerminalQuickFixCommandAction[] = []; + const result: ITerminalQuickFixExecuteTerminalCommandAction[] = []; for (const suggestion of suggestions) { result.push({ id: 'Pwsh General Error', - type: TerminalQuickFixType.Command, + type: TerminalQuickFixType.TerminalCommand, terminalCommand: suggestion, source: QuickFixSource.Builtin }); @@ -314,7 +314,7 @@ export function pwshUnixCommandNotFoundError(): ITerminalQuickFixInternalOptions } // Always remove the first element as it's the "Suggestion [cmd-not-found]"" line - const result: ITerminalQuickFixCommandAction[] = []; + const result: ITerminalQuickFixExecuteTerminalCommandAction[] = []; let inSuggestions = false; for (; i < lines.length; i++) { const line = lines[i].trim(); @@ -325,7 +325,7 @@ export function pwshUnixCommandNotFoundError(): ITerminalQuickFixInternalOptions if (installCommand) { result.push({ id: 'Pwsh Unix Command Not Found Error', - type: TerminalQuickFixType.Command, + type: TerminalQuickFixType.TerminalCommand, terminalCommand: installCommand, source: QuickFixSource.Builtin }); @@ -339,7 +339,7 @@ export function pwshUnixCommandNotFoundError(): ITerminalQuickFixInternalOptions if (inSuggestions) { result.push({ id: 'Pwsh Unix Command Not Found Error', - type: TerminalQuickFixType.Command, + type: TerminalQuickFixType.TerminalCommand, terminalCommand: line.trim(), source: QuickFixSource.Builtin }); diff --git a/src/vscode-dts/vscode.proposed.terminalQuickFixProvider.d.ts b/src/vscode-dts/vscode.proposed.terminalQuickFixProvider.d.ts index 094d1b969c4..4daa96722d8 100644 --- a/src/vscode-dts/vscode.proposed.terminalQuickFixProvider.d.ts +++ b/src/vscode-dts/vscode.proposed.terminalQuickFixProvider.d.ts @@ -80,9 +80,4 @@ declare module 'vscode' { Top = 0, Bottom = 1 } - - enum TerminalQuickFixType { - Command = 0, - Opener = 1 - } } From 2f5c0a562a0a93ad5cf3e049d43b29e4015a8058 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 16 Aug 2023 12:26:07 -0700 Subject: [PATCH 080/223] polish --- .../browser/inlineCompletionsController.ts | 2 +- .../accessibility/browser/accessibilityContributions.ts | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts index 51b2d4fc562..3d7909533c4 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts @@ -192,7 +192,7 @@ export class InlineCompletionsController extends Disposable { const lineText = model.textModel.getLineContent(state.ghostText.lineNumber); this.audioCueService.playAudioCue(AudioCue.inlineSuggestion).then(() => { if (this.editor.getOption(EditorOption.screenReaderAnnounceInlineSuggestion)) { - this.provideScreenReaderUpdate(state.ghostText.renderForScreenReader(lineText)); + this.provideScreenReaderUpdate(lineText + state.ghostText.renderForScreenReader(lineText)); } }); } diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts index db3e3233ae0..2b1699deae5 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts @@ -313,6 +313,7 @@ export class InlineCompletionsAccessibleViewContribution extends Disposable { verbositySettingKey: AccessibilityVerbositySettingId.InlineCompletions, provideContent() { return lineText + ghostText; }, onClose() { + model.stop(); editor.focus(); }, next() { @@ -327,9 +328,11 @@ export class InlineCompletionsAccessibleViewContribution extends Disposable { label: localize('inlineCompletions.accept', "Accept Completion"), tooltip: localize('inlineCompletions.accept', "Accept Completion"), run: () => { - alert('Accepted'); - model.accept(editor); - editor.focus(); + model.accept(editor).then(() => { + alert('Accepted'); + model.stop(); + editor.focus(); + }); }, class: ThemeIcon.asClassName(Codicon.check), enabled: true From 85de2f21c63608e8bbc5c2347fe927b00c8300bf Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 16 Aug 2023 12:28:41 -0700 Subject: [PATCH 081/223] Add command service to tests --- .../test/browser/quickFixAddon.test.ts | 61 ++++++++++--------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/quickFix/test/browser/quickFixAddon.test.ts b/src/vs/workbench/contrib/terminalContrib/quickFix/test/browser/quickFixAddon.test.ts index 107304e632c..5d5a0a16a07 100644 --- a/src/vs/workbench/contrib/terminalContrib/quickFix/test/browser/quickFixAddon.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/quickFix/test/browser/quickFixAddon.test.ts @@ -30,11 +30,13 @@ import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServic import { ITerminalQuickFixService } from 'vs/workbench/contrib/terminalContrib/quickFix/browser/quickFix'; import { ITerminalOutputMatcher } from 'vs/platform/terminal/common/terminal'; import { importAMDNodeModule } from 'vs/amdX'; +import { TestCommandService } from 'vs/editor/test/browser/editorTestServices'; suite('QuickFixAddon', () => { let quickFixAddon: TerminalQuickFixAddon; let terminalInstance: Pick; let commandDetection: CommandDetectionCapability; + let commandService: TestCommandService; let openerService: OpenerService; let labelService: LabelService; let terminal: Terminal; @@ -62,6 +64,7 @@ suite('QuickFixAddon', () => { capabilities.add(TerminalCapability.CommandDetection, commandDetection); instantiationService.stub(IContextMenuService, instantiationService.createInstance(ContextMenuService)); instantiationService.stub(IOpenerService, {} as Partial); + commandService = new TestCommandService(instantiationService); terminalInstance = { async freePortKillProcess(port: string): Promise { } } as Pick; @@ -96,23 +99,23 @@ suite('QuickFixAddon', () => { }); suite('returns undefined when', () => { test('output does not match', async () => { - strictEqual(await (getQuickFixesForCommand([], terminal, createCommand(command, `invalid output`, GitSimilarOutputRegex, exitCode, [`invalid output`]), expectedMap, openerService, labelService)), undefined); + strictEqual(await (getQuickFixesForCommand([], terminal, createCommand(command, `invalid output`, GitSimilarOutputRegex, exitCode, [`invalid output`]), expectedMap, commandService, openerService, labelService)), undefined); }); test('command does not match', async () => { - strictEqual(await (getQuickFixesForCommand([], terminal, createCommand(`gt sttatus`, output, GitSimilarOutputRegex, exitCode, outputLines), expectedMap, openerService, labelService)), undefined); + strictEqual(await (getQuickFixesForCommand([], terminal, createCommand(`gt sttatus`, output, GitSimilarOutputRegex, exitCode, outputLines), expectedMap, commandService, openerService, labelService)), undefined); }); }); suite('returns actions when', () => { test('expected unix exit code', async () => { - assertMatchOptions((await getQuickFixesForCommand([], terminal, createCommand(command, output, GitSimilarOutputRegex, exitCode, outputLines), expectedMap, openerService, labelService)), actions); + assertMatchOptions((await getQuickFixesForCommand([], terminal, createCommand(command, output, GitSimilarOutputRegex, exitCode, outputLines), expectedMap, commandService, openerService, labelService)), actions); }); test('matching exit status', async () => { - assertMatchOptions((await getQuickFixesForCommand([], terminal, createCommand(command, output, GitSimilarOutputRegex, 2, outputLines), expectedMap, openerService, labelService)), actions); + assertMatchOptions((await getQuickFixesForCommand([], terminal, createCommand(command, output, GitSimilarOutputRegex, 2, outputLines), expectedMap, commandService, openerService, labelService)), actions); }); }); suite('returns match', () => { test('returns match', async () => { - assertMatchOptions((await getQuickFixesForCommand([], terminal, createCommand(command, output, GitSimilarOutputRegex, exitCode, outputLines), expectedMap, openerService, labelService)), actions); + assertMatchOptions((await getQuickFixesForCommand([], terminal, createCommand(command, output, GitSimilarOutputRegex, exitCode, outputLines), expectedMap, commandService, openerService, labelService)), actions); }); test('returns multiple match', async () => { @@ -133,13 +136,13 @@ suite('QuickFixAddon', () => { tooltip: 'Run: git push', command: 'git push' }]; - assertMatchOptions((await getQuickFixesForCommand([], terminal, createCommand('git pu', output, GitSimilarOutputRegex, exitCode, output.split('\n')), expectedMap, openerService, labelService)), actions); + assertMatchOptions((await getQuickFixesForCommand([], terminal, createCommand('git pu', output, GitSimilarOutputRegex, exitCode, output.split('\n')), expectedMap, commandService, openerService, labelService)), actions); }); test('passes any arguments through', async () => { output = `git: 'checkoutt' is not a git command. See 'git --help'. The most similar commands are checkout`; - assertMatchOptions((await getQuickFixesForCommand([], terminal, createCommand('git checkoutt .', output, GitSimilarOutputRegex, exitCode, output.split('\n')), expectedMap, openerService, labelService)), [{ + assertMatchOptions((await getQuickFixesForCommand([], terminal, createCommand('git checkoutt .', output, GitSimilarOutputRegex, exitCode, output.split('\n')), expectedMap, commandService, openerService, labelService)), [{ id: 'Git Similar', enabled: true, label: 'Run: git checkout .', @@ -168,18 +171,18 @@ suite('QuickFixAddon', () => { }); suite('returns undefined when', () => { test('output does not match', async () => { - strictEqual((await getQuickFixesForCommand([], terminal, createCommand(command, `invalid output`, GitTwoDashesRegex, exitCode), expectedMap, openerService, labelService)), undefined); + strictEqual((await getQuickFixesForCommand([], terminal, createCommand(command, `invalid output`, GitTwoDashesRegex, exitCode), expectedMap, commandService, openerService, labelService)), undefined); }); test('command does not match', async () => { - strictEqual((await getQuickFixesForCommand([], terminal, createCommand(`gt sttatus`, output, GitTwoDashesRegex, exitCode), expectedMap, openerService, labelService)), undefined); + strictEqual((await getQuickFixesForCommand([], terminal, createCommand(`gt sttatus`, output, GitTwoDashesRegex, exitCode), expectedMap, commandService, openerService, labelService)), undefined); }); }); suite('returns actions when', () => { test('expected unix exit code', async () => { - assertMatchOptions((await getQuickFixesForCommand([], terminal, createCommand(command, output, GitTwoDashesRegex, exitCode), expectedMap, openerService, labelService)), actions); + assertMatchOptions((await getQuickFixesForCommand([], terminal, createCommand(command, output, GitTwoDashesRegex, exitCode), expectedMap, commandService, openerService, labelService)), actions); }); test('matching exit status', async () => { - assertMatchOptions((await getQuickFixesForCommand([], terminal, createCommand(command, output, GitTwoDashesRegex, 2), expectedMap, openerService, labelService)), actions); + assertMatchOptions((await getQuickFixesForCommand([], terminal, createCommand(command, output, GitTwoDashesRegex, 2), expectedMap, commandService, openerService, labelService)), actions); }); }); }); @@ -215,11 +218,11 @@ suite('QuickFixAddon', () => { }); suite('returns undefined when', () => { test('output does not match', async () => { - strictEqual((await getQuickFixesForCommand([], terminal, createCommand(portCommand, `invalid output`, FreePortOutputRegex), expectedMap, openerService, labelService)), undefined); + strictEqual((await getQuickFixesForCommand([], terminal, createCommand(portCommand, `invalid output`, FreePortOutputRegex), expectedMap, commandService, openerService, labelService)), undefined); }); }); test('returns actions', async () => { - assertMatchOptions((await getQuickFixesForCommand([], terminal, createCommand(portCommand, output, FreePortOutputRegex), expectedMap, openerService, labelService)), actionOptions); + assertMatchOptions((await getQuickFixesForCommand([], terminal, createCommand(portCommand, output, FreePortOutputRegex), expectedMap, commandService, openerService, labelService)), actionOptions); }); }); } @@ -246,18 +249,18 @@ suite('QuickFixAddon', () => { }); suite('returns undefined when', () => { test('output does not match', async () => { - strictEqual((await getQuickFixesForCommand([], terminal, createCommand(command, `invalid output`, GitPushOutputRegex, exitCode), expectedMap, openerService, labelService)), undefined); + strictEqual((await getQuickFixesForCommand([], terminal, createCommand(command, `invalid output`, GitPushOutputRegex, exitCode), expectedMap, commandService, openerService, labelService)), undefined); }); test('command does not match', async () => { - strictEqual((await getQuickFixesForCommand([], terminal, createCommand(`git status`, output, GitPushOutputRegex, exitCode), expectedMap, openerService, labelService)), undefined); + strictEqual((await getQuickFixesForCommand([], terminal, createCommand(`git status`, output, GitPushOutputRegex, exitCode), expectedMap, commandService, openerService, labelService)), undefined); }); }); suite('returns actions when', () => { test('expected unix exit code', async () => { - assertMatchOptions((await getQuickFixesForCommand([], terminal, createCommand(command, output, GitPushOutputRegex, exitCode), expectedMap, openerService, labelService)), actions); + assertMatchOptions((await getQuickFixesForCommand([], terminal, createCommand(command, output, GitPushOutputRegex, exitCode), expectedMap, commandService, openerService, labelService)), actions); }); test('matching exit status', async () => { - assertMatchOptions((await getQuickFixesForCommand([], terminal, createCommand(command, output, GitPushOutputRegex, 2), expectedMap, openerService, labelService)), actions); + assertMatchOptions((await getQuickFixesForCommand([], terminal, createCommand(command, output, GitPushOutputRegex, 2), expectedMap, commandService, openerService, labelService)), actions); }); }); }); @@ -287,18 +290,18 @@ suite('QuickFixAddon', () => { }); suite('returns undefined when', () => { test('output does not match', async () => { - strictEqual((await getQuickFixesForCommand([], terminal, createCommand(command, `invalid output`, GitCreatePrOutputRegex, exitCode), expectedMap, openerService, labelService)), undefined); + strictEqual((await getQuickFixesForCommand([], terminal, createCommand(command, `invalid output`, GitCreatePrOutputRegex, exitCode), expectedMap, commandService, openerService, labelService)), undefined); }); test('command does not match', async () => { - strictEqual((await getQuickFixesForCommand([], terminal, createCommand(`git status`, output, GitCreatePrOutputRegex, exitCode), expectedMap, openerService, labelService)), undefined); + strictEqual((await getQuickFixesForCommand([], terminal, createCommand(`git status`, output, GitCreatePrOutputRegex, exitCode), expectedMap, commandService, openerService, labelService)), undefined); }); test('failure exit status', async () => { - strictEqual((await getQuickFixesForCommand([], terminal, createCommand(command, output, GitCreatePrOutputRegex, 2), expectedMap, openerService, labelService)), undefined); + strictEqual((await getQuickFixesForCommand([], terminal, createCommand(command, output, GitCreatePrOutputRegex, 2), expectedMap, commandService, openerService, labelService)), undefined); }); }); suite('returns actions when', () => { test('expected unix exit code', async () => { - assertMatchOptions((await getQuickFixesForCommand([], terminal, createCommand(command, output, GitCreatePrOutputRegex, exitCode), expectedMap, openerService, labelService)), actions); + assertMatchOptions((await getQuickFixesForCommand([], terminal, createCommand(command, output, GitCreatePrOutputRegex, exitCode), expectedMap, commandService, openerService, labelService)), actions); }); }); }); @@ -326,18 +329,18 @@ suite('QuickFixAddon', () => { }); suite('returns undefined when', () => { test('output does not match', async () => { - strictEqual((await getQuickFixesForCommand([], terminal, createCommand(command, `invalid output`, GitPushOutputRegex, exitCode), expectedMap, openerService, labelService)), undefined); + strictEqual((await getQuickFixesForCommand([], terminal, createCommand(command, `invalid output`, GitPushOutputRegex, exitCode), expectedMap, commandService, openerService, labelService)), undefined); }); test('command does not match', async () => { - strictEqual((await getQuickFixesForCommand([], terminal, createCommand(`git status`, output, GitPushOutputRegex, exitCode), expectedMap, openerService, labelService)), undefined); + strictEqual((await getQuickFixesForCommand([], terminal, createCommand(`git status`, output, GitPushOutputRegex, exitCode), expectedMap, commandService, openerService, labelService)), undefined); }); }); suite('returns actions when', () => { test('expected unix exit code', async () => { - assertMatchOptions((await getQuickFixesForCommand([], terminal, createCommand(command, output, GitPushOutputRegex, exitCode), expectedMap, openerService, labelService)), actions); + assertMatchOptions((await getQuickFixesForCommand([], terminal, createCommand(command, output, GitPushOutputRegex, exitCode), expectedMap, commandService, openerService, labelService)), actions); }); test('matching exit status', async () => { - assertMatchOptions((await getQuickFixesForCommand([], terminal, createCommand(command, output, GitPushOutputRegex, 2), expectedMap, openerService, labelService)), actions); + assertMatchOptions((await getQuickFixesForCommand([], terminal, createCommand(command, output, GitPushOutputRegex, 2), expectedMap, commandService, openerService, labelService)), actions); }); }); }); @@ -386,10 +389,10 @@ suite('QuickFixAddon', () => { expectedMap.set(pushCommand.commandLineMatcher.toString(), [pushCommand]); }); test('returns undefined when output does not match', async () => { - strictEqual((await getQuickFixesForCommand([], terminal, createCommand(command, `invalid output`, PwshGeneralErrorOutputRegex, exitCode), expectedMap, openerService, labelService)), undefined); + strictEqual((await getQuickFixesForCommand([], terminal, createCommand(command, `invalid output`, PwshGeneralErrorOutputRegex, exitCode), expectedMap, commandService, openerService, labelService)), undefined); }); test('returns actions when output matches', async () => { - assertMatchOptions((await getQuickFixesForCommand([], terminal, createCommand(command, output, PwshGeneralErrorOutputRegex, exitCode), expectedMap, openerService, labelService)), actions); + assertMatchOptions((await getQuickFixesForCommand([], terminal, createCommand(command, output, PwshGeneralErrorOutputRegex, exitCode), expectedMap, commandService, openerService, labelService)), actions); }); }); suite('Unix cmd-not-found', () => { @@ -430,10 +433,10 @@ suite('QuickFixAddon', () => { expectedMap.set(pushCommand.commandLineMatcher.toString(), [pushCommand]); }); test('returns undefined when output does not match', async () => { - strictEqual((await getQuickFixesForCommand([], terminal, createCommand(command, `invalid output`, PwshUnixCommandNotFoundErrorOutputRegex, exitCode), expectedMap, openerService, labelService)), undefined); + strictEqual((await getQuickFixesForCommand([], terminal, createCommand(command, `invalid output`, PwshUnixCommandNotFoundErrorOutputRegex, exitCode), expectedMap, commandService, openerService, labelService)), undefined); }); test('returns actions when output matches', async () => { - assertMatchOptions((await getQuickFixesForCommand([], terminal, createCommand(command, output, PwshUnixCommandNotFoundErrorOutputRegex, exitCode), expectedMap, openerService, labelService)), actions); + assertMatchOptions((await getQuickFixesForCommand([], terminal, createCommand(command, output, PwshUnixCommandNotFoundErrorOutputRegex, exitCode), expectedMap, commandService, openerService, labelService)), actions); }); }); }); From e5921ad238169538f2646cbb33b34e5803be4c62 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 16 Aug 2023 13:25:07 -0700 Subject: [PATCH 082/223] add focusedView variable to window title --- src/vs/workbench/browser/parts/titlebar/windowTitle.ts | 10 +++++++++- src/vs/workbench/browser/parts/views/viewsService.ts | 10 +++++++++- src/vs/workbench/browser/workbench.contribution.ts | 1 + src/vs/workbench/common/views.ts | 1 + src/vs/workbench/test/browser/workbenchTestServices.ts | 2 ++ 5 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/titlebar/windowTitle.ts b/src/vs/workbench/browser/parts/titlebar/windowTitle.ts index 4b5269d456e..8197240759d 100644 --- a/src/vs/workbench/browser/parts/titlebar/windowTitle.ts +++ b/src/vs/workbench/browser/parts/titlebar/windowTitle.ts @@ -24,6 +24,9 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { Schemas } from 'vs/base/common/network'; import { getVirtualWorkspaceLocation } from 'vs/platform/workspace/common/virtualWorkspace'; import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { FocusedViewContext } from 'vs/workbench/common/contextkeys'; +import { IViewsService } from 'vs/workbench/common/views'; const enum WindowSettingNames { titleSeparator = 'window.titleSeparator', @@ -53,7 +56,9 @@ export class WindowTitle extends Disposable { @IInstantiationService protected readonly instantiationService: IInstantiationService, @ILabelService private readonly labelService: ILabelService, @IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService, - @IProductService private readonly productService: IProductService + @IProductService private readonly productService: IProductService, + @IContextKeyService private readonly contextKeyService: IContextKeyService, + @IViewsService private readonly viewsService: IViewsService ) { super(); this.registerListeners(); @@ -75,6 +80,7 @@ export class WindowTitle extends Disposable { this._register(this.contextService.onDidChangeWorkspaceName(() => this.titleUpdater.schedule())); this._register(this.labelService.onDidChangeFormatters(() => this.titleUpdater.schedule())); this._register(this.userDataProfileService.onDidChangeCurrentProfile(() => this.titleUpdater.schedule())); + this._register(this.viewsService.onDidChangeFocusedView(() => this.titleUpdater.schedule())); } private onConfigurationChanged(event: IConfigurationChangeEvent): void { @@ -237,6 +243,7 @@ export class WindowTitle extends Disposable { const profileName = this.userDataProfileService.currentProfile.isDefault ? '' : this.userDataProfileService.currentProfile.name; const separator = this.configurationService.getValue(WindowSettingNames.titleSeparator); const titleTemplate = this.configurationService.getValue(WindowSettingNames.title); + const focusedView: string | undefined = this.contextKeyService.getContextKeyValue(FocusedViewContext.key); return template(titleTemplate, { activeEditorShort, @@ -254,6 +261,7 @@ export class WindowTitle extends Disposable { appName, remoteName, profileName, + focusedView, separator: { label: separator } }); } diff --git a/src/vs/workbench/browser/parts/views/viewsService.ts b/src/vs/workbench/browser/parts/views/viewsService.ts index e81ea2801ca..3de521af6ef 100644 --- a/src/vs/workbench/browser/parts/views/viewsService.ts +++ b/src/vs/workbench/browser/parts/views/viewsService.ts @@ -46,6 +46,9 @@ export class ViewsService extends Disposable implements IViewsService { private readonly _onDidChangeViewContainerVisibility = this._register(new Emitter<{ id: string; visible: boolean; location: ViewContainerLocation }>()); readonly onDidChangeViewContainerVisibility = this._onDidChangeViewContainerVisibility.event; + private readonly _onDidChangeFocusedView = this._register(new Emitter()); + readonly onDidChangeFocusedView = this._onDidChangeFocusedView.event; + private readonly visibleViewContextKeys: Map>; private readonly focusedViewContextKey: IContextKey; @@ -616,7 +619,12 @@ export class ViewsService extends Disposable implements IViewsService { disposables.add(viewPaneContainer.onDidAddViews(views => this.onViewsAdded(views))); disposables.add(viewPaneContainer.onDidChangeViewVisibility(view => this.onViewsVisibilityChanged(view, view.isBodyVisible()))); disposables.add(viewPaneContainer.onDidRemoveViews(views => this.onViewsRemoved(views))); - disposables.add(viewPaneContainer.onDidFocusView(view => this.focusedViewContextKey.set(view.id))); + disposables.add(viewPaneContainer.onDidFocusView(view => { + if (this.focusedViewContextKey.get() !== view.id) { + this.focusedViewContextKey.set(view.id); + this._onDidChangeFocusedView.fire(); + } + })); disposables.add(viewPaneContainer.onDidBlurView(view => { if (this.focusedViewContextKey.get() === view.id) { this.focusedViewContextKey.reset(); diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 50914bd2182..da60053abeb 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -550,6 +550,7 @@ const registry = Registry.as(ConfigurationExtensions.Con localize('appName', "`${appName}`: e.g. VS Code."), localize('remoteName', "`${remoteName}`: e.g. SSH"), localize('dirty', "`${dirty}`: an indicator for when the active editor has unsaved changes."), + localize('focusedView', "`${focusedView}`: the name of the view that is currently focused."), localize('separator', "`${separator}`: a conditional separator (\" - \") that only shows when surrounded by variables with values or static text.") ].join('\n- '); // intentionally concatenated to not produce a string that is too long for translations diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 8306ee74803..24204e25816 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -587,6 +587,7 @@ export interface IViewsService { // View APIs readonly onDidChangeViewVisibility: Event<{ id: string; visible: boolean }>; + readonly onDidChangeFocusedView: Event; isViewVisible(id: string): boolean; openView(id: string, focus?: boolean): Promise; closeView(id: string): void; diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 52a5c1aa21d..6f35cbe8ee4 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -794,6 +794,8 @@ export class TestViewsService implements IViewsService { onDidChangeViewVisibilityEmitter = new Emitter<{ id: string; visible: boolean }>(); onDidChangeViewVisibility = this.onDidChangeViewVisibilityEmitter.event; + onDidChangeFocusedViewEmitter = new Emitter(); + onDidChangeFocusedView = this.onDidChangeFocusedViewEmitter.event; isViewVisible(id: string): boolean { return true; } getActiveViewWithId(id: string): T | null { return null; } getViewWithId(id: string): T | null { return null; } From 6ea1bd39226496605bc4192b5275af5e482f4045 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 16 Aug 2023 13:51:41 -0700 Subject: [PATCH 083/223] use name --- src/vs/workbench/browser/parts/titlebar/windowTitle.ts | 5 +---- src/vs/workbench/browser/parts/views/viewsService.ts | 6 ++++++ src/vs/workbench/common/views.ts | 1 + src/vs/workbench/test/browser/workbenchTestServices.ts | 1 + 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/browser/parts/titlebar/windowTitle.ts b/src/vs/workbench/browser/parts/titlebar/windowTitle.ts index 8197240759d..28ae24ba4b8 100644 --- a/src/vs/workbench/browser/parts/titlebar/windowTitle.ts +++ b/src/vs/workbench/browser/parts/titlebar/windowTitle.ts @@ -24,8 +24,6 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { Schemas } from 'vs/base/common/network'; import { getVirtualWorkspaceLocation } from 'vs/platform/workspace/common/virtualWorkspace'; import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { FocusedViewContext } from 'vs/workbench/common/contextkeys'; import { IViewsService } from 'vs/workbench/common/views'; const enum WindowSettingNames { @@ -57,7 +55,6 @@ export class WindowTitle extends Disposable { @ILabelService private readonly labelService: ILabelService, @IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService, @IProductService private readonly productService: IProductService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, @IViewsService private readonly viewsService: IViewsService ) { super(); @@ -243,7 +240,7 @@ export class WindowTitle extends Disposable { const profileName = this.userDataProfileService.currentProfile.isDefault ? '' : this.userDataProfileService.currentProfile.name; const separator = this.configurationService.getValue(WindowSettingNames.titleSeparator); const titleTemplate = this.configurationService.getValue(WindowSettingNames.title); - const focusedView: string | undefined = this.contextKeyService.getContextKeyValue(FocusedViewContext.key); + const focusedView: string = this.viewsService.getFocusedViewName(); return template(titleTemplate, { activeEditorShort, diff --git a/src/vs/workbench/browser/parts/views/viewsService.ts b/src/vs/workbench/browser/parts/views/viewsService.ts index 3de521af6ef..ed0b160656b 100644 --- a/src/vs/workbench/browser/parts/views/viewsService.ts +++ b/src/vs/workbench/browser/parts/views/viewsService.ts @@ -238,6 +238,11 @@ export class ViewsService extends Disposable implements IViewsService { return null; } + getFocusedViewName(): string { + const viewId: string = this.contextKeyService.getContextKeyValue(FocusedViewContext.key) ?? ''; + return this.viewDescriptorService.getViewDescriptorById(viewId.toString())?.name ?? ''; + } + async openView(id: string, focus?: boolean): Promise { const viewContainer = this.viewDescriptorService.getViewContainerByViewId(id); if (!viewContainer) { @@ -628,6 +633,7 @@ export class ViewsService extends Disposable implements IViewsService { disposables.add(viewPaneContainer.onDidBlurView(view => { if (this.focusedViewContextKey.get() === view.id) { this.focusedViewContextKey.reset(); + this._onDidChangeFocusedView.fire(); } })); diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 24204e25816..95f89542805 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -584,6 +584,7 @@ export interface IViewsService { closeViewContainer(id: string): void; getVisibleViewContainer(location: ViewContainerLocation): ViewContainer | null; getActiveViewPaneContainerWithId(viewContainerId: string): IViewPaneContainer | null; + getFocusedViewName(): string; // View APIs readonly onDidChangeViewVisibility: Event<{ id: string; visible: boolean }>; diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 6f35cbe8ee4..706028e8ddf 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -803,6 +803,7 @@ export class TestViewsService implements IViewsService { closeView(id: string): void { } getViewProgressIndicator(id: string) { return null!; } getActiveViewPaneContainerWithId(id: string) { return null; } + getFocusedViewName(): string { return ''; } } export class TestEditorGroupsService implements IEditorGroupsService { From 6ce0f37a571184493adc0f03eb65452bbbd8f5ba Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 16 Aug 2023 14:04:23 -0700 Subject: [PATCH 084/223] Fix using variables with uppercase characters (#190606) --- src/vs/workbench/contrib/chat/common/chatVariables.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/common/chatVariables.ts b/src/vs/workbench/contrib/chat/common/chatVariables.ts index 574e166f8ba..f8fe3874ee9 100644 --- a/src/vs/workbench/contrib/chat/common/chatVariables.ts +++ b/src/vs/workbench/contrib/chat/common/chatVariables.ts @@ -57,7 +57,7 @@ export class ChatVariablesService implements IChatVariablesService { let match: RegExpMatchArray | null; while (match = regex.exec(prompt)) { const candidate = match[2]; - const data = this._resolver.get(candidate); + const data = this._resolver.get(candidate.toLowerCase()); if (data) { jobs.push(data[1](prompt, token).then(value => { if (value) { From f7dc5e14345cc179fb26cd7e2cf3e167b1b3396a Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 16 Aug 2023 14:29:08 -0700 Subject: [PATCH 085/223] Use `regexp.flags` instead of custom function (#190617) This ensures we pass along all regex flags instead of just the subset we had code for --- src/vs/base/common/marshalling.ts | 3 +-- src/vs/base/common/strings.ts | 7 ------- src/vs/editor/browser/services/editorWorkerService.ts | 7 +++---- src/vs/workbench/api/common/extHostLanguageFeatures.ts | 4 ++-- 4 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/vs/base/common/marshalling.ts b/src/vs/base/common/marshalling.ts index 67a29703cdc..a51f0094591 100644 --- a/src/vs/base/common/marshalling.ts +++ b/src/vs/base/common/marshalling.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { VSBuffer } from 'vs/base/common/buffer'; -import { regExpFlags } from 'vs/base/common/strings'; import { URI, UriComponents } from 'vs/base/common/uri'; import { MarshalledId } from './marshallingIds'; @@ -28,7 +27,7 @@ function replacer(key: string, value: any): any { return { $mid: MarshalledId.Regexp, source: value.source, - flags: regExpFlags(value), + flags: value.flags, }; } return value; diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts index 5837c2f4c49..3931adefd65 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -221,13 +221,6 @@ export function regExpContainsBackreference(regexpValue: string): boolean { return !!regexpValue.match(/([^\\]|^)(\\\\)*\\\d+/); } -export function regExpFlags(regexp: RegExp): string { - return (regexp.global ? 'g' : '') - + (regexp.ignoreCase ? 'i' : '') - + (regexp.multiline ? 'm' : '') - + ((regexp as any /* standalone editor compilation */).unicode ? 'u' : ''); -} - export function splitLines(str: string): string[] { return str.split(/\r\n|\r|\n/); } diff --git a/src/vs/editor/browser/services/editorWorkerService.ts b/src/vs/editor/browser/services/editorWorkerService.ts index a4ffe3053b5..6757066169f 100644 --- a/src/vs/editor/browser/services/editorWorkerService.ts +++ b/src/vs/editor/browser/services/editorWorkerService.ts @@ -17,7 +17,6 @@ import { EditorSimpleWorker } from 'vs/editor/common/services/editorSimpleWorker import { DiffAlgorithmName, IDiffComputationResult, IEditorWorkerService, ILineChange, IUnicodeHighlightsResult } from 'vs/editor/common/services/editorWorker'; import { IModelService } from 'vs/editor/common/services/model'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfiguration'; -import { regExpFlags } from 'vs/base/common/strings'; import { isNonEmptyArray } from 'vs/base/common/arrays'; import { ILogService } from 'vs/platform/log/common/log'; import { StopWatch } from 'vs/base/common/stopwatch'; @@ -582,7 +581,7 @@ export class EditorWorkerClient extends Disposable implements IEditorWorkerClien public async textualSuggest(resources: URI[], leadingWord: string | undefined, wordDefRegExp: RegExp): Promise<{ words: string[]; duration: number } | null> { const proxy = await this._withSyncedResources(resources); const wordDef = wordDefRegExp.source; - const wordDefFlags = regExpFlags(wordDefRegExp); + const wordDefFlags = wordDefRegExp.flags; return proxy.textualSuggest(resources.map(r => r.toString()), leadingWord, wordDef, wordDefFlags); } @@ -594,7 +593,7 @@ export class EditorWorkerClient extends Disposable implements IEditorWorkerClien } const wordDefRegExp = this.languageConfigurationService.getLanguageConfiguration(model.getLanguageId()).getWordDefinition(); const wordDef = wordDefRegExp.source; - const wordDefFlags = regExpFlags(wordDefRegExp); + const wordDefFlags = wordDefRegExp.flags; return proxy.computeWordRanges(resource.toString(), range, wordDef, wordDefFlags); }); } @@ -607,7 +606,7 @@ export class EditorWorkerClient extends Disposable implements IEditorWorkerClien } const wordDefRegExp = this.languageConfigurationService.getLanguageConfiguration(model.getLanguageId()).getWordDefinition(); const wordDef = wordDefRegExp.source; - const wordDefFlags = regExpFlags(wordDefRegExp); + const wordDefFlags = wordDefRegExp.flags; return proxy.navigateValueSet(resource.toString(), range, up, wordDef, wordDefFlags); }); } diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 359877e5509..7a32009ac07 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -14,7 +14,7 @@ import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/common/extHostCommands'; import { ExtHostDiagnostics } from 'vs/workbench/api/common/extHostDiagnostics'; import * as extHostProtocol from './extHost.protocol'; -import { regExpLeadsToEndlessLoop, regExpFlags } from 'vs/base/common/strings'; +import { regExpLeadsToEndlessLoop } from 'vs/base/common/strings'; import { IPosition } from 'vs/editor/common/core/position'; import { IRange, Range as EditorRange } from 'vs/editor/common/core/range'; import { isFalsyOrEmpty, isNonEmptyArray, coalesce } from 'vs/base/common/arrays'; @@ -2491,7 +2491,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF private static _serializeRegExp(regExp: RegExp): extHostProtocol.IRegExpDto { return { pattern: regExp.source, - flags: regExpFlags(regExp), + flags: regExp.flags, }; } From 1afcd2c22a9d0310393da8b4ffb4eeba3dfdbdc6 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 16 Aug 2023 14:58:07 -0700 Subject: [PATCH 086/223] Remove `regExpContainsBackreference` (#190621) Doesn't appear to be used anywhere besides tests --- src/vs/base/common/strings.ts | 4 ---- src/vs/base/test/common/strings.test.ts | 16 ---------------- 2 files changed, 20 deletions(-) diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts index 3931adefd65..02d9179c792 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -217,10 +217,6 @@ export function regExpLeadsToEndlessLoop(regexp: RegExp): boolean { return !!(match && regexp.lastIndex === 0); } -export function regExpContainsBackreference(regexpValue: string): boolean { - return !!regexpValue.match(/([^\\]|^)(\\\\)*\\\d+/); -} - export function splitLines(str: string): string[] { return str.split(/\r\n|\r|\n/); } diff --git a/src/vs/base/test/common/strings.test.ts b/src/vs/base/test/common/strings.test.ts index f4bea3785b8..18e53a2e5d0 100644 --- a/src/vs/base/test/common/strings.test.ts +++ b/src/vs/base/test/common/strings.test.ts @@ -265,22 +265,6 @@ suite('Strings', () => { assert(regExpWithFlags.multiline); }); - test('regExpContainsBackreference', () => { - assert(strings.regExpContainsBackreference('foo \\5 bar')); - assert(strings.regExpContainsBackreference('\\2')); - assert(strings.regExpContainsBackreference('(\\d)(\\n)(\\1)')); - assert(strings.regExpContainsBackreference('(A).*?\\1')); - assert(strings.regExpContainsBackreference('\\\\\\1')); - assert(strings.regExpContainsBackreference('foo \\\\\\1')); - - assert(!strings.regExpContainsBackreference('')); - assert(!strings.regExpContainsBackreference('\\\\1')); - assert(!strings.regExpContainsBackreference('foo \\\\1')); - assert(!strings.regExpContainsBackreference('(A).*?\\\\1')); - assert(!strings.regExpContainsBackreference('foo \\d1 bar')); - assert(!strings.regExpContainsBackreference('123')); - }); - test('getLeadingWhitespace', () => { assert.strictEqual(strings.getLeadingWhitespace(' foo'), ' '); assert.strictEqual(strings.getLeadingWhitespace(' foo', 2), ''); From 376d0b18918554efd16bd387418e252622433e4c Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 16 Aug 2023 15:28:28 -0700 Subject: [PATCH 087/223] Bump TS version for building VS Code (#190622) --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 470bc68794f..97143500048 100644 --- a/package.json +++ b/package.json @@ -212,7 +212,7 @@ "ts-loader": "^9.4.2", "ts-node": "^10.9.1", "tsec": "0.2.7", - "typescript": "^5.2.0-dev.20230807", + "typescript": "^5.3.0-dev.20230816", "typescript-formatter": "7.1.0", "underscore": "^1.12.1", "util": "^0.12.4", diff --git a/yarn.lock b/yarn.lock index 88f1223f580..17b3729bc6a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10075,10 +10075,10 @@ typescript@^4.7.4: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.4.tgz#c464abca159669597be5f96b8943500b238e60e6" integrity sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ== -typescript@^5.2.0-dev.20230807: - version "5.2.0-dev.20230807" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.0-dev.20230807.tgz#652fa8a3fcc46958035d9b911c815299deb9f58e" - integrity sha512-sF8sZl3r/mpAdKAxASaWaoU+mNPF3g8OrZ601HraU2l4WEwAf6nO7sq0Bzl+Y3FV7sWjy+gb6kdM1CtLjVne/g== +typescript@^5.3.0-dev.20230816: + version "5.3.0-dev.20230816" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.0-dev.20230816.tgz#409982c629164811db1eb62b365ed2e1b526458d" + integrity sha512-iEOudrx61DsbJn+z2bVX+/FldF7ILAuGwQYO2EvF4F33Q8DUV0KSkiikxUB83VVH8ExkwQHVNdtkr16wd2V71w== typical@^4.0.0: version "4.0.0" From ccb95fd921349023027a0df25ed291b0992b9a18 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Wed, 16 Aug 2023 16:05:57 -0700 Subject: [PATCH 088/223] Create an `IQuickWidget` (#190627) To do this, I've pulled `QuickInputController` into its own file since `quickInput.ts` was getting too big... And then I made another thing that extends `QuickInput` that hides pretty much everything but does allow for a title bar. This could be useful for more "QuickWebView" type experiences if we wanna light anything like that up. --- .../quickInput/standaloneQuickInputService.ts | 8 +- .../platform/quickinput/browser/quickInput.ts | 727 +----------------- .../browser/quickInputController.ts | 726 +++++++++++++++++ .../quickinput/browser/quickInputService.ts | 10 +- .../platform/quickinput/common/quickInput.ts | 37 +- .../test/browser/quickinput.test.ts | 2 +- .../browser/actions/chatQuickInputActions.ts | 8 +- .../quickinput/browser/quickInputService.ts | 2 +- .../test/browser/workbenchTestServices.ts | 3 +- 9 files changed, 785 insertions(+), 738 deletions(-) create mode 100644 src/vs/platform/quickinput/browser/quickInputController.ts diff --git a/src/vs/editor/standalone/browser/quickInput/standaloneQuickInputService.ts b/src/vs/editor/standalone/browser/quickInput/standaloneQuickInputService.ts index 4aa2558480f..8c35ee5b269 100644 --- a/src/vs/editor/standalone/browser/quickInput/standaloneQuickInputService.ts +++ b/src/vs/editor/standalone/browser/quickInput/standaloneQuickInputService.ts @@ -8,13 +8,13 @@ import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, OverlayWidgetPosit import { EditorContributionInstantiation, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { IQuickInputService, IQuickInputButton, IQuickPickItem, IQuickPick, IInputBox, IQuickNavigateConfiguration, IPickOptions, QuickPickInput, IInputOptions } from 'vs/platform/quickinput/common/quickInput'; +import { IQuickInputService, IQuickInputButton, IQuickPickItem, IQuickPick, IInputBox, IQuickNavigateConfiguration, IPickOptions, QuickPickInput, IInputOptions, IQuickWidget } from 'vs/platform/quickinput/common/quickInput'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { EditorScopedLayoutService } from 'vs/editor/standalone/browser/standaloneLayoutService'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { IQuickInputControllerHost, QuickInputController } from 'vs/platform/quickinput/browser/quickInput'; +import { QuickInputController, IQuickInputControllerHost } from 'vs/platform/quickinput/browser/quickInputController'; import { QuickInputService } from 'vs/platform/quickinput/browser/quickInputService'; import { once } from 'vs/base/common/functional'; import { IQuickAccessController } from 'vs/platform/quickinput/common/quickAccess'; @@ -111,6 +111,10 @@ export class StandaloneQuickInputService implements IQuickInputService { return this.activeService.createInputBox(); } + createQuickWidget(): IQuickWidget { + return this.activeService.createQuickWidget(); + } + focus(): void { return this.activeService.focus(); } diff --git a/src/vs/platform/quickinput/browser/quickInput.ts b/src/vs/platform/quickinput/browser/quickInput.ts index 4f6affdefbb..072f2f56e89 100644 --- a/src/vs/platform/quickinput/browser/quickInput.ts +++ b/src/vs/platform/quickinput/browser/quickInput.ts @@ -6,7 +6,6 @@ import * as dom from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; -import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { Button, IButtonStyles } from 'vs/base/browser/ui/button/button'; import { CountBadge, ICountBadgeStyles } from 'vs/base/browser/ui/countBadge/countBadge'; import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; @@ -19,19 +18,16 @@ import { IToggleStyles, Toggle } from 'vs/base/browser/ui/toggle/toggle'; import { Action } from 'vs/base/common/actions'; import { equals } from 'vs/base/common/arrays'; import { TimeoutTimer } from 'vs/base/common/async'; -import { CancellationToken } from 'vs/base/common/cancellation'; import { Codicon } from 'vs/base/common/codicons'; import { Emitter, Event } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { Disposable, DisposableStore, dispose } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { isIOS } from 'vs/base/common/platform'; import Severity from 'vs/base/common/severity'; import { ThemeIcon } from 'vs/base/common/themables'; -import { isString } from 'vs/base/common/types'; import 'vs/css!./media/quickInput'; import { localize } from 'vs/nls'; -import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; -import { IInputBox, IInputOptions, IKeyMods, IPickOptions, IQuickInput, IQuickInputButton, IQuickInputHideEvent, IQuickInputToggle, IQuickNavigateConfiguration, IQuickPick, IQuickPickDidAcceptEvent, IQuickPickItem, IQuickPickItemButtonEvent, IQuickPickSeparator, IQuickPickSeparatorButtonEvent, IQuickPickWillAcceptEvent, ItemActivation, NO_KEY_MODS, QuickInputHideReason, QuickPickInput } from 'vs/platform/quickinput/common/quickInput'; +import { IInputBox, IKeyMods, IQuickInput, IQuickInputButton, IQuickInputHideEvent, IQuickInputToggle, IQuickNavigateConfiguration, IQuickPick, IQuickPickDidAcceptEvent, IQuickPickItem, IQuickPickItemButtonEvent, IQuickPickSeparator, IQuickPickSeparatorButtonEvent, IQuickPickWillAcceptEvent, IQuickWidget, ItemActivation, NO_KEY_MODS, QuickInputHideReason } from 'vs/platform/quickinput/common/quickInput'; import { QuickInputBox } from './quickInputBox'; import { QuickInputList, QuickInputListFocus } from './quickInputList'; import { getIconClass, renderQuickInputDescription } from './quickInputUtils'; @@ -77,17 +73,15 @@ export interface IQuickInputWidgetStyles { readonly widgetShadow: string | undefined; } -const $ = dom.$; +export type Writeable = { -readonly [P in keyof T]: T[P] }; -type Writeable = { -readonly [P in keyof T]: T[P] }; - -const backButton = { +export const backButton = { iconClass: ThemeIcon.asClassName(Codicon.quickInputBack), tooltip: localize('quickInput.back', "Back"), handle: -1 // TODO }; -interface QuickInputUI { +export interface QuickInputUI { container: HTMLElement; styleSheet: HTMLStyleElement; leftActionBar: ActionBar; @@ -125,7 +119,7 @@ interface QuickInputUI { hide(): void; } -type Visibilities = { +export type Visibilities = { title?: boolean; description?: boolean; checkAll?: boolean; @@ -496,7 +490,7 @@ class QuickInput extends Disposable implements IQuickInput { } } -class QuickPick extends QuickInput implements IQuickPick { +export class QuickPick extends QuickInput implements IQuickPick { private static readonly DEFAULT_ARIA_LABEL = localize('quickInputBox.ariaLabel', "Type to narrow down results."); @@ -1155,7 +1149,7 @@ class QuickPick extends QuickInput implements IQuickPi } } -class InputBox extends QuickInput implements IInputBox { +export class InputBox extends QuickInput implements IInputBox { private _value = ''; private _valueSelection: Readonly<[number, number]> | undefined; private valueSelectionUpdated = true; @@ -1262,704 +1256,19 @@ class InputBox extends QuickInput implements IInputBox { } } -export class QuickInputController extends Disposable { - private static readonly MAX_WIDTH = 600; // Max total width of quick input widget - - private idPrefix: string; - private ui: QuickInputUI | undefined; - private dimension?: dom.IDimension; - private titleBarOffset?: number; - private enabled = true; - private readonly onDidAcceptEmitter = this._register(new Emitter()); - private readonly onDidCustomEmitter = this._register(new Emitter()); - private readonly onDidTriggerButtonEmitter = this._register(new Emitter()); - private keyMods: Writeable = { ctrlCmd: false, alt: false }; - - private controller: QuickInput | null = null; - - private parentElement: HTMLElement; - private styles: IQuickInputStyles; - - private onShowEmitter = this._register(new Emitter()); - readonly onShow = this.onShowEmitter.event; - - private onHideEmitter = this._register(new Emitter()); - readonly onHide = this.onHideEmitter.event; - - private previousFocusElement?: HTMLElement; - - constructor(private options: IQuickInputOptions) { - super(); - this.idPrefix = options.idPrefix; - this.parentElement = options.container; - this.styles = options.styles; - this.registerKeyModsListeners(); - } - - private registerKeyModsListeners() { - const listener = (e: KeyboardEvent | MouseEvent) => { - this.keyMods.ctrlCmd = e.ctrlKey || e.metaKey; - this.keyMods.alt = e.altKey; - }; - this._register(dom.addDisposableListener(window, dom.EventType.KEY_DOWN, listener, true)); - this._register(dom.addDisposableListener(window, dom.EventType.KEY_UP, listener, true)); - this._register(dom.addDisposableListener(window, dom.EventType.MOUSE_DOWN, listener, true)); - } - - private getUI() { - if (this.ui) { - return this.ui; - } - - const container = dom.append(this.parentElement, $('.quick-input-widget.show-file-icons')); - container.tabIndex = -1; - container.style.display = 'none'; - - const styleSheet = dom.createStyleSheet(container); - - const titleBar = dom.append(container, $('.quick-input-titlebar')); - - const leftActionBar = this._register(new ActionBar(titleBar)); - leftActionBar.domNode.classList.add('quick-input-left-action-bar'); - - const title = dom.append(titleBar, $('.quick-input-title')); - - const rightActionBar = this._register(new ActionBar(titleBar)); - rightActionBar.domNode.classList.add('quick-input-right-action-bar'); - - const headerContainer = dom.append(container, $('.quick-input-header')); - - const checkAll = dom.append(headerContainer, $('input.quick-input-check-all')); - checkAll.type = 'checkbox'; - checkAll.setAttribute('aria-label', localize('quickInput.checkAll', "Toggle all checkboxes")); - this._register(dom.addStandardDisposableListener(checkAll, dom.EventType.CHANGE, e => { - const checked = checkAll.checked; - list.setAllVisibleChecked(checked); - })); - this._register(dom.addDisposableListener(checkAll, dom.EventType.CLICK, e => { - if (e.x || e.y) { // Avoid 'click' triggered by 'space'... - inputBox.setFocus(); - } - })); - - const description2 = dom.append(headerContainer, $('.quick-input-description')); - const inputContainer = dom.append(headerContainer, $('.quick-input-and-message')); - const filterContainer = dom.append(inputContainer, $('.quick-input-filter')); - - const inputBox = this._register(new QuickInputBox(filterContainer, this.styles.inputBox, this.styles.toggle)); - inputBox.setAttribute('aria-describedby', `${this.idPrefix}message`); - - const visibleCountContainer = dom.append(filterContainer, $('.quick-input-visible-count')); - visibleCountContainer.setAttribute('aria-live', 'polite'); - visibleCountContainer.setAttribute('aria-atomic', 'true'); - const visibleCount = new CountBadge(visibleCountContainer, { countFormat: localize({ key: 'quickInput.visibleCount', comment: ['This tells the user how many items are shown in a list of items to select from. The items can be anything. Currently not visible, but read by screen readers.'] }, "{0} Results") }, this.styles.countBadge); - - const countContainer = dom.append(filterContainer, $('.quick-input-count')); - countContainer.setAttribute('aria-live', 'polite'); - const count = new CountBadge(countContainer, { countFormat: localize({ key: 'quickInput.countSelected', comment: ['This tells the user how many items are selected in a list of items to select from. The items can be anything.'] }, "{0} Selected") }, this.styles.countBadge); - - const okContainer = dom.append(headerContainer, $('.quick-input-action')); - const ok = new Button(okContainer, this.styles.button); - ok.label = localize('ok', "OK"); - this._register(ok.onDidClick(e => { - this.onDidAcceptEmitter.fire(); - })); - - const customButtonContainer = dom.append(headerContainer, $('.quick-input-action')); - const customButton = new Button(customButtonContainer, this.styles.button); - customButton.label = localize('custom', "Custom"); - this._register(customButton.onDidClick(e => { - this.onDidCustomEmitter.fire(); - })); - - const message = dom.append(inputContainer, $(`#${this.idPrefix}message.quick-input-message`)); - - const progressBar = new ProgressBar(container, this.styles.progressBar); - progressBar.getContainer().classList.add('quick-input-progress'); - - const widget = dom.append(container, $('.quick-input-html-widget')); - widget.tabIndex = -1; - - const description1 = dom.append(container, $('.quick-input-description')); - - const listId = this.idPrefix + 'list'; - const list = this._register(new QuickInputList(container, listId, this.options)); - inputBox.setAttribute('aria-controls', listId); - this._register(list.onDidChangeFocus(() => { - inputBox.setAttribute('aria-activedescendant', list.getActiveDescendant() ?? ''); - })); - this._register(list.onChangedAllVisibleChecked(checked => { - checkAll.checked = checked; - })); - this._register(list.onChangedVisibleCount(c => { - visibleCount.setCount(c); - })); - this._register(list.onChangedCheckedCount(c => { - count.setCount(c); - })); - this._register(list.onLeave(() => { - // Defer to avoid the input field reacting to the triggering key. - setTimeout(() => { - inputBox.setFocus(); - if (this.controller instanceof QuickPick && this.controller.canSelectMany) { - list.clearFocus(); - } - }, 0); - })); - - const focusTracker = dom.trackFocus(container); - this._register(focusTracker); - this._register(dom.addDisposableListener(container, dom.EventType.FOCUS, e => { - // Ignore focus events within container - if (dom.isAncestor(e.relatedTarget as HTMLElement, container)) { - return; - } - this.previousFocusElement = e.relatedTarget instanceof HTMLElement ? e.relatedTarget : undefined; - }, true)); - this._register(focusTracker.onDidBlur(() => { - if (!this.getUI().ignoreFocusOut && !this.options.ignoreFocusOut()) { - this.hide(QuickInputHideReason.Blur); - } - this.previousFocusElement = undefined; - })); - this._register(dom.addDisposableListener(container, dom.EventType.FOCUS, (e: FocusEvent) => { - inputBox.setFocus(); - })); - // TODO: Turn into commands instead of handling KEY_DOWN - this._register(dom.addStandardDisposableListener(container, dom.EventType.KEY_DOWN, (event) => { - if (dom.isAncestor(event.target, widget)) { - return; // Ignore event if target is inside widget to allow the widget to handle the event. - } - switch (event.keyCode) { - case KeyCode.Enter: - dom.EventHelper.stop(event, true); - if (this.enabled) { - this.onDidAcceptEmitter.fire(); - } - break; - case KeyCode.Escape: - dom.EventHelper.stop(event, true); - this.hide(QuickInputHideReason.Gesture); - break; - case KeyCode.Tab: - if (!event.altKey && !event.ctrlKey && !event.metaKey) { - // detect only visible actions - const selectors = [ - '.quick-input-list .monaco-action-bar .always-visible', - '.quick-input-list-entry:hover .monaco-action-bar', - '.monaco-list-row.focused .monaco-action-bar' - ]; - - if (container.classList.contains('show-checkboxes')) { - selectors.push('input'); - } else { - selectors.push('input[type=text]'); - } - if (this.getUI().list.isDisplayed()) { - selectors.push('.monaco-list'); - } - // focus links if there are any - if (this.getUI().message) { - selectors.push('.quick-input-message a'); - } - - if (this.getUI().widget) { - if (dom.isAncestor(event.target, this.getUI().widget)) { - // let the widget control tab - break; - } - selectors.push('.quick-input-html-widget'); - } - const stops = container.querySelectorAll(selectors.join(', ')); - if (event.shiftKey && event.target === stops[0]) { - // Clear the focus from the list in order to allow - // screen readers to read operations in the input box. - dom.EventHelper.stop(event, true); - list.clearFocus(); - } else if (!event.shiftKey && dom.isAncestor(event.target, stops[stops.length - 1])) { - dom.EventHelper.stop(event, true); - stops[0].focus(); - } - } - break; - case KeyCode.Space: - if (event.ctrlKey) { - dom.EventHelper.stop(event, true); - this.getUI().list.toggleHover(); - } - break; - } - })); - - this.ui = { - container, - styleSheet, - leftActionBar, - titleBar, - title, - description1, - description2, - widget, - rightActionBar, - checkAll, - inputContainer, - filterContainer, - inputBox, - visibleCountContainer, - visibleCount, - countContainer, - count, - okContainer, - ok, - message, - customButtonContainer, - customButton, - list, - progressBar, - onDidAccept: this.onDidAcceptEmitter.event, - onDidCustom: this.onDidCustomEmitter.event, - onDidTriggerButton: this.onDidTriggerButtonEmitter.event, - ignoreFocusOut: false, - keyMods: this.keyMods, - show: controller => this.show(controller), - hide: () => this.hide(), - setVisibilities: visibilities => this.setVisibilities(visibilities), - setEnabled: enabled => this.setEnabled(enabled), - setContextKey: contextKey => this.options.setContextKey(contextKey), - linkOpenerDelegate: content => this.options.linkOpenerDelegate(content) - }; - this.updateStyles(); - return this.ui; - } - - pick>(picks: Promise[]> | QuickPickInput[], options: O = {}, token: CancellationToken = CancellationToken.None): Promise<(O extends { canPickMany: true } ? T[] : T) | undefined> { - type R = (O extends { canPickMany: true } ? T[] : T) | undefined; - return new Promise((doResolve, reject) => { - let resolve = (result: R) => { - resolve = doResolve; - options.onKeyMods?.(input.keyMods); - doResolve(result); - }; - if (token.isCancellationRequested) { - resolve(undefined); - return; - } - const input = this.createQuickPick(); - let activeItem: T | undefined; - const disposables = [ - input, - input.onDidAccept(() => { - if (input.canSelectMany) { - resolve(input.selectedItems.slice()); - input.hide(); - } else { - const result = input.activeItems[0]; - if (result) { - resolve(result); - input.hide(); - } - } - }), - input.onDidChangeActive(items => { - const focused = items[0]; - if (focused && options.onDidFocus) { - options.onDidFocus(focused); - } - }), - input.onDidChangeSelection(items => { - if (!input.canSelectMany) { - const result = items[0]; - if (result) { - resolve(result); - input.hide(); - } - } - }), - input.onDidTriggerItemButton(event => options.onDidTriggerItemButton && options.onDidTriggerItemButton({ - ...event, - removeItem: () => { - const index = input.items.indexOf(event.item); - if (index !== -1) { - const items = input.items.slice(); - const removed = items.splice(index, 1); - const activeItems = input.activeItems.filter(activeItem => activeItem !== removed[0]); - const keepScrollPositionBefore = input.keepScrollPosition; - input.keepScrollPosition = true; - input.items = items; - if (activeItems) { - input.activeItems = activeItems; - } - input.keepScrollPosition = keepScrollPositionBefore; - } - } - })), - input.onDidTriggerSeparatorButton(event => options.onDidTriggerSeparatorButton?.(event)), - input.onDidChangeValue(value => { - if (activeItem && !value && (input.activeItems.length !== 1 || input.activeItems[0] !== activeItem)) { - input.activeItems = [activeItem]; - } - }), - token.onCancellationRequested(() => { - input.hide(); - }), - input.onDidHide(() => { - dispose(disposables); - resolve(undefined); - }), - ]; - input.title = options.title; - input.canSelectMany = !!options.canPickMany; - input.placeholder = options.placeHolder; - input.ignoreFocusOut = !!options.ignoreFocusLost; - input.matchOnDescription = !!options.matchOnDescription; - input.matchOnDetail = !!options.matchOnDetail; - input.matchOnLabel = (options.matchOnLabel === undefined) || options.matchOnLabel; // default to true - input.autoFocusOnList = (options.autoFocusOnList === undefined) || options.autoFocusOnList; // default to true - input.quickNavigate = options.quickNavigate; - input.hideInput = !!options.hideInput; - input.contextKey = options.contextKey; - input.busy = true; - Promise.all([picks, options.activeItem]) - .then(([items, _activeItem]) => { - activeItem = _activeItem; - input.busy = false; - input.items = items; - if (input.canSelectMany) { - input.selectedItems = items.filter(item => item.type !== 'separator' && item.picked) as T[]; - } - if (activeItem) { - input.activeItems = [activeItem]; - } - }); - input.show(); - Promise.resolve(picks).then(undefined, err => { - reject(err); - input.hide(); - }); - }); - } - - private setValidationOnInput(input: IInputBox, validationResult: string | { - content: string; - severity: Severity; - } | null | undefined) { - if (validationResult && isString(validationResult)) { - input.severity = Severity.Error; - input.validationMessage = validationResult; - } else if (validationResult && !isString(validationResult)) { - input.severity = validationResult.severity; - input.validationMessage = validationResult.content; - } else { - input.severity = Severity.Ignore; - input.validationMessage = undefined; - } - } - - input(options: IInputOptions = {}, token: CancellationToken = CancellationToken.None): Promise { - return new Promise((resolve) => { - if (token.isCancellationRequested) { - resolve(undefined); - return; - } - const input = this.createInputBox(); - const validateInput = options.validateInput || (() => >Promise.resolve(undefined)); - const onDidValueChange = Event.debounce(input.onDidChangeValue, (last, cur) => cur, 100); - let validationValue = options.value || ''; - let validation = Promise.resolve(validateInput(validationValue)); - const disposables = [ - input, - onDidValueChange(value => { - if (value !== validationValue) { - validation = Promise.resolve(validateInput(value)); - validationValue = value; - } - validation.then(result => { - if (value === validationValue) { - this.setValidationOnInput(input, result); - } - }); - }), - input.onDidAccept(() => { - const value = input.value; - if (value !== validationValue) { - validation = Promise.resolve(validateInput(value)); - validationValue = value; - } - validation.then(result => { - if (!result || (!isString(result) && result.severity !== Severity.Error)) { - resolve(value); - input.hide(); - } else if (value === validationValue) { - this.setValidationOnInput(input, result); - } - }); - }), - token.onCancellationRequested(() => { - input.hide(); - }), - input.onDidHide(() => { - dispose(disposables); - resolve(undefined); - }), - ]; - - input.title = options.title; - input.value = options.value || ''; - input.valueSelection = options.valueSelection; - input.prompt = options.prompt; - input.placeholder = options.placeHolder; - input.password = !!options.password; - input.ignoreFocusOut = !!options.ignoreFocusLost; - input.show(); - }); - } - - backButton = backButton; - - createQuickPick(): IQuickPick { - const ui = this.getUI(); - return new QuickPick(ui); - } - - createInputBox(): IInputBox { - const ui = this.getUI(); - return new InputBox(ui); - } - - private show(controller: QuickInput) { - const ui = this.getUI(); - this.onShowEmitter.fire(); - const oldController = this.controller; - this.controller = controller; - oldController?.didHide(); - - this.setEnabled(true); - ui.leftActionBar.clear(); - ui.title.textContent = ''; - ui.description1.textContent = ''; - ui.description2.textContent = ''; - dom.reset(ui.widget); - ui.rightActionBar.clear(); - ui.checkAll.checked = false; - // ui.inputBox.value = ''; Avoid triggering an event. - ui.inputBox.placeholder = ''; - ui.inputBox.password = false; - ui.inputBox.showDecoration(Severity.Ignore); - ui.visibleCount.setCount(0); - ui.count.setCount(0); - dom.reset(ui.message); - ui.progressBar.stop(); - ui.list.setElements([]); - ui.list.matchOnDescription = false; - ui.list.matchOnDetail = false; - ui.list.matchOnLabel = true; - ui.list.sortByLabel = true; - ui.ignoreFocusOut = false; - ui.inputBox.toggles = undefined; - - const backKeybindingLabel = this.options.backKeybindingLabel(); - backButton.tooltip = backKeybindingLabel ? localize('quickInput.backWithKeybinding', "Back ({0})", backKeybindingLabel) : localize('quickInput.back', "Back"); - - ui.container.style.display = ''; - this.updateLayout(); - ui.inputBox.setFocus(); - } - - private setVisibilities(visibilities: Visibilities) { - const ui = this.getUI(); - ui.title.style.display = visibilities.title ? '' : 'none'; - ui.description1.style.display = visibilities.description && (visibilities.inputBox || visibilities.checkAll) ? '' : 'none'; - ui.description2.style.display = visibilities.description && !(visibilities.inputBox || visibilities.checkAll) ? '' : 'none'; - ui.checkAll.style.display = visibilities.checkAll ? '' : 'none'; - ui.inputContainer.style.display = visibilities.inputBox ? '' : 'none'; - ui.filterContainer.style.display = visibilities.inputBox ? '' : 'none'; - ui.visibleCountContainer.style.display = visibilities.visibleCount ? '' : 'none'; - ui.countContainer.style.display = visibilities.count ? '' : 'none'; - ui.okContainer.style.display = visibilities.ok ? '' : 'none'; - ui.customButtonContainer.style.display = visibilities.customButton ? '' : 'none'; - ui.message.style.display = visibilities.message ? '' : 'none'; - ui.progressBar.getContainer().style.display = visibilities.progressBar ? '' : 'none'; - ui.list.display(!!visibilities.list); - ui.container.classList.toggle('show-checkboxes', !!visibilities.checkBox); - ui.container.classList.toggle('hidden-input', !visibilities.inputBox && !visibilities.description); - this.updateLayout(); // TODO - } - - private setEnabled(enabled: boolean) { - if (enabled !== this.enabled) { - this.enabled = enabled; - for (const item of this.getUI().leftActionBar.viewItems) { - (item as ActionViewItem).action.enabled = enabled; - } - for (const item of this.getUI().rightActionBar.viewItems) { - (item as ActionViewItem).action.enabled = enabled; - } - this.getUI().checkAll.disabled = !enabled; - this.getUI().inputBox.enabled = enabled; - this.getUI().ok.enabled = enabled; - this.getUI().list.enabled = enabled; - } - } - - hide(reason?: QuickInputHideReason) { - const controller = this.controller; - if (!controller) { +export class QuickWidget extends QuickInput implements IQuickWidget { + protected override update() { + if (!this.visible) { return; } - const focusChanged = !dom.isAncestor(document.activeElement, this.ui?.container ?? null); - this.controller = null; - this.onHideEmitter.fire(); - this.getUI().container.style.display = 'none'; - if (!focusChanged) { - let currentElement = this.previousFocusElement; - while (currentElement && !currentElement.offsetParent) { - currentElement = currentElement.parentElement ?? undefined; - } - if (currentElement?.offsetParent) { - currentElement.focus(); - this.previousFocusElement = undefined; - } else { - this.options.returnFocus(); - } - } - controller.didHide(reason); - } + const visibilities: Visibilities = { + title: !!this.title || !!this.step || !!this.buttons.length, + description: !!this.description || !!this.step, + progressBar: true + }; - focus() { - if (this.isDisplayed()) { - const ui = this.getUI(); - if (ui.inputBox.enabled) { - ui.inputBox.setFocus(); - } else { - ui.list.domFocus(); - } - } - } - - toggle() { - if (this.isDisplayed() && this.controller instanceof QuickPick && this.controller.canSelectMany) { - this.getUI().list.toggleCheckbox(); - } - } - - navigate(next: boolean, quickNavigate?: IQuickNavigateConfiguration) { - if (this.isDisplayed() && this.getUI().list.isDisplayed()) { - this.getUI().list.focus(next ? QuickInputListFocus.Next : QuickInputListFocus.Previous); - if (quickNavigate && this.controller instanceof QuickPick) { - this.controller.quickNavigate = quickNavigate; - } - } - } - - async accept(keyMods: IKeyMods = { alt: false, ctrlCmd: false }) { - // When accepting the item programmatically, it is important that - // we update `keyMods` either from the provided set or unset it - // because the accept did not happen from mouse or keyboard - // interaction on the list itself - this.keyMods.alt = keyMods.alt; - this.keyMods.ctrlCmd = keyMods.ctrlCmd; - - this.onDidAcceptEmitter.fire(); - } - - async back() { - this.onDidTriggerButtonEmitter.fire(this.backButton); - } - - async cancel() { - this.hide(); - } - - layout(dimension: dom.IDimension, titleBarOffset: number): void { - this.dimension = dimension; - this.titleBarOffset = titleBarOffset; - this.updateLayout(); - } - - private updateLayout() { - if (this.ui && this.isDisplayed()) { - this.ui.container.style.top = `${this.titleBarOffset}px`; - - const style = this.ui.container.style; - const width = Math.min(this.dimension!.width * 0.62 /* golden cut */, QuickInputController.MAX_WIDTH); - style.width = width + 'px'; - style.marginLeft = '-' + (width / 2) + 'px'; - - this.ui.inputBox.layout(); - this.ui.list.layout(this.dimension && this.dimension.height * 0.4); - } - } - - applyStyles(styles: IQuickInputStyles) { - this.styles = styles; - this.updateStyles(); - } - - private updateStyles() { - if (this.ui) { - const { - quickInputTitleBackground, - quickInputBackground, - quickInputForeground, - widgetBorder, - widgetShadow, - } = this.styles.widget; - this.ui.titleBar.style.backgroundColor = quickInputTitleBackground ?? ''; - this.ui.container.style.backgroundColor = quickInputBackground ?? ''; - this.ui.container.style.color = quickInputForeground ?? ''; - this.ui.container.style.border = widgetBorder ? `1px solid ${widgetBorder}` : ''; - this.ui.container.style.boxShadow = widgetShadow ? `0 0 8px 2px ${widgetShadow}` : ''; - this.ui.list.style(this.styles.list); - - const content: string[] = []; - if (this.styles.pickerGroup.pickerGroupBorder) { - content.push(`.quick-input-list .quick-input-list-entry { border-top-color: ${this.styles.pickerGroup.pickerGroupBorder}; }`); - } - if (this.styles.pickerGroup.pickerGroupForeground) { - content.push(`.quick-input-list .quick-input-list-separator { color: ${this.styles.pickerGroup.pickerGroupForeground}; }`); - } - if (this.styles.pickerGroup.pickerGroupForeground) { - content.push(`.quick-input-list .quick-input-list-separator-as-item { color: ${this.styles.pickerGroup.pickerGroupForeground}; }`); - } - - if ( - this.styles.keybindingLabel.keybindingLabelBackground || - this.styles.keybindingLabel.keybindingLabelBorder || - this.styles.keybindingLabel.keybindingLabelBottomBorder || - this.styles.keybindingLabel.keybindingLabelShadow || - this.styles.keybindingLabel.keybindingLabelForeground - ) { - content.push('.quick-input-list .monaco-keybinding > .monaco-keybinding-key {'); - if (this.styles.keybindingLabel.keybindingLabelBackground) { - content.push(`background-color: ${this.styles.keybindingLabel.keybindingLabelBackground};`); - } - if (this.styles.keybindingLabel.keybindingLabelBorder) { - // Order matters here. `border-color` must come before `border-bottom-color`. - content.push(`border-color: ${this.styles.keybindingLabel.keybindingLabelBorder};`); - } - if (this.styles.keybindingLabel.keybindingLabelBottomBorder) { - content.push(`border-bottom-color: ${this.styles.keybindingLabel.keybindingLabelBottomBorder};`); - } - if (this.styles.keybindingLabel.keybindingLabelShadow) { - content.push(`box-shadow: inset 0 -1px 0 ${this.styles.keybindingLabel.keybindingLabelShadow};`); - } - if (this.styles.keybindingLabel.keybindingLabelForeground) { - content.push(`color: ${this.styles.keybindingLabel.keybindingLabelForeground};`); - } - content.push('}'); - } - - const newStyles = content.join('\n'); - if (newStyles !== this.ui.styleSheet.textContent) { - this.ui.styleSheet.textContent = newStyles; - } - } - } - - private isDisplayed() { - return this.ui && this.ui.container.style.display !== 'none'; + this.ui.setVisibilities(visibilities); + super.update(); } } - -export interface IQuickInputControllerHost extends ILayoutService { } diff --git a/src/vs/platform/quickinput/browser/quickInputController.ts b/src/vs/platform/quickinput/browser/quickInputController.ts new file mode 100644 index 00000000000..0328a6a6ded --- /dev/null +++ b/src/vs/platform/quickinput/browser/quickInputController.ts @@ -0,0 +1,726 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as dom from 'vs/base/browser/dom'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { Button } from 'vs/base/browser/ui/button/button'; +import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; +import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { Emitter, Event } from 'vs/base/common/event'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { Disposable, dispose } from 'vs/base/common/lifecycle'; +import Severity from 'vs/base/common/severity'; +import { isString } from 'vs/base/common/types'; +import { localize } from 'vs/nls'; +import { IInputBox, IInputOptions, IKeyMods, IPickOptions, IQuickInput, IQuickInputButton, IQuickNavigateConfiguration, IQuickPick, IQuickPickItem, IQuickWidget, QuickInputHideReason, QuickPickInput } from 'vs/platform/quickinput/common/quickInput'; +import { QuickInputBox } from 'vs/platform/quickinput/browser/quickInputBox'; +import { QuickInputList, QuickInputListFocus } from 'vs/platform/quickinput/browser/quickInputList'; +import { QuickInputUI, Writeable, IQuickInputStyles, IQuickInputOptions, QuickPick, backButton, InputBox, Visibilities, QuickWidget } from 'vs/platform/quickinput/browser/quickInput'; +import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; + +const $ = dom.$; + +export class QuickInputController extends Disposable { + private static readonly MAX_WIDTH = 600; // Max total width of quick input widget + + private idPrefix: string; + private ui: QuickInputUI | undefined; + private dimension?: dom.IDimension; + private titleBarOffset?: number; + private enabled = true; + private readonly onDidAcceptEmitter = this._register(new Emitter()); + private readonly onDidCustomEmitter = this._register(new Emitter()); + private readonly onDidTriggerButtonEmitter = this._register(new Emitter()); + private keyMods: Writeable = { ctrlCmd: false, alt: false }; + + private controller: IQuickInput | null = null; + + private parentElement: HTMLElement; + private styles: IQuickInputStyles; + + private onShowEmitter = this._register(new Emitter()); + readonly onShow = this.onShowEmitter.event; + + private onHideEmitter = this._register(new Emitter()); + readonly onHide = this.onHideEmitter.event; + + private previousFocusElement?: HTMLElement; + + constructor(private options: IQuickInputOptions) { + super(); + this.idPrefix = options.idPrefix; + this.parentElement = options.container; + this.styles = options.styles; + this.registerKeyModsListeners(); + } + + private registerKeyModsListeners() { + const listener = (e: KeyboardEvent | MouseEvent) => { + this.keyMods.ctrlCmd = e.ctrlKey || e.metaKey; + this.keyMods.alt = e.altKey; + }; + this._register(dom.addDisposableListener(window, dom.EventType.KEY_DOWN, listener, true)); + this._register(dom.addDisposableListener(window, dom.EventType.KEY_UP, listener, true)); + this._register(dom.addDisposableListener(window, dom.EventType.MOUSE_DOWN, listener, true)); + } + + private getUI() { + if (this.ui) { + return this.ui; + } + + const container = dom.append(this.parentElement, $('.quick-input-widget.show-file-icons')); + container.tabIndex = -1; + container.style.display = 'none'; + + const styleSheet = dom.createStyleSheet(container); + + const titleBar = dom.append(container, $('.quick-input-titlebar')); + + const leftActionBar = this._register(new ActionBar(titleBar)); + leftActionBar.domNode.classList.add('quick-input-left-action-bar'); + + const title = dom.append(titleBar, $('.quick-input-title')); + + const rightActionBar = this._register(new ActionBar(titleBar)); + rightActionBar.domNode.classList.add('quick-input-right-action-bar'); + + const headerContainer = dom.append(container, $('.quick-input-header')); + + const checkAll = dom.append(headerContainer, $('input.quick-input-check-all')); + checkAll.type = 'checkbox'; + checkAll.setAttribute('aria-label', localize('quickInput.checkAll', "Toggle all checkboxes")); + this._register(dom.addStandardDisposableListener(checkAll, dom.EventType.CHANGE, e => { + const checked = checkAll.checked; + list.setAllVisibleChecked(checked); + })); + this._register(dom.addDisposableListener(checkAll, dom.EventType.CLICK, e => { + if (e.x || e.y) { // Avoid 'click' triggered by 'space'... + inputBox.setFocus(); + } + })); + + const description2 = dom.append(headerContainer, $('.quick-input-description')); + const inputContainer = dom.append(headerContainer, $('.quick-input-and-message')); + const filterContainer = dom.append(inputContainer, $('.quick-input-filter')); + + const inputBox = this._register(new QuickInputBox(filterContainer, this.styles.inputBox, this.styles.toggle)); + inputBox.setAttribute('aria-describedby', `${this.idPrefix}message`); + + const visibleCountContainer = dom.append(filterContainer, $('.quick-input-visible-count')); + visibleCountContainer.setAttribute('aria-live', 'polite'); + visibleCountContainer.setAttribute('aria-atomic', 'true'); + const visibleCount = new CountBadge(visibleCountContainer, { countFormat: localize({ key: 'quickInput.visibleCount', comment: ['This tells the user how many items are shown in a list of items to select from. The items can be anything. Currently not visible, but read by screen readers.'] }, "{0} Results") }, this.styles.countBadge); + + const countContainer = dom.append(filterContainer, $('.quick-input-count')); + countContainer.setAttribute('aria-live', 'polite'); + const count = new CountBadge(countContainer, { countFormat: localize({ key: 'quickInput.countSelected', comment: ['This tells the user how many items are selected in a list of items to select from. The items can be anything.'] }, "{0} Selected") }, this.styles.countBadge); + + const okContainer = dom.append(headerContainer, $('.quick-input-action')); + const ok = new Button(okContainer, this.styles.button); + ok.label = localize('ok', "OK"); + this._register(ok.onDidClick(e => { + this.onDidAcceptEmitter.fire(); + })); + + const customButtonContainer = dom.append(headerContainer, $('.quick-input-action')); + const customButton = new Button(customButtonContainer, this.styles.button); + customButton.label = localize('custom', "Custom"); + this._register(customButton.onDidClick(e => { + this.onDidCustomEmitter.fire(); + })); + + const message = dom.append(inputContainer, $(`#${this.idPrefix}message.quick-input-message`)); + + const progressBar = new ProgressBar(container, this.styles.progressBar); + progressBar.getContainer().classList.add('quick-input-progress'); + + const widget = dom.append(container, $('.quick-input-html-widget')); + widget.tabIndex = -1; + + const description1 = dom.append(container, $('.quick-input-description')); + + const listId = this.idPrefix + 'list'; + const list = this._register(new QuickInputList(container, listId, this.options)); + inputBox.setAttribute('aria-controls', listId); + this._register(list.onDidChangeFocus(() => { + inputBox.setAttribute('aria-activedescendant', list.getActiveDescendant() ?? ''); + })); + this._register(list.onChangedAllVisibleChecked(checked => { + checkAll.checked = checked; + })); + this._register(list.onChangedVisibleCount(c => { + visibleCount.setCount(c); + })); + this._register(list.onChangedCheckedCount(c => { + count.setCount(c); + })); + this._register(list.onLeave(() => { + // Defer to avoid the input field reacting to the triggering key. + setTimeout(() => { + inputBox.setFocus(); + if (this.controller instanceof QuickPick && this.controller.canSelectMany) { + list.clearFocus(); + } + }, 0); + })); + + const focusTracker = dom.trackFocus(container); + this._register(focusTracker); + this._register(dom.addDisposableListener(container, dom.EventType.FOCUS, e => { + // Ignore focus events within container + if (dom.isAncestor(e.relatedTarget as HTMLElement, container)) { + return; + } + this.previousFocusElement = e.relatedTarget instanceof HTMLElement ? e.relatedTarget : undefined; + }, true)); + this._register(focusTracker.onDidBlur(() => { + if (!this.getUI().ignoreFocusOut && !this.options.ignoreFocusOut()) { + this.hide(QuickInputHideReason.Blur); + } + this.previousFocusElement = undefined; + })); + this._register(dom.addDisposableListener(container, dom.EventType.FOCUS, (e: FocusEvent) => { + inputBox.setFocus(); + })); + // TODO: Turn into commands instead of handling KEY_DOWN + this._register(dom.addStandardDisposableListener(container, dom.EventType.KEY_DOWN, (event) => { + if (dom.isAncestor(event.target, widget)) { + return; // Ignore event if target is inside widget to allow the widget to handle the event. + } + switch (event.keyCode) { + case KeyCode.Enter: + dom.EventHelper.stop(event, true); + if (this.enabled) { + this.onDidAcceptEmitter.fire(); + } + break; + case KeyCode.Escape: + dom.EventHelper.stop(event, true); + this.hide(QuickInputHideReason.Gesture); + break; + case KeyCode.Tab: + if (!event.altKey && !event.ctrlKey && !event.metaKey) { + // detect only visible actions + const selectors = [ + '.quick-input-list .monaco-action-bar .always-visible', + '.quick-input-list-entry:hover .monaco-action-bar', + '.monaco-list-row.focused .monaco-action-bar' + ]; + + if (container.classList.contains('show-checkboxes')) { + selectors.push('input'); + } else { + selectors.push('input[type=text]'); + } + if (this.getUI().list.isDisplayed()) { + selectors.push('.monaco-list'); + } + // focus links if there are any + if (this.getUI().message) { + selectors.push('.quick-input-message a'); + } + + if (this.getUI().widget) { + if (dom.isAncestor(event.target, this.getUI().widget)) { + // let the widget control tab + break; + } + selectors.push('.quick-input-html-widget'); + } + const stops = container.querySelectorAll(selectors.join(', ')); + if (event.shiftKey && event.target === stops[0]) { + // Clear the focus from the list in order to allow + // screen readers to read operations in the input box. + dom.EventHelper.stop(event, true); + list.clearFocus(); + } else if (!event.shiftKey && dom.isAncestor(event.target, stops[stops.length - 1])) { + dom.EventHelper.stop(event, true); + stops[0].focus(); + } + } + break; + case KeyCode.Space: + if (event.ctrlKey) { + dom.EventHelper.stop(event, true); + this.getUI().list.toggleHover(); + } + break; + } + })); + + this.ui = { + container, + styleSheet, + leftActionBar, + titleBar, + title, + description1, + description2, + widget, + rightActionBar, + checkAll, + inputContainer, + filterContainer, + inputBox, + visibleCountContainer, + visibleCount, + countContainer, + count, + okContainer, + ok, + message, + customButtonContainer, + customButton, + list, + progressBar, + onDidAccept: this.onDidAcceptEmitter.event, + onDidCustom: this.onDidCustomEmitter.event, + onDidTriggerButton: this.onDidTriggerButtonEmitter.event, + ignoreFocusOut: false, + keyMods: this.keyMods, + show: controller => this.show(controller), + hide: () => this.hide(), + setVisibilities: visibilities => this.setVisibilities(visibilities), + setEnabled: enabled => this.setEnabled(enabled), + setContextKey: contextKey => this.options.setContextKey(contextKey), + linkOpenerDelegate: content => this.options.linkOpenerDelegate(content) + }; + this.updateStyles(); + return this.ui; + } + + pick>(picks: Promise[]> | QuickPickInput[], options: O = {}, token: CancellationToken = CancellationToken.None): Promise<(O extends { canPickMany: true } ? T[] : T) | undefined> { + type R = (O extends { canPickMany: true } ? T[] : T) | undefined; + return new Promise((doResolve, reject) => { + let resolve = (result: R) => { + resolve = doResolve; + options.onKeyMods?.(input.keyMods); + doResolve(result); + }; + if (token.isCancellationRequested) { + resolve(undefined); + return; + } + const input = this.createQuickPick(); + let activeItem: T | undefined; + const disposables = [ + input, + input.onDidAccept(() => { + if (input.canSelectMany) { + resolve(input.selectedItems.slice()); + input.hide(); + } else { + const result = input.activeItems[0]; + if (result) { + resolve(result); + input.hide(); + } + } + }), + input.onDidChangeActive(items => { + const focused = items[0]; + if (focused && options.onDidFocus) { + options.onDidFocus(focused); + } + }), + input.onDidChangeSelection(items => { + if (!input.canSelectMany) { + const result = items[0]; + if (result) { + resolve(result); + input.hide(); + } + } + }), + input.onDidTriggerItemButton(event => options.onDidTriggerItemButton && options.onDidTriggerItemButton({ + ...event, + removeItem: () => { + const index = input.items.indexOf(event.item); + if (index !== -1) { + const items = input.items.slice(); + const removed = items.splice(index, 1); + const activeItems = input.activeItems.filter(activeItem => activeItem !== removed[0]); + const keepScrollPositionBefore = input.keepScrollPosition; + input.keepScrollPosition = true; + input.items = items; + if (activeItems) { + input.activeItems = activeItems; + } + input.keepScrollPosition = keepScrollPositionBefore; + } + } + })), + input.onDidTriggerSeparatorButton(event => options.onDidTriggerSeparatorButton?.(event)), + input.onDidChangeValue(value => { + if (activeItem && !value && (input.activeItems.length !== 1 || input.activeItems[0] !== activeItem)) { + input.activeItems = [activeItem]; + } + }), + token.onCancellationRequested(() => { + input.hide(); + }), + input.onDidHide(() => { + dispose(disposables); + resolve(undefined); + }), + ]; + input.title = options.title; + input.canSelectMany = !!options.canPickMany; + input.placeholder = options.placeHolder; + input.ignoreFocusOut = !!options.ignoreFocusLost; + input.matchOnDescription = !!options.matchOnDescription; + input.matchOnDetail = !!options.matchOnDetail; + input.matchOnLabel = (options.matchOnLabel === undefined) || options.matchOnLabel; // default to true + input.autoFocusOnList = (options.autoFocusOnList === undefined) || options.autoFocusOnList; // default to true + input.quickNavigate = options.quickNavigate; + input.hideInput = !!options.hideInput; + input.contextKey = options.contextKey; + input.busy = true; + Promise.all([picks, options.activeItem]) + .then(([items, _activeItem]) => { + activeItem = _activeItem; + input.busy = false; + input.items = items; + if (input.canSelectMany) { + input.selectedItems = items.filter(item => item.type !== 'separator' && item.picked) as T[]; + } + if (activeItem) { + input.activeItems = [activeItem]; + } + }); + input.show(); + Promise.resolve(picks).then(undefined, err => { + reject(err); + input.hide(); + }); + }); + } + + private setValidationOnInput(input: IInputBox, validationResult: string | { + content: string; + severity: Severity; + } | null | undefined) { + if (validationResult && isString(validationResult)) { + input.severity = Severity.Error; + input.validationMessage = validationResult; + } else if (validationResult && !isString(validationResult)) { + input.severity = validationResult.severity; + input.validationMessage = validationResult.content; + } else { + input.severity = Severity.Ignore; + input.validationMessage = undefined; + } + } + + input(options: IInputOptions = {}, token: CancellationToken = CancellationToken.None): Promise { + return new Promise((resolve) => { + if (token.isCancellationRequested) { + resolve(undefined); + return; + } + const input = this.createInputBox(); + const validateInput = options.validateInput || (() => >Promise.resolve(undefined)); + const onDidValueChange = Event.debounce(input.onDidChangeValue, (last, cur) => cur, 100); + let validationValue = options.value || ''; + let validation = Promise.resolve(validateInput(validationValue)); + const disposables = [ + input, + onDidValueChange(value => { + if (value !== validationValue) { + validation = Promise.resolve(validateInput(value)); + validationValue = value; + } + validation.then(result => { + if (value === validationValue) { + this.setValidationOnInput(input, result); + } + }); + }), + input.onDidAccept(() => { + const value = input.value; + if (value !== validationValue) { + validation = Promise.resolve(validateInput(value)); + validationValue = value; + } + validation.then(result => { + if (!result || (!isString(result) && result.severity !== Severity.Error)) { + resolve(value); + input.hide(); + } else if (value === validationValue) { + this.setValidationOnInput(input, result); + } + }); + }), + token.onCancellationRequested(() => { + input.hide(); + }), + input.onDidHide(() => { + dispose(disposables); + resolve(undefined); + }), + ]; + + input.title = options.title; + input.value = options.value || ''; + input.valueSelection = options.valueSelection; + input.prompt = options.prompt; + input.placeholder = options.placeHolder; + input.password = !!options.password; + input.ignoreFocusOut = !!options.ignoreFocusLost; + input.show(); + }); + } + + backButton = backButton; + + createQuickPick(): IQuickPick { + const ui = this.getUI(); + return new QuickPick(ui); + } + + createInputBox(): IInputBox { + const ui = this.getUI(); + return new InputBox(ui); + } + + createQuickWidget(): IQuickWidget { + const ui = this.getUI(); + return new QuickWidget(ui); + } + + private show(controller: IQuickInput) { + const ui = this.getUI(); + this.onShowEmitter.fire(); + const oldController = this.controller; + this.controller = controller; + oldController?.didHide(); + + this.setEnabled(true); + ui.leftActionBar.clear(); + ui.title.textContent = ''; + ui.description1.textContent = ''; + ui.description2.textContent = ''; + dom.reset(ui.widget); + ui.rightActionBar.clear(); + ui.checkAll.checked = false; + // ui.inputBox.value = ''; Avoid triggering an event. + ui.inputBox.placeholder = ''; + ui.inputBox.password = false; + ui.inputBox.showDecoration(Severity.Ignore); + ui.visibleCount.setCount(0); + ui.count.setCount(0); + dom.reset(ui.message); + ui.progressBar.stop(); + ui.list.setElements([]); + ui.list.matchOnDescription = false; + ui.list.matchOnDetail = false; + ui.list.matchOnLabel = true; + ui.list.sortByLabel = true; + ui.ignoreFocusOut = false; + ui.inputBox.toggles = undefined; + + const backKeybindingLabel = this.options.backKeybindingLabel(); + backButton.tooltip = backKeybindingLabel ? localize('quickInput.backWithKeybinding', "Back ({0})", backKeybindingLabel) : localize('quickInput.back', "Back"); + + ui.container.style.display = ''; + this.updateLayout(); + ui.inputBox.setFocus(); + } + + private setVisibilities(visibilities: Visibilities) { + const ui = this.getUI(); + ui.title.style.display = visibilities.title ? '' : 'none'; + ui.description1.style.display = visibilities.description && (visibilities.inputBox || visibilities.checkAll) ? '' : 'none'; + ui.description2.style.display = visibilities.description && !(visibilities.inputBox || visibilities.checkAll) ? '' : 'none'; + ui.checkAll.style.display = visibilities.checkAll ? '' : 'none'; + ui.inputContainer.style.display = visibilities.inputBox ? '' : 'none'; + ui.filterContainer.style.display = visibilities.inputBox ? '' : 'none'; + ui.visibleCountContainer.style.display = visibilities.visibleCount ? '' : 'none'; + ui.countContainer.style.display = visibilities.count ? '' : 'none'; + ui.okContainer.style.display = visibilities.ok ? '' : 'none'; + ui.customButtonContainer.style.display = visibilities.customButton ? '' : 'none'; + ui.message.style.display = visibilities.message ? '' : 'none'; + ui.progressBar.getContainer().style.display = visibilities.progressBar ? '' : 'none'; + ui.list.display(!!visibilities.list); + ui.container.classList.toggle('show-checkboxes', !!visibilities.checkBox); + ui.container.classList.toggle('hidden-input', !visibilities.inputBox && !visibilities.description); + this.updateLayout(); // TODO + } + + private setEnabled(enabled: boolean) { + if (enabled !== this.enabled) { + this.enabled = enabled; + for (const item of this.getUI().leftActionBar.viewItems) { + (item as ActionViewItem).action.enabled = enabled; + } + for (const item of this.getUI().rightActionBar.viewItems) { + (item as ActionViewItem).action.enabled = enabled; + } + this.getUI().checkAll.disabled = !enabled; + this.getUI().inputBox.enabled = enabled; + this.getUI().ok.enabled = enabled; + this.getUI().list.enabled = enabled; + } + } + + hide(reason?: QuickInputHideReason) { + const controller = this.controller; + if (!controller) { + return; + } + + const focusChanged = !dom.isAncestor(document.activeElement, this.ui?.container ?? null); + this.controller = null; + this.onHideEmitter.fire(); + this.getUI().container.style.display = 'none'; + if (!focusChanged) { + let currentElement = this.previousFocusElement; + while (currentElement && !currentElement.offsetParent) { + currentElement = currentElement.parentElement ?? undefined; + } + if (currentElement?.offsetParent) { + currentElement.focus(); + this.previousFocusElement = undefined; + } else { + this.options.returnFocus(); + } + } + controller.didHide(reason); + } + + focus() { + if (this.isDisplayed()) { + const ui = this.getUI(); + if (ui.inputBox.enabled) { + ui.inputBox.setFocus(); + } else { + ui.list.domFocus(); + } + } + } + + toggle() { + if (this.isDisplayed() && this.controller instanceof QuickPick && this.controller.canSelectMany) { + this.getUI().list.toggleCheckbox(); + } + } + + navigate(next: boolean, quickNavigate?: IQuickNavigateConfiguration) { + if (this.isDisplayed() && this.getUI().list.isDisplayed()) { + this.getUI().list.focus(next ? QuickInputListFocus.Next : QuickInputListFocus.Previous); + if (quickNavigate && this.controller instanceof QuickPick) { + this.controller.quickNavigate = quickNavigate; + } + } + } + + async accept(keyMods: IKeyMods = { alt: false, ctrlCmd: false }) { + // When accepting the item programmatically, it is important that + // we update `keyMods` either from the provided set or unset it + // because the accept did not happen from mouse or keyboard + // interaction on the list itself + this.keyMods.alt = keyMods.alt; + this.keyMods.ctrlCmd = keyMods.ctrlCmd; + + this.onDidAcceptEmitter.fire(); + } + + async back() { + this.onDidTriggerButtonEmitter.fire(this.backButton); + } + + async cancel() { + this.hide(); + } + + layout(dimension: dom.IDimension, titleBarOffset: number): void { + this.dimension = dimension; + this.titleBarOffset = titleBarOffset; + this.updateLayout(); + } + + private updateLayout() { + if (this.ui && this.isDisplayed()) { + this.ui.container.style.top = `${this.titleBarOffset}px`; + + const style = this.ui.container.style; + const width = Math.min(this.dimension!.width * 0.62 /* golden cut */, QuickInputController.MAX_WIDTH); + style.width = width + 'px'; + style.marginLeft = '-' + (width / 2) + 'px'; + + this.ui.inputBox.layout(); + this.ui.list.layout(this.dimension && this.dimension.height * 0.4); + } + } + + applyStyles(styles: IQuickInputStyles) { + this.styles = styles; + this.updateStyles(); + } + + private updateStyles() { + if (this.ui) { + const { + quickInputTitleBackground, quickInputBackground, quickInputForeground, widgetBorder, widgetShadow, + } = this.styles.widget; + this.ui.titleBar.style.backgroundColor = quickInputTitleBackground ?? ''; + this.ui.container.style.backgroundColor = quickInputBackground ?? ''; + this.ui.container.style.color = quickInputForeground ?? ''; + this.ui.container.style.border = widgetBorder ? `1px solid ${widgetBorder}` : ''; + this.ui.container.style.boxShadow = widgetShadow ? `0 0 8px 2px ${widgetShadow}` : ''; + this.ui.list.style(this.styles.list); + + const content: string[] = []; + if (this.styles.pickerGroup.pickerGroupBorder) { + content.push(`.quick-input-list .quick-input-list-entry { border-top-color: ${this.styles.pickerGroup.pickerGroupBorder}; }`); + } + if (this.styles.pickerGroup.pickerGroupForeground) { + content.push(`.quick-input-list .quick-input-list-separator { color: ${this.styles.pickerGroup.pickerGroupForeground}; }`); + } + if (this.styles.pickerGroup.pickerGroupForeground) { + content.push(`.quick-input-list .quick-input-list-separator-as-item { color: ${this.styles.pickerGroup.pickerGroupForeground}; }`); + } + + if (this.styles.keybindingLabel.keybindingLabelBackground || + this.styles.keybindingLabel.keybindingLabelBorder || + this.styles.keybindingLabel.keybindingLabelBottomBorder || + this.styles.keybindingLabel.keybindingLabelShadow || + this.styles.keybindingLabel.keybindingLabelForeground) { + content.push('.quick-input-list .monaco-keybinding > .monaco-keybinding-key {'); + if (this.styles.keybindingLabel.keybindingLabelBackground) { + content.push(`background-color: ${this.styles.keybindingLabel.keybindingLabelBackground};`); + } + if (this.styles.keybindingLabel.keybindingLabelBorder) { + // Order matters here. `border-color` must come before `border-bottom-color`. + content.push(`border-color: ${this.styles.keybindingLabel.keybindingLabelBorder};`); + } + if (this.styles.keybindingLabel.keybindingLabelBottomBorder) { + content.push(`border-bottom-color: ${this.styles.keybindingLabel.keybindingLabelBottomBorder};`); + } + if (this.styles.keybindingLabel.keybindingLabelShadow) { + content.push(`box-shadow: inset 0 -1px 0 ${this.styles.keybindingLabel.keybindingLabelShadow};`); + } + if (this.styles.keybindingLabel.keybindingLabelForeground) { + content.push(`color: ${this.styles.keybindingLabel.keybindingLabelForeground};`); + } + content.push('}'); + } + + const newStyles = content.join('\n'); + if (newStyles !== this.ui.styleSheet.textContent) { + this.ui.styleSheet.textContent = newStyles; + } + } + } + + private isDisplayed() { + return this.ui && this.ui.container.style.display !== 'none'; + } +} +export interface IQuickInputControllerHost extends ILayoutService { } + diff --git a/src/vs/platform/quickinput/browser/quickInputService.ts b/src/vs/platform/quickinput/browser/quickInputService.ts index e2f1fd70fdb..f797cd31f05 100644 --- a/src/vs/platform/quickinput/browser/quickInputService.ts +++ b/src/vs/platform/quickinput/browser/quickInputService.ts @@ -14,12 +14,12 @@ import { IWorkbenchListOptions, WorkbenchList } from 'vs/platform/list/browser/l import { IOpenerService } from 'vs/platform/opener/common/opener'; import { QuickAccessController } from 'vs/platform/quickinput/browser/quickAccess'; import { IQuickAccessController } from 'vs/platform/quickinput/common/quickAccess'; -import { IInputBox, IInputOptions, IKeyMods, IPickOptions, IQuickInputButton, IQuickInputService, IQuickNavigateConfiguration, IQuickPick, IQuickPickItem, QuickPickInput } from 'vs/platform/quickinput/common/quickInput'; +import { IInputBox, IInputOptions, IKeyMods, IPickOptions, IQuickInputButton, IQuickInputService, IQuickNavigateConfiguration, IQuickPick, IQuickPickItem, IQuickWidget, QuickPickInput } from 'vs/platform/quickinput/common/quickInput'; import { defaultButtonStyles, defaultCountBadgeStyles, defaultInputBoxStyles, defaultKeybindingLabelStyles, defaultProgressBarStyles, defaultToggleStyles, getListStyles } from 'vs/platform/theme/browser/defaultStyles'; import { activeContrastBorder, asCssVariable, pickerGroupBorder, pickerGroupForeground, quickInputBackground, quickInputForeground, quickInputListFocusBackground, quickInputListFocusForeground, quickInputListFocusIconForeground, quickInputTitleBackground, widgetBorder, widgetShadow } from 'vs/platform/theme/common/colorRegistry'; import { IThemeService, Themable } from 'vs/platform/theme/common/themeService'; -import { QuickInputController, IQuickInputControllerHost, IQuickInputOptions, IQuickInputStyles } from './quickInput'; - +import { IQuickInputOptions, IQuickInputStyles } from './quickInput'; +import { QuickInputController, IQuickInputControllerHost } from 'vs/platform/quickinput/browser/quickInputController'; export class QuickInputService extends Themable implements IQuickInputService { @@ -162,6 +162,10 @@ export class QuickInputService extends Themable implements IQuickInputService { return this.controller.createInputBox(); } + createQuickWidget(): IQuickWidget { + return this.controller.createQuickWidget(); + } + focus() { this.controller.focus(); } diff --git a/src/vs/platform/quickinput/common/quickInput.ts b/src/vs/platform/quickinput/common/quickInput.ts index 8106d170870..4bec55d912d 100644 --- a/src/vs/platform/quickinput/common/quickInput.ts +++ b/src/vs/platform/quickinput/common/quickInput.ts @@ -214,7 +214,8 @@ export interface IQuickInput extends IDisposable { description: string | undefined; /** - * Should be an HTMLElement (TODO: move this entire file into browser) + * Should be an HTMLElement. + * @deprecated Use an IQuickWidget instead */ widget: any | undefined; @@ -222,6 +223,10 @@ export interface IQuickInput extends IDisposable { totalSteps: number | undefined; + buttons: ReadonlyArray; + + readonly onDidTriggerButton: Event; + enabled: boolean; contextKey: string | undefined; @@ -233,6 +238,15 @@ export interface IQuickInput extends IDisposable { show(): void; hide(): void; + + didHide(reason?: QuickInputHideReason): void; +} + +export interface IQuickWidget extends IQuickInput { + /** + * Should be an HTMLElement (TODO: move this entire file into browser) + */ + widget: any | undefined; } export interface IQuickPickWillAcceptEvent { @@ -297,10 +311,6 @@ export interface IQuickPick extends IQuickInput { customHover: string | undefined; - buttons: ReadonlyArray; - - readonly onDidTriggerButton: Event; - readonly onDidTriggerItemButton: Event>; readonly onDidTriggerSeparatorButton: Event; @@ -413,16 +423,6 @@ export interface IInputBox extends IQuickInput { */ readonly onDidAccept: Event; - /** - * Buttons to show in addition to user input submission. - */ - buttons: ReadonlyArray; - - /** - * Event called when a button is selected. - */ - readonly onDidTriggerButton: Event; - /** * Text show below the input box. */ @@ -554,10 +554,15 @@ export interface IQuickInputService { createQuickPick(): IQuickPick; /** - * Provides raw access to the quick input controller. + * Provides raw access to the input box controller. */ createInputBox(): IInputBox; + /** + * Provides raw access to the quick widget controller. + */ + createQuickWidget(): IQuickWidget; + /** * Moves focus into quick input. */ diff --git a/src/vs/platform/quickinput/test/browser/quickinput.test.ts b/src/vs/platform/quickinput/test/browser/quickinput.test.ts index 6824b9b1d2c..b50d6523c68 100644 --- a/src/vs/platform/quickinput/test/browser/quickinput.test.ts +++ b/src/vs/platform/quickinput/test/browser/quickinput.test.ts @@ -13,7 +13,7 @@ import { raceTimeout } from 'vs/base/common/async'; import { unthemedCountStyles } from 'vs/base/browser/ui/countBadge/countBadge'; import { unthemedKeybindingLabelOptions } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel'; import { unthemedProgressBarOptions } from 'vs/base/browser/ui/progressbar/progressbar'; -import { QuickInputController } from 'vs/platform/quickinput/browser/quickInput'; +import { QuickInputController } from 'vs/platform/quickinput/browser/quickInputController'; import { IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { ColorScheme } from 'vs/platform/theme/common/theme'; diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatQuickInputActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatQuickInputActions.ts index bd249edb1cc..c644cc34fbe 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatQuickInputActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatQuickInputActions.ts @@ -16,7 +16,7 @@ import { ContextKeyExpr, IContextKeyService, IScopedContextKeyService } from 'vs import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { IQuickInputService, IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; +import { IQuickInputService, IQuickWidget } from 'vs/platform/quickinput/common/quickInput'; import { editorBackground, editorForeground, inputBackground } from 'vs/platform/theme/common/colorRegistry'; import { CHAT_CATEGORY } from 'vs/workbench/contrib/chat/browser/actions/chatActions'; import { IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat'; @@ -78,7 +78,7 @@ class QuickChatGlobalAction extends Action2 { export function getQuickChatActionForProvider(id: string, label: string) { return class AskQuickChatAction extends Action2 { _currentTimer: any | undefined; - _input: IQuickPick | undefined; + _input: IQuickWidget | undefined; _currentChat: QuickChat | undefined; constructor() { @@ -116,10 +116,8 @@ export function getQuickChatActionForProvider(id: string, label: string) { //#region Setup quick pick - this._input = quickInputService.createQuickPick(); + this._input = quickInputService.createQuickWidget(); disposableStore.add(this._input); - this._input.hideInput = true; - const containerSession = dom.$('.interactive-session'); this._input.widget = containerSession; diff --git a/src/vs/workbench/services/quickinput/browser/quickInputService.ts b/src/vs/workbench/services/quickinput/browser/quickInputService.ts index e47d590694e..14d68592f60 100644 --- a/src/vs/workbench/services/quickinput/browser/quickInputService.ts +++ b/src/vs/workbench/services/quickinput/browser/quickInputService.ts @@ -9,7 +9,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { QuickInputController } from 'vs/platform/quickinput/browser/quickInput'; +import { QuickInputController } from 'vs/platform/quickinput/browser/quickInputController'; import { QuickInputService as BaseQuickInputService } from 'vs/platform/quickinput/browser/quickInputService'; import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 706028e8ddf..0fffe75c46f 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -95,7 +95,7 @@ import { CodeEditorService } from 'vs/workbench/services/editor/browser/codeEdit import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IDiffEditor } from 'vs/editor/common/editorCommon'; -import { IInputBox, IInputOptions, IPickOptions, IQuickInputButton, IQuickInputService, IQuickNavigateConfiguration, IQuickPick, IQuickPickItem, QuickPickInput } from 'vs/platform/quickinput/common/quickInput'; +import { IInputBox, IInputOptions, IPickOptions, IQuickInputButton, IQuickInputService, IQuickNavigateConfiguration, IQuickPick, IQuickPickItem, IQuickWidget, QuickPickInput } from 'vs/platform/quickinput/common/quickInput'; import { QuickInputService } from 'vs/workbench/services/quickinput/browser/quickInputService'; import { IListService } from 'vs/platform/list/browser/listService'; import { win32, posix } from 'vs/base/common/path'; @@ -1936,6 +1936,7 @@ export class TestQuickInputService implements IQuickInputService { createQuickPick(): IQuickPick { throw new Error('not implemented.'); } createInputBox(): IInputBox { throw new Error('not implemented.'); } + createQuickWidget(): IQuickWidget { throw new Error('Method not implemented.'); } focus(): void { throw new Error('not implemented.'); } toggle(): void { throw new Error('not implemented.'); } navigate(next: boolean, quickNavigate?: IQuickNavigateConfiguration): void { throw new Error('not implemented.'); } From 634b57ca260fcad1e7e03c17a7a978da1cb333b3 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Thu, 17 Aug 2023 16:07:14 +0200 Subject: [PATCH 089/223] assignging to previous model when the update method returns null --- .../editor/contrib/stickyScroll/browser/stickyScrollProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts index 391ddd6c978..c9037dd3ea0 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts @@ -125,7 +125,7 @@ export class StickyLineCandidateProvider extends Disposable implements IStickyLi } }, 75) : undefined; - this._model = await this._stickyModelProvider.update(textModel, modelVersionId, token); + this._model = (await this._stickyModelProvider.update(textModel, modelVersionId, token)) ?? this._model; clearTimeout(resetHandle); } From af700713711b96445a0db59c7305034f4fddeb6d Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Thu, 17 Aug 2023 16:46:09 +0200 Subject: [PATCH 090/223] retrigger tests --- .../editor/contrib/stickyScroll/browser/stickyScrollProvider.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts index c9037dd3ea0..f73fca6f793 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts @@ -126,7 +126,6 @@ export class StickyLineCandidateProvider extends Disposable implements IStickyLi }, 75) : undefined; this._model = (await this._stickyModelProvider.update(textModel, modelVersionId, token)) ?? this._model; - clearTimeout(resetHandle); } From 0e2849672fa62421435d8e49e86a2776681cdb39 Mon Sep 17 00:00:00 2001 From: Ulugbek Abdullaev Date: Thu, 17 Aug 2023 17:05:26 +0200 Subject: [PATCH 091/223] refactor: add types to remove warning --- src/vs/workbench/api/common/extHost.api.impl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 1673b845c0c..b48277a3d55 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1340,7 +1340,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I checkProposedApiEnabled(extension, 'chatRequestAccess'); return extHostChatProvider.requestChatResponseProvider(extension.identifier, id); }, - registerVariable(name, description, resolver) { + registerVariable(name: string, description: string, resolver: vscode.ChatVariableResolver) { checkProposedApiEnabled(extension, 'chatVariables'); return extHostChatVariables.registerVariableResolver(extension, name, description, resolver); } From 3e9e7b3b5a31a6d07c5bd3b88184dce8bb82d896 Mon Sep 17 00:00:00 2001 From: Ulugbek Abdullaev Date: Thu, 17 Aug 2023 17:10:35 +0200 Subject: [PATCH 092/223] Initial implementation of `mappedEditsProvider` proposed API --- .../api/browser/extensionHost.contribution.ts | 1 + .../api/browser/mainThreadMappedEdits.ts | 57 ++++++++++++++++ .../workbench/api/common/extHost.api.impl.ts | 6 ++ .../workbench/api/common/extHost.protocol.ts | 21 ++++++ .../api/common/extHostMappedEdits.ts | 61 +++++++++++++++++ .../browser/actions/chatCodeblockActions.ts | 29 +++++++-- .../common/extensionsApiProposals.ts | 1 + .../mappedEdits/browser/mappedEditsService.ts | 51 +++++++++++++++ .../mappedEdits/common/mappedEdits.ts | 65 +++++++++++++++++++ src/vs/workbench/workbench.common.main.ts | 1 + .../vscode.proposed.mappedEditsProvider.d.ts | 48 ++++++++++++++ 11 files changed, 334 insertions(+), 7 deletions(-) create mode 100644 src/vs/workbench/api/browser/mainThreadMappedEdits.ts create mode 100644 src/vs/workbench/api/common/extHostMappedEdits.ts create mode 100644 src/vs/workbench/services/mappedEdits/browser/mappedEditsService.ts create mode 100644 src/vs/workbench/services/mappedEdits/common/mappedEdits.ts create mode 100644 src/vscode-dts/vscode.proposed.mappedEditsProvider.d.ts diff --git a/src/vs/workbench/api/browser/extensionHost.contribution.ts b/src/vs/workbench/api/browser/extensionHost.contribution.ts index 774cc0cc158..6d836e715e1 100644 --- a/src/vs/workbench/api/browser/extensionHost.contribution.ts +++ b/src/vs/workbench/api/browser/extensionHost.contribution.ts @@ -86,6 +86,7 @@ import './mainThreadTimeline'; import './mainThreadTesting'; import './mainThreadSecretState'; import './mainThreadShare'; +import './mainThreadMappedEdits'; import './mainThreadProfilContentHandlers'; import './mainThreadSemanticSimilarity'; import './mainThreadIssueReporter'; diff --git a/src/vs/workbench/api/browser/mainThreadMappedEdits.ts b/src/vs/workbench/api/browser/mainThreadMappedEdits.ts new file mode 100644 index 00000000000..a2a34f4155d --- /dev/null +++ b/src/vs/workbench/api/browser/mainThreadMappedEdits.ts @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; +import { reviveWorkspaceEditDto } from 'vs/workbench/api/browser/mainThreadBulkEdits'; +import { ExtHostContext, ExtHostMappedEditsShape, IDocumentFilterDto, IMappedEditsContextDto, MainContext, MainThreadMappedEditsShape } from 'vs/workbench/api/common/extHost.protocol'; +import { IMappedEditsProvider, IMappedEditsService } from 'vs/workbench/services/mappedEdits/common/mappedEdits'; +import { IExtHostContext, extHostNamedCustomer } from 'vs/workbench/services/extensions/common/extHostCustomers'; + +@extHostNamedCustomer(MainContext.MainThreadMappedEdits) +export class MainThreadMappedEdits implements MainThreadMappedEditsShape { + + private readonly proxy: ExtHostMappedEditsShape; + + private providers = new Map(); + + private providerDisposables = new Map(); + + constructor( + extHostContext: IExtHostContext, + @IMappedEditsService private readonly mappedEditsService: IMappedEditsService, + @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, + ) { + this.proxy = extHostContext.getProxy(ExtHostContext.ExtHostMappedEdits); + } + + $registerMappedEditsProvider(handle: number, selector: IDocumentFilterDto[]): void { + const provider: IMappedEditsProvider = { + selector, + provideMappedEdits: async (document, codeBlocks, context, token) => { + const result = await this.proxy.$provideMappedEdits(handle, document.uri, codeBlocks, context, token); + return result ? reviveWorkspaceEditDto(result, this.uriIdentityService) : null; + } + }; + this.providers.set(handle, provider); + const disposable = this.mappedEditsService.registerMappedEditsProvider(provider); + this.providerDisposables.set(handle, disposable); + } + + $unregisterMappedEditsProvider(handle: number): void { + if (this.providers.has(handle)) { + this.providers.delete(handle); + } + if (this.providerDisposables.has(handle)) { + this.providerDisposables.delete(handle); + } + } + + dispose(): void { + this.providers.clear(); + dispose(this.providerDisposables.values()); + this.providerDisposables.clear(); + } +} diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index b48277a3d55..055ed24f627 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -106,6 +106,7 @@ import { IExtHostManagedSockets } from 'vs/workbench/api/common/extHostManagedSo import { ExtHostShare } from 'vs/workbench/api/common/extHostShare'; import { ExtHostChatProvider } from 'vs/workbench/api/common/extHostChatProvider'; import { ExtHostChatSlashCommands } from 'vs/workbench/api/common/extHostChatSlashCommand'; +import { ExtHostMappedEdits } from 'vs/workbench/api/common/extHostMappedEdits'; import { ExtHostChatVariables } from 'vs/workbench/api/common/extHostChatVariables'; export interface IExtensionRegistries { @@ -210,6 +211,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostChatSlashCommands = rpcProtocol.set(ExtHostContext.ExtHostChatSlashCommands, new ExtHostChatSlashCommands(rpcProtocol, extHostChatProvider, extHostLogService)); const extHostChatVariables = rpcProtocol.set(ExtHostContext.ExtHostChatVariables, new ExtHostChatVariables(rpcProtocol)); const extHostChat = rpcProtocol.set(ExtHostContext.ExtHostChat, new ExtHostChat(rpcProtocol, extHostLogService)); + const extHostMappedEdits = rpcProtocol.set(ExtHostContext.ExtHostMappedEdits, new ExtHostMappedEdits(rpcProtocol, extHostDocuments, uriTransformer)); const extHostSemanticSimilarity = rpcProtocol.set(ExtHostContext.ExtHostSemanticSimilarity, new ExtHostSemanticSimilarity(rpcProtocol)); const extHostIssueReporter = rpcProtocol.set(ExtHostContext.ExtHostIssueReporter, new ExtHostIssueReporter(rpcProtocol)); const extHostStatusBar = rpcProtocol.set(ExtHostContext.ExtHostStatusBar, new ExtHostStatusBar(rpcProtocol, extHostCommands.converter)); @@ -1343,6 +1345,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I registerVariable(name: string, description: string, resolver: vscode.ChatVariableResolver) { checkProposedApiEnabled(extension, 'chatVariables'); return extHostChatVariables.registerVariableResolver(extension, name, description, resolver); + }, + registerMappedEditsProvider(selector: vscode.DocumentSelector, provider: vscode.MappedEditsProvider) { + checkProposedApiEnabled(extension, 'mappedEditsProvider'); + return extHostMappedEdits.registerMappedEditsProvider(selector, provider); } }; diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 6886d86e0a9..686e4ad8af6 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -371,6 +371,16 @@ export interface IShareableItemDto { selection?: IRange; } +export interface IRelatedContextItemDto { + readonly uri: UriComponents; + readonly range: IRange; +} + +export interface IMappedEditsContextDto { + selections: ISelection[]; //FIXME@ulugbekna: is this serializable? should I use ISelection? + related: IRelatedContextItemDto[]; +} + export interface ISignatureHelpProviderMetadataDto { readonly triggerCharacters: readonly string[]; readonly retriggerCharacters: readonly string[]; @@ -1315,6 +1325,11 @@ export interface MainThreadShareShape extends IDisposable { $unregisterShareProvider(handle: number): void; } +export interface MainThreadMappedEditsShape extends IDisposable { + $registerMappedEditsProvider(handle: number, selector: IDocumentFilterDto[]): void; + $unregisterMappedEditsProvider(handle: number): void; +} + export interface MainThreadTaskShape extends IDisposable { $createTaskId(task: tasks.ITaskDTO): Promise; $registerTaskProvider(handle: number, type: string): Promise; @@ -2098,6 +2113,10 @@ export interface ExtHostShareShape { $provideShare(handle: number, shareableItem: IShareableItemDto, token: CancellationToken): Promise; } +export interface ExtHostMappedEditsShape { + $provideMappedEdits(handle: number, document: UriComponents, codeBlocks: string[], context: IMappedEditsContextDto, token: CancellationToken): Promise; +} + export interface ExtHostTaskShape { $provideTasks(handle: number, validTypes: { [key: string]: boolean }): Promise; $resolveTask(handle: number, taskDTO: tasks.ITaskDTO): Promise; @@ -2623,6 +2642,7 @@ export const MainContext = { MainThreadSCM: createProxyIdentifier('MainThreadSCM'), MainThreadSearch: createProxyIdentifier('MainThreadSearch'), MainThreadShare: createProxyIdentifier('MainThreadShare'), + MainThreadMappedEdits: createProxyIdentifier('MainThreadMappedEdits'), MainThreadTask: createProxyIdentifier('MainThreadTask'), MainThreadWindow: createProxyIdentifier('MainThreadWindow'), MainThreadLabelService: createProxyIdentifier('MainThreadLabelService'), @@ -2700,6 +2720,7 @@ export const ExtHostContext = { ExtHostChatSlashCommands: createProxyIdentifier('ExtHostChatSlashCommands'), ExtHostChatVariables: createProxyIdentifier('ExtHostChatVariables'), ExtHostChatProvider: createProxyIdentifier('ExtHostChatProvider'), + ExtHostMappedEdits: createProxyIdentifier('ExtHostMappedEdits'), ExtHostSemanticSimilarity: createProxyIdentifier('ExtHostSemanticSimilarity'), ExtHostTheming: createProxyIdentifier('ExtHostTheming'), ExtHostTunnelService: createProxyIdentifier('ExtHostTunnelService'), diff --git a/src/vs/workbench/api/common/extHostMappedEdits.ts b/src/vs/workbench/api/common/extHostMappedEdits.ts new file mode 100644 index 00000000000..c98adda03b2 --- /dev/null +++ b/src/vs/workbench/api/common/extHostMappedEdits.ts @@ -0,0 +1,61 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CancellationToken } from 'vs/base/common/cancellation'; +import { URI, UriComponents } from 'vs/base/common/uri'; +import { IURITransformer } from 'vs/base/common/uriIpc'; +import { ExtHostMappedEditsShape, IMainContext, IMappedEditsContextDto, IWorkspaceEditDto, MainContext, MainThreadMappedEditsShape } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; +import { Range, Selection, DocumentSelector, WorkspaceEdit } from 'vs/workbench/api/common/extHostTypeConverters'; +import type * as vscode from 'vscode'; + +export class ExtHostMappedEdits implements ExtHostMappedEditsShape { + + private static handlePool: number = 0; + + private proxy: MainThreadMappedEditsShape; + private providers = new Map(); + + constructor( + mainContext: IMainContext, + private readonly _documents: ExtHostDocuments, + private readonly uriTransformer: IURITransformer | undefined + ) { + this.proxy = mainContext.getProxy(MainContext.MainThreadMappedEdits); + } + + async $provideMappedEdits(handle: number, docUri: UriComponents, codeBlocks: string[], context: IMappedEditsContextDto, token: CancellationToken): Promise { + const provider = this.providers.get(handle); + if (!provider) { + return null; + } + const uri = URI.revive(docUri); + const doc = this._documents.getDocument(uri); + const ctx = { + selections: context.selections.map(s => Selection.to(s)), + related: context.related.map(r => ({ uri: URI.revive(r.uri), range: Range.to(r.range) })), + }; + const mappedEdits = await provider.provideMappedEdits(doc, codeBlocks, ctx, token); + if (!mappedEdits) { + return null; + } + + return WorkspaceEdit.from(mappedEdits); + } + + registerMappedEditsProvider(selector: vscode.DocumentSelector, provider: vscode.MappedEditsProvider): vscode.Disposable { + const handle = ExtHostMappedEdits.handlePool++; + this.providers.set(handle, provider); + this.proxy.$registerMappedEditsProvider(handle, DocumentSelector.from(selector, this.uriTransformer)); + return { + dispose: () => { + ExtHostMappedEdits.handlePool--; + this.proxy.$unregisterMappedEditsProvider(handle); + this.providers.delete(handle); + } + }; + } + +} diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts index e9c9089baf6..85fd1ee7ad2 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Codicon } from 'vs/base/common/codicons'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { ICodeEditor, isCodeEditor, isDiffEditor } from 'vs/editor/browser/editorBrowser'; @@ -24,6 +25,7 @@ import { IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat'; import { CONTEXT_IN_CHAT_SESSION, CONTEXT_PROVIDER_EXISTS } from 'vs/workbench/contrib/chat/common/chatContextKeys'; import { IChatCopyAction, IChatService, IChatUserActionEvent, InteractiveSessionCopyKind } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatResponseViewModel, isResponseVM } from 'vs/workbench/contrib/chat/common/chatViewModel'; +import { IMappedEditsService, RelatedContextItem } from 'vs/workbench/services/mappedEdits/common/mappedEdits'; import { insertCell } from 'vs/workbench/contrib/notebook/browser/controller/cellOperations'; import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellKind, NOTEBOOK_EDITOR_ID } from 'vs/workbench/contrib/notebook/common/notebookCommon'; @@ -232,17 +234,30 @@ export function registerChatCodeBlockActions() { this.notifyUserAction(accessor, context); } - private async handleTextEditor(accessor: ServicesAccessor, codeEditor: ICodeEditor, activeModel: ITextModel, context: IChatCodeBlockActionContext) { - this.notifyUserAction(accessor, context); + private async handleTextEditor(accessor: ServicesAccessor, codeEditor: ICodeEditor, activeModel: ITextModel, chatCodeBlockActionContext: IChatCodeBlockActionContext) { + this.notifyUserAction(accessor, chatCodeBlockActionContext); const bulkEditService = accessor.get(IBulkEditService); const codeEditorService = accessor.get(ICodeEditorService); + const mappedEditsService = accessor.get(IMappedEditsService); - const activeSelection = codeEditor.getSelection() ?? new Range(activeModel.getLineCount(), 1, activeModel.getLineCount(), 1); - await bulkEditService.apply([new ResourceTextEdit(activeModel.uri, { - range: activeSelection, - text: context.code, - })]); + // try applying workspace edit that was returned by a MappedEditsProvider, else simply insert at selection + const selections = codeEditor.getSelections() ?? []; + const mappedEditsContext = { + selections, + related: [] as RelatedContextItem[], // FIXME@ulugbekna: this needs to be populated but we don't yet have a way to get this info from extensions + }; + const cancellationTokenSource = new CancellationTokenSource(); + const workspaceEdit = await mappedEditsService.provideMappedEdits(activeModel, [chatCodeBlockActionContext.code], mappedEditsContext, cancellationTokenSource.token); + if (workspaceEdit) { + await bulkEditService.apply(workspaceEdit); + } else { + const activeSelection = codeEditor.getSelection() ?? new Range(activeModel.getLineCount(), 1, activeModel.getLineCount(), 1); + await bulkEditService.apply([new ResourceTextEdit(activeModel.uri, { + range: activeSelection, + text: chatCodeBlockActionContext.code, + })]); + } codeEditorService.listCodeEditors().find(editor => editor.getModel()?.uri.toString() === activeModel.uri.toString())?.focus(); } diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index f3dc351a3a5..dd1794371f0 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -58,6 +58,7 @@ export const allApiProposals = Object.freeze({ interactiveWindow: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.interactiveWindow.d.ts', ipc: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.ipc.d.ts', languageConfigurationAutoClosingPairs: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.languageConfigurationAutoClosingPairs.d.ts', + mappedEditsProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.mappedEditsProvider.d.ts', notebookCellExecutionState: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookCellExecutionState.d.ts', notebookCodeActions: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookCodeActions.d.ts', notebookControllerAffinityHidden: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookControllerAffinityHidden.d.ts', diff --git a/src/vs/workbench/services/mappedEdits/browser/mappedEditsService.ts b/src/vs/workbench/services/mappedEdits/browser/mappedEditsService.ts new file mode 100644 index 00000000000..fe1cec5815a --- /dev/null +++ b/src/vs/workbench/services/mappedEdits/browser/mappedEditsService.ts @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CancellationToken } from 'vs/base/common/cancellation'; +import { ITextModel } from 'vs/editor/common/model'; +import { IMappedEditsProvider, IMappedEditsService, MappedEditsContext } from 'vs/workbench/services/mappedEdits/common/mappedEdits'; +import { score } from 'vs/editor/common/languageSelector'; +import { WorkspaceEdit } from 'vs/editor/common/languages'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; + +export class MappedEditsService implements IMappedEditsService { + readonly _serviceBrand: undefined; + + private readonly _providers = new Set(); + + constructor() { } + + registerMappedEditsProvider(provider: IMappedEditsProvider) { + this._providers.add(provider); + return { + dispose: () => { + this._providers.delete(provider); + } + }; + } + + async provideMappedEdits(document: ITextModel, codeBlocks: string[], context: MappedEditsContext, token: CancellationToken): Promise { + + const language = document.getLanguageId(); + + const providers = [...this._providers.values()] + .map((p): [IMappedEditsProvider, number] => { + const pts = score(p.selector, document.uri, language, true, undefined, undefined); + return [p, pts]; + }) + .filter(([p, pts]) => pts > 0) + .sort((a, b) => b[1] - a[1]); + + if (providers.length === 0) { + return null; + } + + const provider = providers[0][0]; + + return provider.provideMappedEdits(document, codeBlocks, context, token); + } +} + +registerSingleton(IMappedEditsService, MappedEditsService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/mappedEdits/common/mappedEdits.ts b/src/vs/workbench/services/mappedEdits/common/mappedEdits.ts new file mode 100644 index 00000000000..8e58c5c1031 --- /dev/null +++ b/src/vs/workbench/services/mappedEdits/common/mappedEdits.ts @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { URI } from 'vs/base/common/uri'; +import { ITextModel } from 'vs/editor/common/model'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { LanguageSelector } from 'vs/editor/common/languageSelector'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { WorkspaceEdit } from 'vs/editor/common/languages'; +import { Selection } from 'vs/editor/common/core/selection'; +import { Range } from 'vs/editor/common/core/range'; + +export interface RelatedContextItem { + readonly uri: URI; + readonly range: Range; +} + +export interface MappedEditsContext { + selections: Selection[]; + + /** + * If there's no context, the array should be empty. It's also empty until we figure out how to compute this or retrieve from an extension (eg, copilot chat) + * + * TODO@ulugbekna: should this array be sorted from highest priority to lowest? + */ + related: RelatedContextItem[]; +} + +export interface IMappedEditsProvider { + + selector: LanguageSelector; + + /** + * Provide mapped edits for a given document. + * + * @param document The document to provide mapped edits for. + * @param codeBlocks Code blocks that come from an LLM's reply. + * "Insert at cursor" in the panel chat only sends one edit that the user clicks on, but inline chat can send multiple blocks and let the lang server decide what to do with them. + * @param context The context for providing mapped edits. + * @param token A cancellation token. + * @returns A provider result of text edits. + */ + provideMappedEdits( + document: ITextModel, + codeBlocks: string[], + context: MappedEditsContext, + token: CancellationToken + ): Promise; +} + + +export const IMappedEditsService = createDecorator('mappedEditsService'); + +export interface IMappedEditsService { + _serviceBrand: undefined; + registerMappedEditsProvider(provider: IMappedEditsProvider): IDisposable; + provideMappedEdits( + document: ITextModel, + codeBlocks: string[], + context: MappedEditsContext, + token: CancellationToken): Promise; +} diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index a52b2b4c274..81170dfd99e 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -111,6 +111,7 @@ import 'vs/workbench/services/textMate/browser/textMateTokenizationFeature.contr import 'vs/workbench/services/userActivity/common/userActivityService'; import 'vs/workbench/services/userActivity/browser/userActivityBrowser'; import 'vs/workbench/services/issue/browser/issueTroubleshoot'; +import 'vs/workbench/services/mappedEdits/browser/mappedEditsService'; import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; diff --git a/src/vscode-dts/vscode.proposed.mappedEditsProvider.d.ts b/src/vscode-dts/vscode.proposed.mappedEditsProvider.d.ts new file mode 100644 index 00000000000..9d05e467fd9 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.mappedEditsProvider.d.ts @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + export interface RelatedContextItem { + readonly uri: Uri; + readonly range: Range; + } + + export interface MappedEditsContext { + selections: Selection[]; + + /** + * If there's no context, the array should be empty. It's also empty until we figure out how to compute this or retrieve from an extension (eg, copilot chat) + * + * TODO: it was suggested initially to be sorted from highest priority to lowest. How would it look like? + */ + related: RelatedContextItem[]; + } + + /** + * Interface for providing mapped edits for a given document. + */ + export interface MappedEditsProvider { + /** + * Provide mapped edits for a given document. + * @param document The document to provide mapped edits for. + * @param codeBlocks Code blocks that come from an LLM's reply. + * "Insert at cursor" in the panel chat only sends one edit that the user clicks on, but inline chat can send multiple blocks and let the lang server decide what to do with them. + * @param context The context for providing mapped edits. + * @param token A cancellation token. + * @returns A provider result of text edits. + */ + provideMappedEdits( + document: TextDocument, + codeBlocks: string[], + context: MappedEditsContext, + token: CancellationToken + ): ProviderResult; + } + + namespace chat { + export function registerMappedEditsProvider(documentSelector: DocumentSelector, provider: MappedEditsProvider): Disposable; + } +} From b4cf1fa6029f3b05413a7c28b89971288a7b9f08 Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Thu, 17 Aug 2023 09:49:01 -0700 Subject: [PATCH 093/223] Add settings similarity search (#190453) --- .../preferences/browser/preferencesSearch.ts | 152 ++++++++++++++---- .../preferences/common/preferences.ts | 5 +- .../preferences/common/preferencesModels.ts | 4 + 3 files changed, 128 insertions(+), 33 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts b/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts index 24d0b989f8f..5c8e9443b7b 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ISettingsEditorModel, ISetting, ISettingsGroup, ISearchResult, IGroupFilter, SettingMatchType } from 'vs/workbench/services/preferences/common/preferences'; +import { ISettingsEditorModel, ISetting, ISettingsGroup, ISearchResult, IGroupFilter, SettingMatchType, ISettingMatch } from 'vs/workbench/services/preferences/common/preferences'; import { IRange } from 'vs/editor/common/core/range'; import { distinct } from 'vs/base/common/arrays'; import * as strings from 'vs/base/common/strings'; @@ -19,8 +19,8 @@ import { IWorkbenchExtensionEnablementService } from 'vs/workbench/services/exte import { CancellationToken } from 'vs/base/common/cancellation'; import { ExtensionType } from 'vs/platform/extensions/common/extensions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IProductService } from 'vs/platform/product/common/productService'; import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { ISemanticSimilarityService } from 'vs/workbench/services/semanticSimilarity/common/semanticSimilarityService'; export interface IEndpointDetails { urlBase?: string; @@ -32,11 +32,11 @@ export class PreferencesSearchService extends Disposable implements IPreferences // @ts-expect-error disable remote search for now, ref https://github.com/microsoft/vscode/issues/172411 private _installedExtensions: Promise; + private _remoteSearchProvider: RemoteSearchProvider | undefined; constructor( @IInstantiationService private readonly instantiationService: IInstantiationService, @IConfigurationService private readonly configurationService: IConfigurationService, - @IProductService private readonly productService: IProductService, @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService ) { @@ -52,34 +52,22 @@ export class PreferencesSearchService extends Disposable implements IPreferences }); } - // @ts-expect-error disable remote search for now, ref https://github.com/microsoft/vscode/issues/172411 private get remoteSearchAllowed(): boolean { const workbenchSettings = this.configurationService.getValue().workbench.settings; - if (!workbenchSettings.enableNaturalLanguageSearch) { - return false; - } - - return !!this._endpoint.urlBase; + return workbenchSettings.enableNaturalLanguageSearch; } - private get _endpoint(): IEndpointDetails { - const workbenchSettings = this.configurationService.getValue().workbench.settings; - if (workbenchSettings.naturalLanguageSearchEndpoint) { - return { - urlBase: workbenchSettings.naturalLanguageSearchEndpoint, - key: workbenchSettings.naturalLanguageSearchKey - }; - } else { - return { - urlBase: this.productService.settingsSearchUrl - }; + getRemoteSearchProvider(filter: string, newExtensionsOnly = false): RemoteSearchProvider | undefined { + if (!this.remoteSearchAllowed) { + return undefined; } - } - getRemoteSearchProvider(filter: string, newExtensionsOnly = false): ISearchProvider | undefined { - // Disable for now because Bing search isn't supported anymore. - // Ref https://github.com/microsoft/vscode/issues/172411 - return undefined; + if (!this._remoteSearchProvider) { + this._remoteSearchProvider = this.instantiationService.createInstance(RemoteSearchProvider); + } + + this._remoteSearchProvider.setFilter(filter); + return this._remoteSearchProvider; } getLocalSearchProvider(filter: string): LocalSearchProvider { @@ -87,6 +75,15 @@ export class PreferencesSearchService extends Disposable implements IPreferences } } +function cleanFilter(filter: string): string { + // Remove " and : which are likely to be copypasted as part of a setting name. + // Leave other special characters which the user might want to search for. + return filter + .replace(/[":]/g, ' ') + .replace(/ /g, ' ') + .trim(); +} + export class LocalSearchProvider implements ISearchProvider { static readonly EXACT_MATCH_SCORE = 10000; static readonly START_SCORE = 1000; @@ -95,12 +92,7 @@ export class LocalSearchProvider implements ISearchProvider { private _filter: string, @IConfigurationService private readonly configurationService: IConfigurationService ) { - // Remove " and : which are likely to be copypasted as part of a setting name. - // Leave other special characters which the user might want to search for. - this._filter = this._filter - .replace(/[":]/g, ' ') - .replace(/ /g, ' ') - .trim(); + this._filter = cleanFilter(this._filter); } searchModel(preferencesModel: ISettingsEditorModel, token?: CancellationToken): Promise { @@ -305,4 +297,102 @@ export class SettingMatches { } } +class RemoteSearchKeysProvider { + private settingKeys: string[] = []; + private settingsRecord: Record = {}; + private currentPreferencesModel: ISettingsEditorModel | undefined; + + constructor(private readonly semanticSimilarityService: ISemanticSimilarityService) { + } + + updateModel(preferencesModel: ISettingsEditorModel) { + if (preferencesModel === this.currentPreferencesModel) { + return; + } + + this.currentPreferencesModel = preferencesModel; + this.refresh(); + } + + private refresh() { + this.settingKeys = []; + this.settingsRecord = {}; + + if (!this.semanticSimilarityService.isEnabled() || !this.currentPreferencesModel) { + return; + } + + for (const group of this.currentPreferencesModel.settingsGroups) { + if (group.id === 'mostCommonlyUsed') { + continue; + } + for (const section of group.sections) { + for (const setting of section.settings) { + this.settingKeys.push(setting.key); + this.settingsRecord[setting.key] = setting; + } + } + } + } + + getSettingKeys(): string[] { + return this.settingKeys; + } + + getSettingsRecord(): Record { + return this.settingsRecord; + } +} + +export class RemoteSearchProvider implements ISearchProvider { + private static readonly SEMANTIC_SIMILARITY_THRESHOLD = 0.75; + private static readonly SEMANTIC_SIMILARITY_MAX_PICKS = 10; + + private readonly _keysProvider: RemoteSearchKeysProvider; + private _filter: string = ''; + + constructor( + @ISemanticSimilarityService private readonly semanticSimilarityService: ISemanticSimilarityService, + ) { + this._keysProvider = new RemoteSearchKeysProvider(semanticSimilarityService); + } + + setFilter(filter: string) { + this._filter = cleanFilter(filter); + } + + async searchModel(preferencesModel: ISettingsEditorModel, token?: CancellationToken | undefined): Promise { + if (!this.semanticSimilarityService.isEnabled() || !this._filter) { + return null; + } + + this._keysProvider.updateModel(preferencesModel); + const settingKeys = this._keysProvider.getSettingKeys(); + const settingsRecord = this._keysProvider.getSettingsRecord(); + + const scores = await this.semanticSimilarityService.getSimilarityScore(this._filter, settingKeys, token ?? CancellationToken.None); + const filterMatches: ISettingMatch[] = []; + const sortedIndices = scores.map((_, i) => i).sort((a, b) => scores[b] - scores[a]); + let numOfSmartPicks = 0; + for (const i of sortedIndices) { + const score = scores[i]; + if (score < RemoteSearchProvider.SEMANTIC_SIMILARITY_THRESHOLD || numOfSmartPicks === RemoteSearchProvider.SEMANTIC_SIMILARITY_MAX_PICKS) { + break; + } + + const pick = settingKeys[i]; + filterMatches.push({ + setting: settingsRecord[pick], + matches: [settingsRecord[pick].range], + matchType: SettingMatchType.RemoteMatch, + score + }); + numOfSmartPicks++; + } + return { + filterMatches + }; + } +} + registerSingleton(IPreferencesSearchService, PreferencesSearchService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/preferences/common/preferences.ts b/src/vs/workbench/services/preferences/common/preferences.ts index eb3cd0cb738..c50429a9294 100644 --- a/src/vs/workbench/services/preferences/common/preferences.ts +++ b/src/vs/workbench/services/preferences/common/preferences.ts @@ -138,8 +138,9 @@ export interface IFilterResult { */ export enum SettingMatchType { None = 0, - WholeWordMatch = 1 << 0, - KeyMatch = 1 << 1 + RemoteMatch = 1 << 0, + WholeWordMatch = 1 << 1, + KeyMatch = 1 << 2 } export interface ISettingMatch { diff --git a/src/vs/workbench/services/preferences/common/preferencesModels.ts b/src/vs/workbench/services/preferences/common/preferencesModels.ts index 0ccee385532..fa940fa4782 100644 --- a/src/vs/workbench/services/preferences/common/preferencesModels.ts +++ b/src/vs/workbench/services/preferences/common/preferencesModels.ts @@ -95,6 +95,10 @@ abstract class AbstractSettingsModel extends EditorModel { // Sort by match type if the match types are not the same. // The priority of the match type is given by the SettingMatchType enum. return b.matchType - a.matchType; + } else if (a.matchType === SettingMatchType.RemoteMatch) { + // The match types are the same and are RemoteMatch. + // Sort by score. + return b.score - a.score; } else { // The match types are the same. if (a.setting.extensionInfo && b.setting.extensionInfo From c4bb9ad2771fd57efa44e7f5106cea89f6851fb5 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 17 Aug 2023 09:53:46 -0700 Subject: [PATCH 094/223] fix #188764 --- .../contrib/accessibility/browser/accessibilityContributions.ts | 2 +- src/vs/workbench/contrib/terminal/browser/terminalService.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts index 2b1699deae5..47866e0b75f 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts @@ -201,7 +201,7 @@ export class NotificationAccessibleViewContribution extends Disposable { notification.onDidClose(() => accessibleViewService.next()); accessibleViewService.show({ provideContent: () => { - return localize('notification.accessibleView', '{0} Source: {1}', message, notification.source); + return notification.source ? localize('notification.accessibleViewSrc', '{0} Source: {1}', message, notification.source) : localize('notification.accessibleView', '{0}', message); }, onClose(): void { focusList(); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 23b4cbdfd3d..d96f26608e6 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -61,7 +61,6 @@ export class TerminalService extends Disposable implements ITerminalService { private _detachedXterms = new Set(); private _terminalEditorActive: IContextKey; private readonly _terminalShellTypeContextKey: IContextKey; - private _isShuttingDown: boolean = false; private _backgroundedTerminalInstances: ITerminalInstance[] = []; private _backgroundedTerminalDisposables: Map = new Map(); From 945039cb0c35e2f316a2b81cdebfa76b02315672 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Thu, 17 Aug 2023 09:54:53 -0700 Subject: [PATCH 095/223] Update src/vs/workbench/contrib/terminal/browser/terminalService.ts --- src/vs/workbench/contrib/terminal/browser/terminalService.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index d96f26608e6..23b4cbdfd3d 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -61,6 +61,7 @@ export class TerminalService extends Disposable implements ITerminalService { private _detachedXterms = new Set(); private _terminalEditorActive: IContextKey; private readonly _terminalShellTypeContextKey: IContextKey; + private _isShuttingDown: boolean = false; private _backgroundedTerminalInstances: ITerminalInstance[] = []; private _backgroundedTerminalDisposables: Map = new Map(); From 63c6a82c02bc44fc716b8ec1a9f8c4498cc62279 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 17 Aug 2023 10:33:11 -0700 Subject: [PATCH 096/223] disable / enable actions in the accessible view depending on context --- .../browser/accessibilityConfiguration.ts | 3 + .../accessibility/browser/accessibleView.ts | 56 +++++++++++++++---- .../browser/accessibleViewActions.ts | 13 ++--- 3 files changed, 54 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts index 74316d108c3..91f562646d6 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts @@ -10,6 +10,9 @@ import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; export const accessibilityHelpIsShown = new RawContextKey('accessibilityHelpIsShown', false, true); export const accessibleViewIsShown = new RawContextKey('accessibleViewIsShown', false, true); +export const accessibleViewSupportsNavigation = new RawContextKey('accessibleViewSupportsNavigation', false, true); +export const accessibleViewVerbosityEnabled = new RawContextKey('accessibleViewVerbosityEnabled', false, true); +export const accessibleViewGoToSymbolSupported = new RawContextKey('accessibleViewGoToSymbolSupported', false, true); export const enum AccessibilitySettingId { UnfocusedViewOpacity = 'accessibility.unfocusedViewOpacity' diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index cd134d921de..d9e69754f09 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -32,7 +32,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IPickerQuickAccessItem } from 'vs/platform/quickinput/browser/pickerQuickAccess'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { AccessibilityCommandId } from 'vs/workbench/contrib/accessibility/common/accessibilityCommands'; -import { AccessibilityVerbositySettingId, accessibilityHelpIsShown, accessibleViewIsShown } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; +import { AccessibilityVerbositySettingId, accessibilityHelpIsShown, accessibleViewGoToSymbolSupported, accessibleViewIsShown, accessibleViewSupportsNavigation, accessibleViewVerbosityEnabled } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; import { getSimpleEditorOptions } from 'vs/workbench/contrib/codeEditor/browser/simpleEditorOptions'; import { IAction } from 'vs/base/common/actions'; import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; @@ -96,8 +96,13 @@ export interface IAccessibleViewOptions { class AccessibleView extends Disposable { private _editorWidget: CodeEditorWidget; + private _accessiblityHelpIsShown: IContextKey; private _accessibleViewIsShown: IContextKey; + private _accessibleViewSupportsNavigation: IContextKey; + private _accessibleViewVerbosityEnabled: IContextKey; + private _accessibleViewGoToSymbolSupported: IContextKey; + get editorWidget() { return this._editorWidget; } private _editorContainer: HTMLElement; private _currentProvider: IAccessibleContentProvider | undefined; @@ -116,8 +121,13 @@ class AccessibleView extends Disposable { @IMenuService private readonly _menuService: IMenuService ) { super(); + this._accessiblityHelpIsShown = accessibilityHelpIsShown.bindTo(this._contextKeyService); this._accessibleViewIsShown = accessibleViewIsShown.bindTo(this._contextKeyService); + this._accessibleViewSupportsNavigation = accessibleViewSupportsNavigation.bindTo(this._contextKeyService); + this._accessibleViewVerbosityEnabled = accessibleViewVerbosityEnabled.bindTo(this._contextKeyService); + this._accessibleViewGoToSymbolSupported = accessibleViewGoToSymbolSupported.bindTo(this._contextKeyService); + this._editorContainer = document.createElement('div'); this._editorContainer.classList.add('accessible-view'); const codeEditorWidgetOptions: ICodeEditorWidgetOptions = { @@ -149,8 +159,12 @@ class AccessibleView extends Disposable { } })); this._register(this._configurationService.onDidChangeConfiguration(e => { - if (this._currentProvider && this._accessiblityHelpIsShown.get() && e.affectsConfiguration(this._currentProvider.verbositySettingKey)) { - this.show(this._currentProvider); + if (this._currentProvider && e.affectsConfiguration(this._currentProvider.verbositySettingKey)) { + if (this._accessiblityHelpIsShown.get()) { + this.show(this._currentProvider); + } + this._accessibleViewVerbosityEnabled.set(this._configurationService.getValue(this._currentProvider.verbositySettingKey)); + this._updateToolbar(this._currentProvider.actions, this._currentProvider.options.type); } })); } @@ -264,6 +278,14 @@ class AccessibleView extends Disposable { this._accessibleViewIsShown.set(shown); this._accessiblityHelpIsShown.set(!shown); } + if (provider.next && provider.previous) { + this._accessibleViewSupportsNavigation.set(true); + } else { + this._accessibleViewSupportsNavigation.reset(); + } + const verbosityEnabled: boolean = this._configurationService.getValue(provider.verbositySettingKey); + this._accessibleViewVerbosityEnabled.set(verbosityEnabled); + this._accessibleViewGoToSymbolSupported.set(this._goToSymbolsSupported() ? this.getSymbols()?.length! > 0 : false); } private _render(provider: IAccessibleContentProvider, container: HTMLElement, showAccessibleViewHelp?: boolean): IDisposable { @@ -311,15 +333,21 @@ class AccessibleView extends Disposable { let ariaLabel = ''; let actionsHint = ''; const verbose = this._configurationService.getValue(provider.verbositySettingKey); - if (verbose && provider.options.type === AccessibleViewType.View && !showAccessibleViewHelp) { + if (verbose && !showAccessibleViewHelp && (this._accessibleViewSupportsNavigation.get() || this._accessibleViewVerbosityEnabled.get() || this._accessibleViewGoToSymbolSupported.get())) { actionsHint = localize('ariaAccessibleViewActions', "Use Shift+Tab to explore actions such as disabling this hint."); } - if (actionsHint) { - ariaLabel = provider.options.ariaLabel ? localize('actionsAriaKb', "{0}, {1}", provider.options.ariaLabel, actionsHint) : localize('accessible-view', "Accessible View, {0}", actionsHint); - } else { - ariaLabel = provider.options.ariaLabel ? provider.options.ariaLabel : localize('helpAriaNoKb', "Accessible View"); + if (actionsHint && provider.options.type === AccessibleViewType.View) { + ariaLabel = provider.options.ariaLabel ? localize('actionsAriaKb', "{0}, {1}", provider.options.ariaLabel, actionsHint) : localize('accessible-view-hint', "Accessible View, {0}", actionsHint); + } else if (actionsHint) { + ariaLabel = provider.options.ariaLabel ? localize('actionsAriaKb', "{0}, {1}", provider.options.ariaLabel, actionsHint) : localize('accessibility-help-hint', "Accessibility Help, {0}", actionsHint); + } + if (!ariaLabel) { + if (provider.options.type === AccessibleViewType.View) { + ariaLabel = provider.options.ariaLabel ? provider.options.ariaLabel : localize('accessible-view', "Accessible View"); + } else { + ariaLabel = provider.options.ariaLabel ? provider.options.ariaLabel : localize('accessible-help', "Accessibility Help"); + } } - this._editorWidget.updateOptions({ ariaLabel }); this._editorWidget.focus(); }); @@ -385,6 +413,13 @@ class AccessibleView extends Disposable { return this._modelService.createModel(resource.fragment, null, resource, false); } + private _goToSymbolsSupported(): boolean { + if (!this._currentProvider) { + return false; + } + return this._currentProvider.options.language === 'markdown' || this._currentProvider.options.language === undefined || !!this._currentProvider.getSymbols; + } + public showAccessibleViewHelp(): void { if (!this._currentProvider) { return; @@ -392,9 +427,8 @@ class AccessibleView extends Disposable { } const currentProvider = Object.assign({}, this._currentProvider); currentProvider.options = Object.assign({}, currentProvider.options); - const currentProviderHasSymbols = this._currentProvider.options.language === 'markdown' || this._currentProvider.options.language === undefined || !!this._currentProvider.getSymbols; const accessibleViewHelpProvider: IAccessibleContentProvider = { - provideContent: () => this._getAccessibleViewHelpDialogContent(currentProviderHasSymbols), + provideContent: () => this._getAccessibleViewHelpDialogContent(this._goToSymbolsSupported()), onClose: () => this.show(currentProvider), options: { ariaLabel: localize('accessibleViewHelp', "Accessible View Help"), diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts index da283f254cc..b9a3d3f141e 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts @@ -11,7 +11,7 @@ import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/act import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { AccessibilityCommandId } from 'vs/workbench/contrib/accessibility/common/accessibilityCommands'; -import { accessibilityHelpIsShown, accessibleViewIsShown } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; +import { accessibilityHelpIsShown, accessibleViewGoToSymbolSupported, accessibleViewIsShown, accessibleViewSupportsNavigation, accessibleViewVerbosityEnabled } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; import { IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; const accessibleViewMenu = { @@ -28,7 +28,7 @@ class AccessibleViewNextAction extends Action2 { constructor() { super({ id: AccessibilityCommandId.ShowNext, - precondition: accessibleViewIsShown, + precondition: ContextKeyExpr.and(accessibleViewIsShown, accessibleViewSupportsNavigation), keybinding: { primary: KeyMod.Alt | KeyCode.BracketRight, weight: KeybindingWeight.WorkbenchContrib @@ -49,7 +49,7 @@ class AccessibleViewPreviousAction extends Action2 { constructor() { super({ id: AccessibilityCommandId.ShowPrevious, - precondition: accessibleViewIsShown, + precondition: ContextKeyExpr.and(accessibleViewIsShown, accessibleViewSupportsNavigation), keybinding: { primary: KeyMod.Alt | KeyCode.BracketLeft, weight: KeybindingWeight.WorkbenchContrib @@ -70,7 +70,7 @@ class AccessibleViewGoToSymbolAction extends Action2 { constructor() { super({ id: AccessibilityCommandId.GoToSymbol, - precondition: accessibleViewIsShown, + precondition: ContextKeyExpr.and(accessibleViewIsShown, accessibleViewGoToSymbolSupported), keybinding: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyO, weight: KeybindingWeight.WorkbenchContrib + 10 @@ -134,8 +134,8 @@ class AccessibleViewDisableHintAction extends Action2 { constructor() { super({ id: AccessibilityCommandId.DisableVerbosityHint, + precondition: ContextKeyExpr.and(ContextKeyExpr.or(accessibleViewIsShown, accessibilityHelpIsShown), accessibleViewVerbosityEnabled), keybinding: { - when: ContextKeyExpr.or(accessibleViewIsShown, accessibilityHelpIsShown), primary: KeyMod.Alt | KeyCode.F6, weight: KeybindingWeight.WorkbenchContrib }, @@ -143,8 +143,7 @@ class AccessibleViewDisableHintAction extends Action2 { menu: [commandPalette, { id: MenuId.AccessibleView, - group: 'navigation', - when: ContextKeyExpr.or(accessibleViewIsShown, accessibilityHelpIsShown) + group: 'navigation' }], title: localize('editor.action.accessibleViewDisableHint', "Disable Accessible View Hint") }); From 49c886c08a4e80c9a1d36d5d24cc96cb57414e4f Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 17 Aug 2023 11:12:54 -0700 Subject: [PATCH 097/223] use simple aria label --- .../browser/accessibilityContributions.ts | 15 ++++--------- .../accessibility/browser/accessibleView.ts | 22 +++++-------------- 2 files changed, 10 insertions(+), 27 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts index 2b1699deae5..ca86c8fabcf 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts @@ -62,7 +62,7 @@ class AccessibilityHelpProvider implements IAccessibleContentProvider { onClose() { this._editor.focus(); } - options: IAccessibleViewOptions = { type: AccessibleViewType.Help, ariaLabel: localize('editor-help', "editor accessibility help"), readMoreUrl: 'https://go.microsoft.com/fwlink/?linkid=851010' }; + options: IAccessibleViewOptions = { type: AccessibleViewType.Help, readMoreUrl: 'https://go.microsoft.com/fwlink/?linkid=851010' }; verbositySettingKey = AccessibilityVerbositySettingId.Editor; constructor( private readonly _editor: ICodeEditor, @@ -107,9 +107,7 @@ class AccessibilityHelpProvider implements IAccessibleContentProvider { export class HoverAccessibleViewContribution extends Disposable { static ID: 'hoverAccessibleViewContribution'; - private _options: IAccessibleViewOptions = { - ariaLabel: localize('hoverAccessibleView', "Hover Accessible View"), language: 'typescript', type: AccessibleViewType.View - }; + private _options: IAccessibleViewOptions = { language: 'typescript', type: AccessibleViewType.View }; constructor() { super(); this._register(AccessibleViewAction.addImplementation(95, 'hover', accessor => { @@ -225,10 +223,7 @@ export class NotificationAccessibleViewContribution extends Disposable { renderAccessibleView(); }, verbositySettingKey: AccessibilityVerbositySettingId.Notification, - options: { - ariaLabel: localize('notification', "Notification Accessible View"), - type: AccessibleViewType.View - }, + options: { type: AccessibleViewType.View }, actions: getActionsFromNotification(notification) }); return true; @@ -281,9 +276,7 @@ export function alertFocusChange(index: number | undefined, length: number | und export class InlineCompletionsAccessibleViewContribution extends Disposable { static ID: 'inlineCompletionsAccessibleViewContribution'; - private _options: IAccessibleViewOptions = { - ariaLabel: localize('inlineCompletionsAccessibleView', "Inline Completions Accessible View"), type: AccessibleViewType.View - }; + private _options: IAccessibleViewOptions = { type: AccessibleViewType.View }; constructor() { super(); this._register(AccessibleViewAction.addImplementation(95, 'inline-completions', accessor => { diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index d9e69754f09..71fcfb7a5a3 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -85,7 +85,6 @@ export const enum AccessibleViewType { } export interface IAccessibleViewOptions { - ariaLabel: string; readMoreUrl?: string; /** * Defaults to markdown @@ -330,23 +329,17 @@ class AccessibleView extends Disposable { } model.setLanguage(provider.options.language ?? 'markdown'); container.appendChild(this._editorContainer); - let ariaLabel = ''; let actionsHint = ''; const verbose = this._configurationService.getValue(provider.verbositySettingKey); - if (verbose && !showAccessibleViewHelp && (this._accessibleViewSupportsNavigation.get() || this._accessibleViewVerbosityEnabled.get() || this._accessibleViewGoToSymbolSupported.get())) { + const hasActions = this._accessibleViewSupportsNavigation.get() || this._accessibleViewVerbosityEnabled.get() || this._accessibleViewGoToSymbolSupported.get() || this._currentProvider?.actions; + if (verbose && !showAccessibleViewHelp && hasActions) { actionsHint = localize('ariaAccessibleViewActions', "Use Shift+Tab to explore actions such as disabling this hint."); } + let ariaLabel = provider.options.type === AccessibleViewType.Help ? localize('accessibility-help', "Accessibility Help") : localize('accessible-view', "Accessible View"); if (actionsHint && provider.options.type === AccessibleViewType.View) { - ariaLabel = provider.options.ariaLabel ? localize('actionsAriaKb', "{0}, {1}", provider.options.ariaLabel, actionsHint) : localize('accessible-view-hint', "Accessible View, {0}", actionsHint); + ariaLabel = localize('accessible-view-hint', "Accessible View, {0}", actionsHint); } else if (actionsHint) { - ariaLabel = provider.options.ariaLabel ? localize('actionsAriaKb', "{0}, {1}", provider.options.ariaLabel, actionsHint) : localize('accessibility-help-hint', "Accessibility Help, {0}", actionsHint); - } - if (!ariaLabel) { - if (provider.options.type === AccessibleViewType.View) { - ariaLabel = provider.options.ariaLabel ? provider.options.ariaLabel : localize('accessible-view', "Accessible View"); - } else { - ariaLabel = provider.options.ariaLabel ? provider.options.ariaLabel : localize('accessible-help', "Accessibility Help"); - } + ariaLabel = localize('accessibility-help-hint', "Accessibility Help, {0}", actionsHint); } this._editorWidget.updateOptions({ ariaLabel }); this._editorWidget.focus(); @@ -430,10 +423,7 @@ class AccessibleView extends Disposable { const accessibleViewHelpProvider: IAccessibleContentProvider = { provideContent: () => this._getAccessibleViewHelpDialogContent(this._goToSymbolsSupported()), onClose: () => this.show(currentProvider), - options: { - ariaLabel: localize('accessibleViewHelp', "Accessible View Help"), - type: AccessibleViewType.Help, - }, + options: { type: AccessibleViewType.Help }, verbositySettingKey: this._currentProvider.verbositySettingKey }; this._contextViewService.hideContextView(); From 551f3d04b808f1e04aad8b2ac6ea7681fa244cdd Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 17 Aug 2023 11:14:03 -0700 Subject: [PATCH 098/223] more instances --- .../contrib/chat/browser/actions/chatAccessibilityHelp.ts | 2 +- src/vs/workbench/contrib/chat/browser/chat.contribution.ts | 2 +- .../contrib/codeEditor/browser/diffEditorHelper.ts | 2 +- .../contrib/inlineChat/browser/inlineChat.contribution.ts | 3 +-- .../contrib/notebook/browser/notebookAccessibility.ts | 7 ++----- .../accessibility/browser/terminalAccessibilityHelp.ts | 1 - 6 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts b/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts index bc2bc2e9430..f8c40b935f7 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts @@ -84,6 +84,6 @@ export async function runAccessibilityHelpAction(accessor: ServicesAccessor, edi InlineChatController.get(editor)?.focus(); } }, - options: { type: AccessibleViewType.Help, ariaLabel: type === 'panelChat' ? localize('chat-help-label', "Chat accessibility help") : localize('inline-chat-label', "Inline chat accessibility help") } + options: { type: AccessibleViewType.Help } }); } diff --git a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts index 4b967d38580..01068dfc64f 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts @@ -212,7 +212,7 @@ class ChatAccessibleViewContribution extends Disposable { alertFocusChange(responseIndex, length, 'previous'); renderAccessibleView(accessibleViewService, widgetService, codeEditorService); }, - options: { ariaLabel: nls.localize('chatAccessibleView', "Chat Accessible View"), type: AccessibleViewType.View } + options: { type: AccessibleViewType.View } }); return true; } diff --git a/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts b/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts index 44ba3928008..1e0d06ff9b8 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts @@ -111,7 +111,7 @@ function createScreenReaderHelp(): IDisposable { onClose: () => { codeEditor.focus(); }, - options: { type: AccessibleViewType.Help, ariaLabel: localize('chat-help-label', "Diff editor accessibility help") } + options: { type: AccessibleViewType.Help } }); }, ContextKeyExpr.and( ContextKeyEqualsExpr.create('diffEditorVersion', 2), diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts index 8b6c004f074..96f58a5b6f8 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts @@ -18,7 +18,6 @@ import { AccessibilityVerbositySettingId } from 'vs/workbench/contrib/accessibil import { AccessibleViewType, IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; import { Disposable } from 'vs/base/common/lifecycle'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { localize } from 'vs/nls'; import { Extensions, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { AccessibleViewAction } from 'vs/workbench/contrib/accessibility/browser/accessibleViewActions'; @@ -87,7 +86,7 @@ class InlineChatAccessibleViewContribution extends Disposable { controller.focus(); }, - options: { ariaLabel: localize('inlineChatAccessibleView', "Inline Chat Accessible View"), type: AccessibleViewType.View } + options: { type: AccessibleViewType.View } }); return true; }, ContextKeyExpr.or(CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_RESPONSE_FOCUSED))); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookAccessibility.ts b/src/vs/workbench/contrib/notebook/browser/notebookAccessibility.ts index 55716426739..790e9e8bdc3 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookAccessibility.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookAccessibility.ts @@ -54,7 +54,7 @@ export async function runAccessibilityHelpAction(accessor: ServicesAccessor, edi onClose: () => { editor.focus(); }, - options: { type: AccessibleViewType.Help, ariaLabel: 'Notebook accessibility help' } + options: { type: AccessibleViewType.Help } }); } @@ -114,10 +114,7 @@ export function showAccessibleOutput(accessibleViewService: IAccessibleViewServi notebookEditor?.setFocus(selections[0]); activePane?.focus(); }, - options: { - ariaLabel: localize('NotebookCellOutputAccessibleView', "Notebook Cell Output Accessible View"), - type: AccessibleViewType.View - } + options: { type: AccessibleViewType.View } }); return true; } diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts index 58bd11c6150..d3a0e3a592c 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts @@ -38,7 +38,6 @@ export class TerminalAccessibleContentProvider extends Disposable implements IAc } options: IAccessibleViewOptions = { type: AccessibleViewType.Help, - ariaLabel: localize('terminal-help-label', "terminal accessibility help"), readMoreUrl: 'https://code.visualstudio.com/docs/editor/accessibility#_terminal-accessibility' }; verbositySettingKey = AccessibilityVerbositySettingId.Terminal; From eb68de2519ee0317f4438e2a79790561350e1ffd Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 17 Aug 2023 11:21:19 -0700 Subject: [PATCH 099/223] ensure screen reader focuses the correct elt --- .../accessibility/browser/accessibilityContributions.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts index 47866e0b75f..755bacf567e 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts @@ -289,6 +289,7 @@ export class InlineCompletionsAccessibleViewContribution extends Disposable { this._register(AccessibleViewAction.addImplementation(95, 'inline-completions', accessor => { const accessibleViewService = accessor.get(IAccessibleViewService); const codeEditorService = accessor.get(ICodeEditorService); + const contextViewService = accessor.get(IContextViewService); const show = () => { const editor = codeEditorService.getActiveCodeEditor() || codeEditorService.getFocusedCodeEditor(); if (!editor) { @@ -317,10 +318,12 @@ export class InlineCompletionsAccessibleViewContribution extends Disposable { editor.focus(); }, next() { - model.next().then(() => show()); + contextViewService.hideContextView(); + setTimeout(() => model.next().then(() => show()), 50); }, previous() { - model.previous().then(() => show()); + contextViewService.hideContextView(); + setTimeout(() => model.previous().then(() => show()), 50); }, actions: [ { From 03434ca5ac72c055732f4d9aa7fd85dc2612d186 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Thu, 17 Aug 2023 11:27:11 -0700 Subject: [PATCH 100/223] dont switch to a fallback language for a text model langauge change event --- .../cellStatusBar/statusBarProviders.ts | 34 ++++++++++++++----- .../contrib/notebook/browser/notebookIcons.ts | 1 + .../common/model/notebookCellTextModel.ts | 23 ++++++++----- 3 files changed, 41 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/statusBarProviders.ts b/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/statusBarProviders.ts index ee2b1fdfa22..436dabcefb9 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/statusBarProviders.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/statusBarProviders.ts @@ -15,6 +15,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { CHANGE_CELL_LANGUAGE, DETECT_CELL_LANGUAGE } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { warningStateIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService'; import { CellKind, CellStatusbarAlignment, INotebookCellStatusBarItem, INotebookCellStatusBarItemList, INotebookCellStatusBarItemProvider } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; @@ -38,19 +39,36 @@ class CellStatusBarLanguagePickerProvider implements INotebookCellStatusBarItemP return; } - const languageId = cell.cellKind === CellKind.Markup ? - 'markdown' : - (this._languageService.getLanguageIdByLanguageName(cell.language) || cell.language); - const text = this._languageService.getLanguageName(languageId) || languageId; - const item = { - text, + const statusBarItems: INotebookCellStatusBarItem[] = []; + let displayLanguage = cell.language; + if (cell.cellKind === CellKind.Markup) { + displayLanguage = 'markdown'; + } else { + const registeredId = this._languageService.getLanguageIdByLanguageName(cell.language); + if (registeredId) { + displayLanguage = this._languageService.getLanguageName(displayLanguage) ?? displayLanguage; + } else { + // add unregistered lanugage warning item + const searchTooltip = localize('notebook.cell.status.searchLanguageExtensions', "Unknown cell language '{0}' - Search for notebook extensions", cell.language); + statusBarItems.push({ + text: `$(${warningStateIcon.id})`, + command: { id: 'workbench.extensions.search', arguments: [`@tag:${cell.language}`] }, + tooltip: searchTooltip, + alignment: CellStatusbarAlignment.Right, + priority: -Number.MAX_SAFE_INTEGER + 1 + }); + } + } + + statusBarItems.push({ + text: displayLanguage, command: CHANGE_CELL_LANGUAGE, tooltip: localize('notebook.cell.status.language', "Select Cell Language Mode"), alignment: CellStatusbarAlignment.Right, priority: -Number.MAX_SAFE_INTEGER - }; + }); return { - items: [item] + items: statusBarItems }; } } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookIcons.ts b/src/vs/workbench/contrib/notebook/browser/notebookIcons.ts index 69c932935eb..ad8ee51a054 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookIcons.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookIcons.ts @@ -22,6 +22,7 @@ export const clearIcon = registerIcon('notebook-clear', Codicon.clearAll, locali export const splitCellIcon = registerIcon('notebook-split-cell', Codicon.splitVertical, localize('splitCellIcon', 'Icon to split a cell in notebook editors.')); export const successStateIcon = registerIcon('notebook-state-success', Codicon.check, localize('successStateIcon', 'Icon to indicate a success state in notebook editors.')); export const errorStateIcon = registerIcon('notebook-state-error', Codicon.error, localize('errorStateIcon', 'Icon to indicate an error state in notebook editors.')); +export const warningStateIcon = registerIcon('notebook-state-warning', Codicon.warning, localize('warningStateIcon', 'Icon to an item that may need attention.')); export const pendingStateIcon = registerIcon('notebook-state-pending', Codicon.clock, localize('pendingStateIcon', 'Icon to indicate a pending state in notebook editors.')); export const executingStateIcon = registerIcon('notebook-state-executing', Codicon.sync, localize('executingStateIcon', 'Icon to indicate an executing state in notebook editors.')); diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts index e32021676e5..73d9e68b70c 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts @@ -166,17 +166,10 @@ export class NotebookCellTextModel extends Disposable implements ICell { this._textModelDisposables.clear(); this._textModel = m; if (this._textModel) { - // Init language from text model - // The language defined in the cell might not be supported in the editor so the text model might be using the default fallback - // If so let's not modify the language - if (!(this._languageService.isRegisteredLanguageId(this.language) === false && (this._textModel.getLanguageId() === PLAINTEXT_LANGUAGE_ID || this._textModel.getLanguageId() === 'jupyter'))) { - this.language = this._textModel.getLanguageId(); - } + this.setRegisteredLanguage(this._languageService, this._textModel.getLanguageId(), this.language); // Listen to language changes on the model - this._textModelDisposables.add(this._textModel.onDidChangeLanguage(e => { - this.language = e.newLanguage; - })); + this._textModelDisposables.add(this._textModel.onDidChangeLanguage((e) => this.setRegisteredLanguage(this._languageService, e.newLanguage, this.language))); this._textModelDisposables.add(this._textModel.onWillDispose(() => this.textModel = undefined)); this._textModelDisposables.add(this._textModel.onDidChangeContent(() => { if (this._textModel) { @@ -191,6 +184,18 @@ export class NotebookCellTextModel extends Disposable implements ICell { } } + private setRegisteredLanguage(languageService: ILanguageService, newLanguage: string, currentLanguage: string) { + // The language defined in the cell might not be supported in the editor so the text model might be using the default fallback + // If so let's not modify the language + const isFallBackLanguage = (newLanguage === PLAINTEXT_LANGUAGE_ID || newLanguage === 'jupyter'); + if (!languageService.isRegisteredLanguageId(currentLanguage) && isFallBackLanguage) { + // notify to display warning, but don't change the language + this._onDidChangeLanguage.fire(currentLanguage); + } else { + this.language = newLanguage; + } + } + constructor( readonly uri: URI, public readonly handle: number, From bf5f9266bce34d83a161357557f24a170d5f5a2e Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 17 Aug 2023 11:35:59 -0700 Subject: [PATCH 101/223] rm line text fr announcement --- .../inlineCompletions/browser/inlineCompletionsController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts index 3d7909533c4..51b2d4fc562 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts @@ -192,7 +192,7 @@ export class InlineCompletionsController extends Disposable { const lineText = model.textModel.getLineContent(state.ghostText.lineNumber); this.audioCueService.playAudioCue(AudioCue.inlineSuggestion).then(() => { if (this.editor.getOption(EditorOption.screenReaderAnnounceInlineSuggestion)) { - this.provideScreenReaderUpdate(lineText + state.ghostText.renderForScreenReader(lineText)); + this.provideScreenReaderUpdate(state.ghostText.renderForScreenReader(lineText)); } }); } From 29094819b76d8585edbeb4c7d3d8c96815445a2c Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Thu, 17 Aug 2023 11:48:39 -0700 Subject: [PATCH 102/223] clean up --- .../contrib/cellStatusBar/statusBarProviders.ts | 11 +++++------ .../contrib/notebook/browser/notebookIcons.ts | 1 - 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/statusBarProviders.ts b/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/statusBarProviders.ts index 436dabcefb9..2eaaa655a19 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/statusBarProviders.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/statusBarProviders.ts @@ -15,7 +15,6 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { CHANGE_CELL_LANGUAGE, DETECT_CELL_LANGUAGE } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { warningStateIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService'; import { CellKind, CellStatusbarAlignment, INotebookCellStatusBarItem, INotebookCellStatusBarItemList, INotebookCellStatusBarItemProvider } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; @@ -49,10 +48,10 @@ class CellStatusBarLanguagePickerProvider implements INotebookCellStatusBarItemP displayLanguage = this._languageService.getLanguageName(displayLanguage) ?? displayLanguage; } else { // add unregistered lanugage warning item - const searchTooltip = localize('notebook.cell.status.searchLanguageExtensions', "Unknown cell language '{0}' - Search for notebook extensions", cell.language); - statusBarItems.push({ - text: `$(${warningStateIcon.id})`, - command: { id: 'workbench.extensions.search', arguments: [`@tag:${cell.language}`] }, + const searchTooltip = localize('notebook.cell.status.searchLanguageExtensions', "Unknown cell language - Search for '{0}' extensions", cell.language); + statusBarItems.push({ + text: `$(dialog-warning)`, + command: { id: 'workbench.extensions.search', arguments: [`@tag:${cell.language}`], title: 'Search Extensions' }, tooltip: searchTooltip, alignment: CellStatusbarAlignment.Right, priority: -Number.MAX_SAFE_INTEGER + 1 @@ -60,7 +59,7 @@ class CellStatusBarLanguagePickerProvider implements INotebookCellStatusBarItemP } } - statusBarItems.push({ + statusBarItems.push({ text: displayLanguage, command: CHANGE_CELL_LANGUAGE, tooltip: localize('notebook.cell.status.language', "Select Cell Language Mode"), diff --git a/src/vs/workbench/contrib/notebook/browser/notebookIcons.ts b/src/vs/workbench/contrib/notebook/browser/notebookIcons.ts index ad8ee51a054..69c932935eb 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookIcons.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookIcons.ts @@ -22,7 +22,6 @@ export const clearIcon = registerIcon('notebook-clear', Codicon.clearAll, locali export const splitCellIcon = registerIcon('notebook-split-cell', Codicon.splitVertical, localize('splitCellIcon', 'Icon to split a cell in notebook editors.')); export const successStateIcon = registerIcon('notebook-state-success', Codicon.check, localize('successStateIcon', 'Icon to indicate a success state in notebook editors.')); export const errorStateIcon = registerIcon('notebook-state-error', Codicon.error, localize('errorStateIcon', 'Icon to indicate an error state in notebook editors.')); -export const warningStateIcon = registerIcon('notebook-state-warning', Codicon.warning, localize('warningStateIcon', 'Icon to an item that may need attention.')); export const pendingStateIcon = registerIcon('notebook-state-pending', Codicon.clock, localize('pendingStateIcon', 'Icon to indicate a pending state in notebook editors.')); export const executingStateIcon = registerIcon('notebook-state-executing', Codicon.sync, localize('executingStateIcon', 'Icon to indicate an executing state in notebook editors.')); From a49237900cd0e9affb049a6906cff35bcab62330 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Thu, 17 Aug 2023 12:47:25 -0700 Subject: [PATCH 103/223] improve tooltip --- .../browser/contrib/cellStatusBar/statusBarProviders.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/statusBarProviders.ts b/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/statusBarProviders.ts index 2eaaa655a19..6f3874d0acd 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/statusBarProviders.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/statusBarProviders.ts @@ -48,7 +48,7 @@ class CellStatusBarLanguagePickerProvider implements INotebookCellStatusBarItemP displayLanguage = this._languageService.getLanguageName(displayLanguage) ?? displayLanguage; } else { // add unregistered lanugage warning item - const searchTooltip = localize('notebook.cell.status.searchLanguageExtensions', "Unknown cell language - Search for '{0}' extensions", cell.language); + const searchTooltip = localize('notebook.cell.status.searchLanguageExtensions', "Unknown cell language. Click to search for '{0}' extensions", cell.language); statusBarItems.push({ text: `$(dialog-warning)`, command: { id: 'workbench.extensions.search', arguments: [`@tag:${cell.language}`], title: 'Search Extensions' }, From ce4442da23f55b75d52d54f396324aeec30aa854 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 17 Aug 2023 13:45:42 -0700 Subject: [PATCH 104/223] don't use hack, alert is sufficient --- .../accessibility/browser/accessibilityContributions.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts index e6a0025f4f2..ccfc48a979b 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts @@ -282,7 +282,6 @@ export class InlineCompletionsAccessibleViewContribution extends Disposable { this._register(AccessibleViewAction.addImplementation(95, 'inline-completions', accessor => { const accessibleViewService = accessor.get(IAccessibleViewService); const codeEditorService = accessor.get(ICodeEditorService); - const contextViewService = accessor.get(IContextViewService); const show = () => { const editor = codeEditorService.getActiveCodeEditor() || codeEditorService.getFocusedCodeEditor(); if (!editor) { @@ -311,12 +310,10 @@ export class InlineCompletionsAccessibleViewContribution extends Disposable { editor.focus(); }, next() { - contextViewService.hideContextView(); - setTimeout(() => model.next().then(() => show()), 50); + model.next().then(() => show()); }, previous() { - contextViewService.hideContextView(); - setTimeout(() => model.previous().then(() => show()), 50); + model.previous().then(() => show()); }, actions: [ { From bda93fcb858e340bb19708dd2b2183d28a490a12 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Thu, 17 Aug 2023 14:29:27 -0700 Subject: [PATCH 105/223] skip scroll to reveal when copying output and select the correct output --- .../notebook/browser/controller/cellOutputActions.ts | 2 +- .../workbench/contrib/notebook/browser/notebookBrowser.ts | 1 + .../contrib/notebook/browser/notebookEditorWidget.ts | 6 +++--- .../notebook/browser/view/renderers/backLayerWebView.ts | 4 ++-- .../notebook/browser/view/renderers/webviewMessages.ts | 2 +- .../notebook/browser/view/renderers/webviewPreloads.ts | 6 +++--- 6 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts index 25d0ffbc1ba..d71503c9cb6 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts @@ -39,7 +39,7 @@ registerAction2(class CopyCellOutputAction extends NotebookAction { if (mimeType?.startsWith('image/')) { const editorService = accessor.get(IEditorService); const editor = editorService.activeEditorPane?.getControl() as INotebookEditor; - await editor.focusNotebookCell(outputViewModel.cellViewModel as ICellViewModel, 'output'); + await editor.focusNotebookCell(outputViewModel.cellViewModel as ICellViewModel, 'output', { skipReveal: true, outputId: outputViewModel.model.outputId }); editor.copyOutputImage(outputViewModel); } else { const clipboardService = accessor.get(IClipboardService); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index d7516848531..587b8619587 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -151,6 +151,7 @@ export interface IFocusNotebookCellOptions { readonly skipReveal?: boolean; readonly focusEditorLine?: number; readonly minimalScrolling?: boolean; + readonly outputId?: string; } //#endregion diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index d424587ada8..f4f76b539db 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -2341,6 +2341,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD } else if (focusItem === 'output') { this.focusElement(cell); + if (!this.hasEditorFocus()) { this._list.focusView(); } @@ -2349,9 +2350,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD return; } - if (!options?.skipReveal) { - this._webview.focusOutput(cell.id, this._webviewFocused); - } + const focusElementId = options?.outputId ?? cell.id; + this._webview.focusOutput(focusElementId, this._webviewFocused); cell.updateEditState(CellEditState.Preview, 'focusNotebookCell'); cell.focusMode = CellFocusMode.Output; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index 5b3bb79f8e0..1b67d83daf6 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -1606,7 +1606,7 @@ export class BackLayerWebView extends Themable { this.webview?.focus(); } - focusOutput(cellId: string, viewFocused: boolean) { + focusOutput(cellOrOutputId: string, viewFocused: boolean) { if (this._disposed) { return; } @@ -1617,7 +1617,7 @@ export class BackLayerWebView extends Themable { this._sendMessageToWebview({ type: 'focus-output', - cellId, + cellOrOutputId: cellOrOutputId, }); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts index 653a16a35bd..e74e173ae33 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts @@ -273,7 +273,7 @@ export interface ICopyImageMessage { export interface IFocusOutputMessage { readonly type: 'focus-output'; - readonly cellId: string; + readonly cellOrOutputId: string; } export interface IAckOutputHeight { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts index 28e91de05c4..ca2ad71955c 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -474,8 +474,8 @@ async function webviewPreloads(ctx: PreloadContext) { }); }; - function focusFirstFocusableOrContainerInOutput(cellId: string) { - const cellOutputContainer = document.getElementById(cellId); + function focusFirstFocusableOrContainerInOutput(cellOrOutputId: string) { + const cellOutputContainer = document.getElementById(cellOrOutputId); if (cellOutputContainer) { if (cellOutputContainer.contains(document.activeElement)) { return; @@ -1523,7 +1523,7 @@ async function webviewPreloads(ctx: PreloadContext) { break; } case 'focus-output': - focusFirstFocusableOrContainerInOutput(event.data.cellId); + focusFirstFocusableOrContainerInOutput(event.data.cellOrOutputId); break; case 'decorations': { let outputContainer = document.getElementById(event.data.cellId); From cf1b44383b23ae6bb0aeab74a6bb2381124b4edd Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Thu, 17 Aug 2023 14:39:13 -0700 Subject: [PATCH 106/223] clean up --- .../workbench/contrib/notebook/browser/notebookEditorWidget.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index f4f76b539db..ba8ae7da1db 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -2341,7 +2341,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD } else if (focusItem === 'output') { this.focusElement(cell); - if (!this.hasEditorFocus()) { this._list.focusView(); } From 3e753b10dd0d45eba51fc8955482bda4344503f3 Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Thu, 17 Aug 2023 15:32:37 -0700 Subject: [PATCH 107/223] Show more results (#190707) --- .../contrib/preferences/browser/preferencesSearch.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts b/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts index 5c8e9443b7b..486c29e09be 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts @@ -345,8 +345,8 @@ class RemoteSearchKeysProvider { } export class RemoteSearchProvider implements ISearchProvider { - private static readonly SEMANTIC_SIMILARITY_THRESHOLD = 0.75; - private static readonly SEMANTIC_SIMILARITY_MAX_PICKS = 10; + private static readonly SEMANTIC_SIMILARITY_THRESHOLD = 0.73; + private static readonly SEMANTIC_SIMILARITY_MAX_PICKS = 15; private readonly _keysProvider: RemoteSearchKeysProvider; private _filter: string = ''; From d9839c35eeb453bbc908dba9940dc49da25afae6 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Thu, 17 Aug 2023 15:55:23 -0700 Subject: [PATCH 108/223] Add a side-menu in chat (#190709) * Add a side-menu in chat Adds a new menu to the right of the input box but outside of it, not inside like execute. This PR doesn't add anything that takes advantage of this, but that'll be in the next PR. * don't even create the toolbar if we're not in compact --- src/vs/platform/actions/common/actions.ts | 1 + .../contrib/chat/browser/chatInputPart.ts | 16 +++++++++++-- .../contrib/chat/browser/media/chat.css | 24 +++++++++---------- 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index d1f216f0971..0b6f1148464 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -187,6 +187,7 @@ export class MenuId { static readonly ChatCodeBlock = new MenuId('ChatCodeblock'); static readonly ChatMessageTitle = new MenuId('ChatMessageTitle'); static readonly ChatExecute = new MenuId('ChatExecute'); + static readonly ChatInputSide = new MenuId('ChatInputSide'); static readonly AccessibleView = new MenuId('AccessibleView'); /** diff --git a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts index 40a6f13a08f..6dce4e1d7d1 100644 --- a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts @@ -180,7 +180,8 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge this.container = dom.append(container, $('.interactive-input-part')); this.followupsContainer = dom.append(this.container, $('.interactive-input-followups')); - const inputContainer = dom.append(this.container, $('.interactive-input-and-toolbar')); + const inputAndSideToolbar = dom.append(this.container, $('.interactive-input-and-side-toolbar')); + const inputContainer = dom.append(inputAndSideToolbar, $('.interactive-input-and-execute-toolbar')); const inputScopedContextKeyService = this._register(this.contextKeyService.createScoped(inputContainer)); CONTEXT_IN_CHAT_INPUT.bindTo(inputScopedContextKeyService).set(true); @@ -240,6 +241,16 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge toolbar.getElement().classList.add('interactive-execute-toolbar'); toolbar.context = { widget }; + if (this.options.renderStyle === 'compact') { + const toolbarSide = this._register(this.instantiationService.createInstance(MenuWorkbenchToolBar, inputAndSideToolbar, MenuId.ChatInputSide, { + menuOptions: { + shouldForwardArgs: true + } + })); + toolbarSide.getElement().classList.add('chat-side-toolbar'); + toolbarSide.context = { widget }; + } + this.inputModel = this.modelService.getModel(this.inputUri) || this.modelService.createModel('', null, this.inputUri, true); this.inputModel.updateOptions({ bracketColorizationOptions: { enabled: false, independentColorPoolPerBracketType: false } }); this._inputEditor.setModel(this.inputModel); @@ -280,9 +291,10 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge const editorBorder = 2; const editorPadding = 8; const executeToolbarWidth = 25; + const sideToolbarWidth = this.options.renderStyle === 'compact' ? 20 : 0; const initialEditorScrollWidth = this._inputEditor.getScrollWidth(); - this._inputEditor.layout({ width: width - inputPartHorizontalPadding - editorBorder - editorPadding - executeToolbarWidth, height: inputEditorHeight }); + this._inputEditor.layout({ width: width - inputPartHorizontalPadding - editorBorder - editorPadding - executeToolbarWidth - sideToolbarWidth, height: inputEditorHeight }); if (allowRecurse && initialEditorScrollWidth < 10) { // This is probably the initial layout. Now that the editor is layed out with its correct width, it should report the correct contentHeight diff --git a/src/vs/workbench/contrib/chat/browser/media/chat.css b/src/vs/workbench/contrib/chat/browser/media/chat.css index a67ffb6896b..7e193499146 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chat.css +++ b/src/vs/workbench/contrib/chat/browser/media/chat.css @@ -221,7 +221,7 @@ margin: 0 0 8px 0; } -.interactive-session .interactive-input-and-toolbar { +.interactive-session .interactive-input-and-execute-toolbar { display: flex; box-sizing: border-box; cursor: text; @@ -235,16 +235,22 @@ justify-content: space-between; } -.interactive-session .interactive-input-and-toolbar.focused { +.interactive-session .interactive-input-and-side-toolbar { + display: flex; + gap: 6px; + align-items: center; +} + +.interactive-session .interactive-input-and-execute-toolbar.focused { border-color: var(--vscode-focusBorder); } -.interactive-session .interactive-input-and-toolbar .monaco-editor, -.interactive-session .interactive-input-and-toolbar .monaco-editor .monaco-editor-background { +.interactive-session .interactive-input-and-execute-toolbar .monaco-editor, +.interactive-session .interactive-input-and-execute-toolbar .monaco-editor .monaco-editor-background { background-color: var(--vscode-input-background) !important; } -.interactive-session .interactive-input-and-toolbar .monaco-editor .cursors-layer { +.interactive-session .interactive-input-and-execute-toolbar .monaco-editor .cursors-layer { padding-left: 4px; } @@ -411,20 +417,14 @@ .quick-input-widget .interactive-session .interactive-input-part { padding: 8px 6px 6px 6px; - border-top: 0; } .quick-input-widget .interactive-session .interactive-input-part .interactive-execute-toolbar { bottom: 1px; } -.quick-input-widget .interactive-session .interactive-input-and-toolbar { +.quick-input-widget .interactive-session .interactive-input-and-execute-toolbar { margin: 0; - border-radius: 2px; -} - -.quick-input-widget .interactive-session .interactive-input-and-toolbar .chat-slash-command-content-widget { - top: 2px !important; } /* #endregion */ From 501baeb5e07b5a404f862d922bacb93771d91331 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Thu, 17 Aug 2023 17:05:44 -0700 Subject: [PATCH 109/223] Fix copying responses (#190712) Fix microsoft/vscode-copilot-release#364 --- .../workbench/contrib/chat/browser/actions/chatCopyActions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatCopyActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatCopyActions.ts index 41624ff1316..a2e7aa52b62 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatCopyActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatCopyActions.ts @@ -76,5 +76,5 @@ export function registerChatCopyActions() { function stringifyItem(item: IChatRequestViewModel | IChatResponseViewModel): string { return isRequestVM(item) ? - `${item.username}: ${item.messageText}` : `${item.username}: ${item.response.value}`; + `${item.username}: ${item.messageText}` : `${item.username}: ${item.response.asString()}`; } From a1e8856e4b3c1a54c4902e3ddfbb3ba7265911ad Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 18 Aug 2023 12:09:49 +0200 Subject: [PATCH 110/223] Place sticky scroll into View > Apperance (#190677) placing sticky scroll into View > Apperance --- .../editor/contrib/stickyScroll/browser/stickyScrollActions.ts | 2 +- .../contrib/codeEditor/browser/toggleRenderControlCharacter.ts | 2 +- .../contrib/codeEditor/browser/toggleRenderWhitespace.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollActions.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollActions.ts index 3d5b4942690..ece8daa4e51 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollActions.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollActions.ts @@ -33,7 +33,7 @@ export class ToggleStickyScroll extends Action2 { }, menu: [ { id: MenuId.CommandPalette }, - { id: MenuId.MenubarViewMenu, group: '5_editor', order: 2 }, + { id: MenuId.MenubarAppearanceMenu, group: '4_editor', order: 3 }, { id: MenuId.StickyScrollContext } ] }); diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleRenderControlCharacter.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleRenderControlCharacter.ts index b34d5058935..d4be0883444 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleRenderControlCharacter.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleRenderControlCharacter.ts @@ -28,7 +28,7 @@ export class ToggleRenderControlCharacterAction extends Action2 { menu: { id: MenuId.MenubarAppearanceMenu, group: '4_editor', - order: 4 + order: 5 } }); } diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleRenderWhitespace.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleRenderWhitespace.ts index af948e693be..e5255c219b5 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleRenderWhitespace.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleRenderWhitespace.ts @@ -28,7 +28,7 @@ class ToggleRenderWhitespaceAction extends Action2 { menu: { id: MenuId.MenubarAppearanceMenu, group: '4_editor', - order: 3 + order: 4 } }); } From f137f258d865276ffb34994ad733faaa9fc4dcde Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 18 Aug 2023 05:14:14 -0700 Subject: [PATCH 111/223] Fix typo plural -> singular --- .../contrib/accessibility/browser/accessibilityConfiguration.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts index 91f562646d6..0a7043a5cd8 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts @@ -84,7 +84,7 @@ const configuration: IConfigurationNode = { ...baseProperty }, [AccessibilitySettingId.UnfocusedViewOpacity]: { - description: localize('unfocusedViewOpacity', 'The opacity fraction (0.2 to 1.0) to use for unfocused editors and terminals. This will dim inactive views to make the focused views more obvious.'), + description: localize('unfocusedViewOpacity', 'The opacity fraction (0.2 to 1.0) to use for unfocused editors and terminals. This will dim inactive views to make the focused view more obvious.'), type: 'number', minimum: 0.2, maximum: 1, From 6336ee75304eaac32897d8e74c451863d877cb28 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 18 Aug 2023 08:04:19 -0700 Subject: [PATCH 112/223] eng: cleanup some leaks around around editors and workbenchInstantiationService (#190623) * inline chat: fix leaks found in testing Fixes some leaks I found while looking at https://github.com/microsoft/vscode/pull/190444, by adding ensureNoDisposablesAreLeakedInTestSuite to the suite. I don't have a dev setup for inline chat, so I have not tested this beyond running tests and verifying the fix * eng: cleanup some leaks around around editors and workbenchInstantiationService I added a disposable leak tracker to a test that used `workbenchInstantiationService`. This fixes the baseline leaks and some extra leaks with that function. * fix build * rm too-eager leak checker * remove forgotten busy loop --- src/vs/base/browser/ui/grid/gridview.ts | 1 + src/vs/base/browser/ui/splitview/splitview.ts | 4 +-- src/vs/base/browser/ui/toolbar/toolbar.ts | 1 + src/vs/base/common/stream.ts | 31 ++++++++++++++----- src/vs/base/test/common/stream.test.ts | 8 +++-- .../diffEditorWidget2/diffEditorEditors.ts | 4 +-- src/vs/editor/common/model/textModel.ts | 4 ++- .../suggest/browser/suggestController.ts | 2 +- src/vs/platform/actions/browser/buttonbar.ts | 2 +- .../platform/checksum/node/checksumService.ts | 12 +++++-- src/vs/platform/files/common/fileService.ts | 12 +++++-- .../node/diskFileSystemProviderServer.ts | 11 ++++--- src/vs/workbench/browser/dnd.ts | 6 ++-- .../browser/parts/editor/editorGroupView.ts | 2 +- .../parts/editor/editorGroupWatermark.ts | 2 +- .../browser/parts/editor/tabsTitleControl.ts | 10 +++--- .../common/editor/editorGroupModel.ts | 2 +- .../browser/editors/textFileEditorTracker.ts | 2 +- .../browser/textFileEditorTracker.test.ts | 3 ++ .../browser/inlineChatController.ts | 3 +- .../inlineChat/browser/inlineChatSession.ts | 2 ++ .../inlineChat/browser/inlineChatWidget.ts | 8 ++--- .../test/browser/inlineChatController.test.ts | 26 ++++------------ .../markers/test/browser/markersModel.test.ts | 3 ++ .../editor/browser/codeEditorService.ts | 4 +-- .../services/editor/browser/editorService.ts | 6 ++-- .../browser/browserTextFileService.ts | 2 +- .../textfile/browser/textFileService.ts | 7 +++-- .../common/textFileSaveParticipant.ts | 5 ++- .../test/browser/textFileEditorModel.test.ts | 4 +-- .../storedFileWorkingCopySaveParticipant.ts | 1 + .../workingCopyFileOperationParticipant.ts | 1 + .../test/browser/workbenchTestServices.ts | 13 ++++---- 33 files changed, 124 insertions(+), 80 deletions(-) diff --git a/src/vs/base/browser/ui/grid/gridview.ts b/src/vs/base/browser/ui/grid/gridview.ts index c89c6a7a063..9445c64286a 100644 --- a/src/vs/base/browser/ui/grid/gridview.ts +++ b/src/vs/base/browser/ui/grid/gridview.ts @@ -705,6 +705,7 @@ class BranchNode implements ISplitView, IDisposable { this.splitviewSashResetDisposable.dispose(); this.childrenSashResetDisposable.dispose(); this.childrenChangeDisposable.dispose(); + this.onDidScrollDisposable.dispose(); this.splitview.dispose(); } } diff --git a/src/vs/base/browser/ui/splitview/splitview.ts b/src/vs/base/browser/ui/splitview/splitview.ts index b822db43751..28f18d42537 100644 --- a/src/vs/base/browser/ui/splitview/splitview.ts +++ b/src/vs/base/browser/ui/splitview/splitview.ts @@ -565,11 +565,11 @@ export class SplitView extends Disposable { this.sashContainer = append(this.el, $('.sash-container')); this.viewContainer = $('.split-view-container'); - this.scrollable = new Scrollable({ + this.scrollable = this._register(new Scrollable({ forceIntegerValues: true, smoothScrollDuration: 125, scheduleAtNextAnimationFrame - }); + })); this.scrollableElement = this._register(new SmoothScrollableElement(this.viewContainer, { vertical: this.orientation === Orientation.VERTICAL ? (options.scrollbarVisibility ?? ScrollbarVisibility.Auto) : ScrollbarVisibility.Hidden, horizontal: this.orientation === Orientation.HORIZONTAL ? (options.scrollbarVisibility ?? ScrollbarVisibility.Auto) : ScrollbarVisibility.Hidden diff --git a/src/vs/base/browser/ui/toolbar/toolbar.ts b/src/vs/base/browser/ui/toolbar/toolbar.ts index 1a6089b5958..67a2401ecfe 100644 --- a/src/vs/base/browser/ui/toolbar/toolbar.ts +++ b/src/vs/base/browser/ui/toolbar/toolbar.ts @@ -212,6 +212,7 @@ export class ToolBar extends Disposable { override dispose(): void { this.clear(); + this.disposables.dispose(); super.dispose(); } } diff --git a/src/vs/base/common/stream.ts b/src/vs/base/common/stream.ts index 9f1039c0e52..dca56d44d38 100644 --- a/src/vs/base/common/stream.ts +++ b/src/vs/base/common/stream.ts @@ -519,13 +519,14 @@ export function consumeStream(stream: ReadableStreamEvents, reducer return new Promise((resolve, reject) => { const chunks: T[] = []; - listenStream(stream, { + const l = listenStream(stream, { onData: chunk => { if (reducer) { chunks.push(chunk); } }, onError: error => { + l.dispose(); if (reducer) { reject(error); } else { @@ -533,6 +534,7 @@ export function consumeStream(stream: ReadableStreamEvents, reducer } }, onEnd: () => { + l.dispose(); if (reducer) { resolve(reducer(chunks)); } else { @@ -570,15 +572,18 @@ export interface IStreamListener { export function listenStream(stream: ReadableStreamEvents, listener: IStreamListener): IDisposable { let destroyed = false; + // error and end events are in the next microtask so that a stream that is + // closed synchronously (e.g from a memory `toStream`) can get its disposable + // and destroy the stream. stream.on('error', error => { if (!destroyed) { - listener.onError(error); + queueMicrotask(() => listener.onError(error)); } }); stream.on('end', () => { if (!destroyed) { - listener.onEnd(); + queueMicrotask(() => listener.onEnd()); } }); @@ -692,10 +697,16 @@ export function toReadable(t: T): Readable { export function transform(stream: ReadableStreamEvents, transformer: ITransformer, reducer: IReducer): ReadableStream { const target = newWriteableStream(reducer); - listenStream(stream, { + const l = listenStream(stream, { onData: data => target.write(transformer.data(data)), - onError: error => target.error(transformer.error ? transformer.error(error) : error), - onEnd: () => target.end() + onError: error => { + l.dispose(); + target.error(transformer.error ? transformer.error(error) : error); + }, + onEnd: () => { + l.dispose(); + target.end(); + } }); return target; @@ -740,7 +751,7 @@ export function prefixedStream(prefix: T, stream: ReadableStream, reducer: const target = newWriteableStream(reducer); - listenStream(stream, { + const l = listenStream(stream, { onData: data => { // Handle prefix only once @@ -752,8 +763,12 @@ export function prefixedStream(prefix: T, stream: ReadableStream, reducer: return target.write(data); }, - onError: error => target.error(error), + onError: error => { + l.dispose(); + target.error(error); + }, onEnd: () => { + l.dispose(); // Handle prefix only once if (!prefixHandled) { diff --git a/src/vs/base/test/common/stream.test.ts b/src/vs/base/test/common/stream.test.ts index 78c38d691a4..fe69e603827 100644 --- a/src/vs/base/test/common/stream.test.ts +++ b/src/vs/base/test/common/stream.test.ts @@ -315,14 +315,14 @@ suite('Stream', () => { assert.strictEqual(consumed, undefined); }); - test('listenStream', () => { + test('listenStream', async () => { const stream = newWriteableStream(strings => strings.join()); let error = false; let end = false; let data = ''; - listenStream(stream, { + const l = listenStream(stream, { onData: d => { data = d; }, @@ -345,10 +345,14 @@ suite('Stream', () => { assert.strictEqual(end, false); stream.error(new Error()); + await new Promise(r => queueMicrotask(r)); assert.strictEqual(error, true); stream.end('Final Bit'); + await new Promise(r => queueMicrotask(r)); assert.strictEqual(end, true); + + l.dispose(); }); test('listenStream - dispose', () => { diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts index cce99a94271..ee764c81aed 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts @@ -35,8 +35,8 @@ export class DiffEditorEditors extends Disposable { ) { super(); - this.original = this._createLeftHandSideEditor(_options.editorOptions.get(), codeEditorWidgetOptions.originalEditor || {}); - this.modified = this._createRightHandSideEditor(_options.editorOptions.get(), codeEditorWidgetOptions.modifiedEditor || {}); + this.original = this._register(this._createLeftHandSideEditor(_options.editorOptions.get(), codeEditorWidgetOptions.originalEditor || {})); + this.modified = this._register(this._createRightHandSideEditor(_options.editorOptions.get(), codeEditorWidgetOptions.modifiedEditor || {})); this._register(autorunHandleChanges({ createEmptyChangeSummary: () => ({} as IDiffEditorConstructionOptions), diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index b600bd7e3bb..409216fb9d9 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -67,19 +67,21 @@ export function createTextBufferFactoryFromStream(stream: ITextStream | VSBuffer let done = false; - listenStream(stream, { + const l = listenStream(stream, { onData: chunk => { builder.acceptChunk((typeof chunk === 'string') ? chunk : chunk.toString()); }, onError: error => { if (!done) { done = true; + l.dispose(); reject(error); } }, onEnd: () => { if (!done) { done = true; + l.dispose(); resolve(builder.finish()); } } diff --git a/src/vs/editor/contrib/suggest/browser/suggestController.ts b/src/vs/editor/contrib/suggest/browser/suggestController.ts index 887fb23cb2d..6450e989bce 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestController.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestController.ts @@ -143,7 +143,7 @@ export class SuggestController implements IEditorContribution { // context key: update insert/replace mode const ctxInsertMode = SuggestContext.InsertMode.bindTo(_contextKeyService); ctxInsertMode.set(editor.getOption(EditorOption.suggest).insertMode); - this.model.onDidTrigger(() => ctxInsertMode.set(editor.getOption(EditorOption.suggest).insertMode)); + this._toDispose.add(this.model.onDidTrigger(() => ctxInsertMode.set(editor.getOption(EditorOption.suggest).insertMode))); this.widget = this._toDispose.add(new IdleValue(() => { diff --git a/src/vs/platform/actions/browser/buttonbar.ts b/src/vs/platform/actions/browser/buttonbar.ts index cb6ab4c31df..4965d6bb5cd 100644 --- a/src/vs/platform/actions/browser/buttonbar.ts +++ b/src/vs/platform/actions/browser/buttonbar.ts @@ -55,7 +55,7 @@ export class MenuWorkbenchButtonBar extends ButtonBar { 'workbenchActionExecuted', { id: e.action.id, from: options.telemetrySource! } ); - }, this._store); + }, undefined, this._store); } const conifgProvider: IButtonConfigProvider = options?.buttonConfigProvider ?? (() => ({ showLabel: true })); diff --git a/src/vs/platform/checksum/node/checksumService.ts b/src/vs/platform/checksum/node/checksumService.ts index e4214019ff1..0554a503960 100644 --- a/src/vs/platform/checksum/node/checksumService.ts +++ b/src/vs/platform/checksum/node/checksumService.ts @@ -20,10 +20,16 @@ export class ChecksumService implements IChecksumService { return new Promise((resolve, reject) => { const hash = createHash('md5'); - listenStream(stream, { + const l = listenStream(stream, { onData: data => hash.update(data.buffer), - onError: error => reject(error), - onEnd: () => resolve(hash.digest('base64').replace(/=+$/, '')) + onError: error => { + l.dispose(); + reject(error); + }, + onEnd: () => { + l.dispose(); + resolve(hash.digest('base64').replace(/=+$/, '')); + } }); }); } diff --git a/src/vs/platform/files/common/fileService.ts b/src/vs/platform/files/common/fileService.ts index e88ff723b8b..dc22a8203e1 100644 --- a/src/vs/platform/files/common/fileService.ts +++ b/src/vs/platform/files/common/fileService.ts @@ -1228,7 +1228,7 @@ export class FileService extends Disposable implements IFileService { } return new Promise((resolve, reject) => { - listenStream(stream, { + const l = listenStream(stream, { onData: async chunk => { // pause stream to perform async write operation @@ -1248,8 +1248,14 @@ export class FileService extends Disposable implements IFileService { // handler again before finishing. setTimeout(() => stream.resume()); }, - onError: error => reject(error), - onEnd: () => resolve() + onError: error => { + l.dispose(); + reject(error); + }, + onEnd: () => { + l.dispose(); + resolve(); + } }); }); } diff --git a/src/vs/platform/files/node/diskFileSystemProviderServer.ts b/src/vs/platform/files/node/diskFileSystemProviderServer.ts index b7e81ab4491..989a714094b 100644 --- a/src/vs/platform/files/node/diskFileSystemProviderServer.ts +++ b/src/vs/platform/files/node/diskFileSystemProviderServer.ts @@ -106,16 +106,19 @@ export abstract class AbstractDiskFileSystemProviderChannel extends Disposabl // Ensure to cancel the read operation when there is no more // listener on the other side to prevent unneeded work. - cts.cancel(); + cts.dispose(true); } }); const fileStream = this.provider.readFileStream(resource, opts, cts.token); - listenStream(fileStream, { + const l = listenStream(fileStream, { onData: chunk => emitter.fire(VSBuffer.wrap(chunk)), - onError: error => emitter.fire(error), + onError: error => { + l.dispose(); + emitter.fire(error); + }, onEnd: () => { - + l.dispose(); // Forward event emitter.fire('end'); diff --git a/src/vs/workbench/browser/dnd.ts b/src/vs/workbench/browser/dnd.ts index c196ac2132e..a025892f899 100644 --- a/src/vs/workbench/browser/dnd.ts +++ b/src/vs/workbench/browser/dnd.ts @@ -12,7 +12,7 @@ import { ITreeDragOverReaction } from 'vs/base/browser/ui/tree/tree'; import { coalesce } from 'vs/base/common/arrays'; import { UriList, VSDataTransfer } from 'vs/base/common/dataTransfer'; import { Emitter } from 'vs/base/common/event'; -import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, IDisposable, markAsSingleton } from 'vs/base/common/lifecycle'; import { stringify } from 'vs/base/common/marshalling'; import { Mimes } from 'vs/base/common/mime'; import { FileAccess, Schemas } from 'vs/base/common/network'; @@ -427,8 +427,10 @@ export class CompositeDragAndDropObserver extends Disposable { static get INSTANCE(): CompositeDragAndDropObserver { if (!CompositeDragAndDropObserver.instance) { CompositeDragAndDropObserver.instance = new CompositeDragAndDropObserver(); + markAsSingleton(CompositeDragAndDropObserver.instance); } + return CompositeDragAndDropObserver.instance; } @@ -523,7 +525,7 @@ export class CompositeDragAndDropObserver extends Disposable { if (callbacks.onDragEnd) { this.onDragEnd.event(e => { callbacks.onDragEnd!(e); - }); + }, this, disposableStore); } return this._register(disposableStore); diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index c80fa632d3e..01b92a4b7e2 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -244,7 +244,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { const groupEditorsCountContext = EditorGroupEditorsCountContext.bindTo(this.scopedContextKeyService); const groupLockedContext = ActiveEditorGroupLockedContext.bindTo(this.scopedContextKeyService); - const activeEditorListener = new MutableDisposable(); + const activeEditorListener = this._register(new MutableDisposable()); const observeActiveEditor = () => { activeEditorListener.clear(); diff --git a/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts b/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts index 55d6506abab..ff8a2778c7c 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts @@ -90,7 +90,7 @@ export class EditorGroupWatermark extends Disposable { } private registerListeners(): void { - this.lifecycleService.onDidShutdown(() => this.dispose()); + this._register(this.lifecycleService.onDidShutdown(() => this.dispose())); this._register(this.configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('workbench.tips.enabled')) { diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index d712b1a146b..3272b3e4839 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -184,7 +184,7 @@ export class TabsTitleControl extends TitleControl { this.updateTabSizing(false); // Tabs Scrollbar - this.tabsScrollbar = this._register(this.createTabsScrollbar(this.tabsContainer)); + this.tabsScrollbar = this.createTabsScrollbar(this.tabsContainer); this.tabsAndActionsContainer.appendChild(this.tabsScrollbar.getDomNode()); // Tabs Container listeners @@ -206,19 +206,19 @@ export class TabsTitleControl extends TitleControl { } private createTabsScrollbar(scrollable: HTMLElement): ScrollableElement { - const tabsScrollbar = new ScrollableElement(scrollable, { + const tabsScrollbar = this._register(new ScrollableElement(scrollable, { horizontal: ScrollbarVisibility.Auto, horizontalScrollbarSize: this.getTabsScrollbarSizing(), vertical: ScrollbarVisibility.Hidden, scrollYToX: true, useShadows: false - }); + })); - tabsScrollbar.onScroll(e => { + this._register(tabsScrollbar.onScroll(e => { if (e.scrollLeftChanged) { scrollable.scrollLeft = e.scrollLeft; } - }); + })); return tabsScrollbar; } diff --git a/src/vs/workbench/common/editor/editorGroupModel.ts b/src/vs/workbench/common/editor/editorGroupModel.ts index 7fc1ae6cbc0..0a31b47bcf7 100644 --- a/src/vs/workbench/common/editor/editorGroupModel.ts +++ b/src/vs/workbench/common/editor/editorGroupModel.ts @@ -404,7 +404,7 @@ export class EditorGroupModel extends Disposable { } private registerEditorListeners(editor: EditorInput): void { - const listeners = new DisposableStore(); + const listeners = this._register(new DisposableStore()); // Re-emit disposal of editor input as our own event listeners.add(Event.once(editor.onWillDispose)(() => { diff --git a/src/vs/workbench/contrib/files/browser/editors/textFileEditorTracker.ts b/src/vs/workbench/contrib/files/browser/editors/textFileEditorTracker.ts index fb876ba670a..afa94056cd5 100644 --- a/src/vs/workbench/contrib/files/browser/editors/textFileEditorTracker.ts +++ b/src/vs/workbench/contrib/files/browser/editors/textFileEditorTracker.ts @@ -47,7 +47,7 @@ export class TextFileEditorTracker extends Disposable implements IWorkbenchContr this._register(this.hostService.onDidChangeFocus(hasFocus => hasFocus ? this.reloadVisibleTextFileEditors() : undefined)); // Lifecycle - this.lifecycleService.onDidShutdown(() => this.dispose()); + this._register(this.lifecycleService.onDidShutdown(() => this.dispose())); } //#region Text File: Ensure every dirty text and untitled file is opened in an editor diff --git a/src/vs/workbench/contrib/files/test/browser/textFileEditorTracker.test.ts b/src/vs/workbench/contrib/files/test/browser/textFileEditorTracker.test.ts index bc69b29a381..b0afe7cc554 100644 --- a/src/vs/workbench/contrib/files/test/browser/textFileEditorTracker.test.ts +++ b/src/vs/workbench/contrib/files/test/browser/textFileEditorTracker.test.ts @@ -77,6 +77,7 @@ suite('Files - TextFileEditorTracker', () => { instantiationService.stub(IWorkspaceTrustRequestService, new TestWorkspaceTrustRequestService(false)); const editorService: EditorService = instantiationService.createInstance(EditorService); + disposables.add(editorService); instantiationService.stub(IEditorService, editorService); const accessor = instantiationService.createInstance(TestServiceAccessor); @@ -93,6 +94,7 @@ suite('Files - TextFileEditorTracker', () => { const resource = toResource.call(this, '/path/index.txt'); const model = await accessor.textFileService.files.resolve(resource) as IResolvedTextFileEditorModel; + disposables.add(model); model.textEditorModel.setValue('Super Good'); assert.strictEqual(snapshotToString(model.createSnapshot()!), 'Super Good'); @@ -141,6 +143,7 @@ suite('Files - TextFileEditorTracker', () => { } const model = await accessor.textFileService.files.resolve(resource) as IResolvedTextFileEditorModel; + disposables.add(model); model.textEditorModel.setValue('Super Good'); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index 104830cc852..f91de7e3ee8 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -100,7 +100,7 @@ export class InlineChatController implements IEditorContribution { private _messages = this._store.add(new Emitter()); - private readonly _sessionStore: DisposableStore = new DisposableStore(); + private readonly _sessionStore: DisposableStore = this._store.add(new DisposableStore()); private readonly _stashedSession: MutableDisposable = this._store.add(new MutableDisposable()); private _activeSession?: Session; private _strategy?: EditModeStrategy; @@ -146,6 +146,7 @@ export class InlineChatController implements IEditorContribution { } dispose(): void { + this._strategy?.dispose(); this._stashedSession.clear(); this.finishExistingSession(); this._store.dispose(); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts index 434aea1f4d2..fcbb998dc79 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts @@ -385,6 +385,8 @@ export interface IInlineChatSessionService { // recordings(): readonly Recording[]; + + dispose(): void; } type SessionData = { diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts index bf16c0aec05..8619088ce96 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts @@ -230,7 +230,7 @@ export class InlineChatWidget { })); const uri = URI.from({ scheme: 'vscode', authority: 'inline-chat', path: `/inline-chat/model${InlineChatWidget._modelPool++}.txt` }); - this._inputModel = this._modelService.getModel(uri) ?? this._modelService.createModel('', null, uri); + this._inputModel = this._store.add(this._modelService.getModel(uri) ?? this._modelService.createModel('', null, uri)); this._inputEditor.setModel(this._inputModel); // --- context keys @@ -359,13 +359,13 @@ export class InlineChatWidget { this._store.add(feedbackToolbar); // preview editors - this._previewDiffEditor = new IdleValue(() => this._store.add(_instantiationService.createInstance(EmbeddedDiffEditorWidget2, this._elements.previewDiff, { + this._previewDiffEditor = this._store.add(new IdleValue(() => this._store.add(_instantiationService.createInstance(EmbeddedDiffEditorWidget2, this._elements.previewDiff, { ..._previewEditorEditorOptions, onlyShowAccessibleDiffViewer: this._accessibilityService.isScreenReaderOptimized(), - }, { modifiedEditor: codeEditorWidgetOptions, originalEditor: codeEditorWidgetOptions }, parentEditor))); + }, { modifiedEditor: codeEditorWidgetOptions, originalEditor: codeEditorWidgetOptions }, parentEditor)))); this._previewCreateTitle = this._store.add(_instantiationService.createInstance(ResourceLabel, this._elements.previewCreateTitle, { supportIcons: true })); - this._previewCreateEditor = new IdleValue(() => this._store.add(_instantiationService.createInstance(EmbeddedCodeEditorWidget, this._elements.previewCreate, _previewEditorEditorOptions, codeEditorWidgetOptions, parentEditor))); + this._previewCreateEditor = this._store.add(new IdleValue(() => this._store.add(_instantiationService.createInstance(EmbeddedCodeEditorWidget, this._elements.previewCreate, _previewEditorEditorOptions, codeEditorWidgetOptions, parentEditor)))); this._elements.message.tabIndex = 0; this._elements.message.ariaLabel = this._accessibleViewService.getOpenAriaHint(AccessibilityVerbositySettingId.InlineChat); diff --git a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts index ab5e4b27e1b..7a9beea7519 100644 --- a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts +++ b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts @@ -24,7 +24,6 @@ import { IEditorProgressService, IProgressRunner } from 'vs/platform/progress/co import { mock } from 'vs/base/test/common/mock'; import { Emitter, Event } from 'vs/base/common/event'; import { equals } from 'vs/base/common/arrays'; -import { timeout } from 'vs/base/common/async'; import { IChatAccessibilityService } from 'vs/workbench/contrib/chat/browser/chat'; import { IChatResponseViewModel } from 'vs/workbench/contrib/chat/common/chatViewModel'; import { IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; @@ -114,11 +113,11 @@ suite('InteractiveChatController', function () { }] ); - instaService = workbenchInstantiationService(undefined, store).createChild(serviceCollection); - inlineChatSessionService = instaService.get(IInlineChatSessionService); + instaService = store.add(workbenchInstantiationService(undefined, store).createChild(serviceCollection)); + inlineChatSessionService = store.add(instaService.get(IInlineChatSessionService)); - model = instaService.get(IModelService).createModel('Hello\nWorld\nHello Again\nHello World\n', null); - editor = instantiateTestCodeEditor(instaService, model); + model = store.add(instaService.get(IModelService).createModel('Hello\nWorld\nHello Again\nHello World\n', null)); + editor = store.add(instantiateTestCodeEditor(instaService, model)); store.add(inlineChatService.addProvider({ debugName: 'Unit Test', @@ -142,8 +141,6 @@ suite('InteractiveChatController', function () { }); teardown(function () { - editor.dispose(); - model.dispose(); store.clear(); ctrl?.dispose(); }); @@ -295,19 +292,8 @@ suite('InteractiveChatController', function () { wholeRange: new Range(3, 1, 3, 3) }; }, - async provideResponse(session, request) { - - // SLOW response - await timeout(50000); - - return { - type: InlineChatResponseType.EditorEdit, - id: Math.random(), - edits: [{ - range: new Range(1, 1, 1, 1), // EDIT happens outside of whole range - text: `${request.prompt}\n${request.prompt}` - }] - }; + provideResponse(session, request) { + return new Promise(() => { }); } }); store.add(d); diff --git a/src/vs/workbench/contrib/markers/test/browser/markersModel.test.ts b/src/vs/workbench/contrib/markers/test/browser/markersModel.test.ts index 9cb2c9dc650..b8334c948d6 100644 --- a/src/vs/workbench/contrib/markers/test/browser/markersModel.test.ts +++ b/src/vs/workbench/contrib/markers/test/browser/markersModel.test.ts @@ -8,6 +8,7 @@ import { URI } from 'vs/base/common/uri'; import { IMarker, MarkerSeverity, IRelatedInformation } from 'vs/platform/markers/common/markers'; import { MarkersModel, Marker, ResourceMarkers, RelatedInformation } from 'vs/workbench/contrib/markers/browser/markersModel'; import { groupBy } from 'vs/base/common/collections'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; class TestMarkersModel extends MarkersModel { @@ -27,6 +28,8 @@ class TestMarkersModel extends MarkersModel { suite('MarkersModel Test', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + test('marker ids are unique', function () { const marker1 = anErrorWithRange(3); const marker2 = anErrorWithRange(3); diff --git a/src/vs/workbench/services/editor/browser/codeEditorService.ts b/src/vs/workbench/services/editor/browser/codeEditorService.ts index 657f203312c..9930f85f58c 100644 --- a/src/vs/workbench/services/editor/browser/codeEditorService.ts +++ b/src/vs/workbench/services/editor/browser/codeEditorService.ts @@ -25,8 +25,8 @@ export class CodeEditorService extends AbstractCodeEditorService { ) { super(themeService); - this.registerCodeEditorOpenHandler(this.doOpenCodeEditor.bind(this)); - this.registerCodeEditorOpenHandler(this.doOpenCodeEditorFromDiff.bind(this)); + this._register(this.registerCodeEditorOpenHandler(this.doOpenCodeEditor.bind(this))); + this._register(this.registerCodeEditorOpenHandler(this.doOpenCodeEditorFromDiff.bind(this))); } getActiveCodeEditor(): ICodeEditor | null { diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index e241e493f65..f997b3ecfbd 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -83,9 +83,9 @@ export class EditorService extends Disposable implements EditorServiceImpl { // Editor & group changes this.editorGroupService.whenReady.then(() => this.onEditorGroupsReady()); - this.editorGroupService.onDidChangeActiveGroup(group => this.handleActiveEditorChange(group)); - this.editorGroupService.onDidAddGroup(group => this.registerGroupListeners(group as IEditorGroupView)); - this.editorsObserver.onDidMostRecentlyActiveEditorsChange(() => this._onDidMostRecentlyActiveEditorsChange.fire()); + this._register(this.editorGroupService.onDidChangeActiveGroup(group => this.handleActiveEditorChange(group))); + this._register(this.editorGroupService.onDidAddGroup(group => this.registerGroupListeners(group as IEditorGroupView))); + this._register(this.editorsObserver.onDidMostRecentlyActiveEditorsChange(() => this._onDidMostRecentlyActiveEditorsChange.fire())); // Out of workspace file watchers this._register(this.onDidVisibleEditorsChange(() => this.handleVisibleEditorsChange())); diff --git a/src/vs/workbench/services/textfile/browser/browserTextFileService.ts b/src/vs/workbench/services/textfile/browser/browserTextFileService.ts index 78957c01afd..188ca299d5f 100644 --- a/src/vs/workbench/services/textfile/browser/browserTextFileService.ts +++ b/src/vs/workbench/services/textfile/browser/browserTextFileService.ts @@ -54,7 +54,7 @@ export class BrowserTextFileService extends AbstractTextFileService { private registerListeners(): void { // Lifecycle - this.lifecycleService.onBeforeShutdown(event => event.veto(this.onBeforeShutdown(), 'veto.textFiles')); + this._register(this.lifecycleService.onBeforeShutdown(event => event.veto(this.onBeforeShutdown(), 'veto.textFiles'))); } private onBeforeShutdown(): boolean { diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index 45015baa458..f80681a7058 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -89,7 +89,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex private provideDecorations(): void { // Text file model decorations - this.decorationsService.registerDecorationsProvider(new class extends Disposable implements IDecorationsProvider { + const provider = new class extends Disposable implements IDecorationsProvider { readonly label = localize('textFileModelDecorations', "Text File Model Decorations"); @@ -160,7 +160,10 @@ export abstract class AbstractTextFileService extends Disposable implements ITex return undefined; } - }(this.files)); + }(this.files); + + this._register(provider); + this._register(this.decorationsService.registerDecorationsProvider(provider)); } //#endregin diff --git a/src/vs/workbench/services/textfile/common/textFileSaveParticipant.ts b/src/vs/workbench/services/textfile/common/textFileSaveParticipant.ts index ab3cb0a0e8a..12affdd638f 100644 --- a/src/vs/workbench/services/textfile/common/textFileSaveParticipant.ts +++ b/src/vs/workbench/services/textfile/common/textFileSaveParticipant.ts @@ -60,11 +60,14 @@ export class TextFileSaveParticipant extends Disposable { model.textEditorModel?.pushStackElement(); }, () => { // user cancel - cts.dispose(true); + cts.cancel(); + }).finally(() => { + cts.dispose(); }); } override dispose(): void { this.saveParticipants.splice(0, this.saveParticipants.length); + super.dispose(); } } diff --git a/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts b/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts index 7595d417a5f..0dcc1e614ee 100644 --- a/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts +++ b/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts @@ -638,8 +638,8 @@ suite('Files - TextFileEditorModel', () => { }); test('save() and isDirty() - proper with check for mtimes', async function () { - const input1 = createFileEditorInput(instantiationService, toResource.call(this, '/path/index_async2.txt')); - const input2 = createFileEditorInput(instantiationService, toResource.call(this, '/path/index_async.txt')); + const input1 = disposables.add(createFileEditorInput(instantiationService, toResource.call(this, '/path/index_async2.txt'))); + const input2 = disposables.add(createFileEditorInput(instantiationService, toResource.call(this, '/path/index_async.txt'))); const model1 = await input1.resolve() as TextFileEditorModel; const model2 = await input2.resolve() as TextFileEditorModel; diff --git a/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopySaveParticipant.ts b/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopySaveParticipant.ts index 18be856eb47..f66950e4281 100644 --- a/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopySaveParticipant.ts +++ b/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopySaveParticipant.ts @@ -69,5 +69,6 @@ export class StoredFileWorkingCopySaveParticipant extends Disposable { override dispose(): void { this.saveParticipants.splice(0, this.saveParticipants.length); + super.dispose(); } } diff --git a/src/vs/workbench/services/workingCopy/common/workingCopyFileOperationParticipant.ts b/src/vs/workbench/services/workingCopy/common/workingCopyFileOperationParticipant.ts index e75246b8b83..3d0f521c340 100644 --- a/src/vs/workbench/services/workingCopy/common/workingCopyFileOperationParticipant.ts +++ b/src/vs/workbench/services/workingCopy/common/workingCopyFileOperationParticipant.ts @@ -46,5 +46,6 @@ export class WorkingCopyFileOperationParticipant extends Disposable { override dispose(): void { this.participants.clear(); + super.dispose(); } } diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 0fffe75c46f..c566bc46401 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -283,15 +283,16 @@ export function workbenchInstantiationService( instantiationService.stub(IUndoRedoService, instantiationService.createInstance(UndoRedoService)); const themeService = new TestThemeService(); instantiationService.stub(IThemeService, themeService); - instantiationService.stub(ILanguageConfigurationService, new TestLanguageConfigurationService()); + instantiationService.stub(ILanguageConfigurationService, disposables.add(new TestLanguageConfigurationService())); instantiationService.stub(IModelService, disposables.add(instantiationService.createInstance(ModelService))); const fileService = overrides?.fileService ? overrides.fileService(instantiationService) : new TestFileService(); instantiationService.stub(IFileService, fileService); const uriIdentityService = new UriIdentityService(fileService); + disposables.add(uriIdentityService); instantiationService.stub(IFilesConfigurationService, disposables.add(new TestFilesConfigurationService(contextKeyService, configService, workspaceContextService, environmentService, uriIdentityService, fileService))); instantiationService.stub(IUriIdentityService, uriIdentityService); - const userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, fileService, uriIdentityService, new NullLogService())); - instantiationService.stub(IUserDataProfileService, new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService)); + const userDataProfilesService = instantiationService.stub(IUserDataProfilesService, disposables.add(new UserDataProfilesService(environmentService, fileService, uriIdentityService, new NullLogService()))); + instantiationService.stub(IUserDataProfileService, disposables.add(new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService))); instantiationService.stub(IWorkingCopyBackupService, overrides?.workingCopyBackupService ? overrides?.workingCopyBackupService(instantiationService) : new TestWorkingCopyBackupService()); instantiationService.stub(ITelemetryService, NullTelemetryService); instantiationService.stub(INotificationService, new TestNotificationService()); @@ -305,7 +306,7 @@ export function workbenchInstantiationService( instantiationService.stub(ITextFileService, overrides?.textFileService ? overrides.textFileService(instantiationService) : disposables.add(instantiationService.createInstance(TestTextFileService))); instantiationService.stub(IHostService, instantiationService.createInstance(TestHostService)); instantiationService.stub(ITextModelService, disposables.add(instantiationService.createInstance(TextModelResolverService))); - instantiationService.stub(ILoggerService, new TestLoggerService(TestEnvironmentService.logsHome)); + instantiationService.stub(ILoggerService, disposables.add(new TestLoggerService(TestEnvironmentService.logsHome))); instantiationService.stub(ILogService, new NullLogService()); const editorGroupService = new TestEditorGroupsService([new TestEditorGroupView(0)]); instantiationService.stub(IEditorGroupsService, editorGroupService); @@ -314,10 +315,10 @@ export function workbenchInstantiationService( instantiationService.stub(IEditorService, editorService); instantiationService.stub(IWorkingCopyEditorService, disposables.add(instantiationService.createInstance(WorkingCopyEditorService))); instantiationService.stub(IEditorResolverService, disposables.add(instantiationService.createInstance(EditorResolverService))); - const textEditorService = overrides?.textEditorService ? overrides.textEditorService(instantiationService) : instantiationService.createInstance(TextEditorService); + const textEditorService = overrides?.textEditorService ? overrides.textEditorService(instantiationService) : disposables.add(instantiationService.createInstance(TextEditorService)); instantiationService.stub(ITextEditorService, textEditorService); instantiationService.stub(ICodeEditorService, disposables.add(new CodeEditorService(editorService, themeService, configService))); - instantiationService.stub(IPaneCompositePartService, new TestPaneCompositeService()); + instantiationService.stub(IPaneCompositePartService, disposables.add(new TestPaneCompositeService())); instantiationService.stub(IListService, new TestListService()); const hoverService = instantiationService.stub(IHoverService, instantiationService.createInstance(TestHoverService)); instantiationService.stub(IQuickInputService, disposables.add(new QuickInputService(configService, instantiationService, keybindingService, contextKeyService, themeService, layoutService, hoverService))); From 7ebb304bbf8e4eaf2a4c71ee6122672c10f988c5 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 18 Aug 2023 08:11:46 -0700 Subject: [PATCH 113/223] Add enabled dim unfocused setting, tweak enabled setting Part of #30522 --- .../browser/accessibilityConfiguration.ts | 60 +++++++++++++++---- .../unfocusedViewDimmingContribution.ts | 39 +++++++----- 2 files changed, 70 insertions(+), 29 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts index 0a7043a5cd8..aac405116e1 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts @@ -4,9 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { Extensions, IConfigurationNode, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; +import { ConfigurationScope, Extensions, IConfigurationNode, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration'; export const accessibilityHelpIsShown = new RawContextKey('accessibilityHelpIsShown', false, true); export const accessibleViewIsShown = new RawContextKey('accessibleViewIsShown', false, true); @@ -14,8 +15,19 @@ export const accessibleViewSupportsNavigation = new RawContextKey('acce export const accessibleViewVerbosityEnabled = new RawContextKey('accessibleViewVerbosityEnabled', false, true); export const accessibleViewGoToSymbolSupported = new RawContextKey('accessibleViewGoToSymbolSupported', false, true); -export const enum AccessibilitySettingId { - UnfocusedViewOpacity = 'accessibility.unfocusedViewOpacity' +/** + * Miscellaneous settings tagged with accessibility and implemented in the accessibility contrib but + * were better to live under workbench for discoverability. + */ +export const enum AccessibilityWorkbenchSettingId { + ViewDimUnfocusedEnabled = 'workbench.view.dimUnfocused.enabled', + ViewDimUnfocusedOpacity = 'workbench.view.dimUnfocused.opacity' +} + +export const enum ViewDimUnfocusedOpacityProperties { + Default = 0.75, + Minimum = 0.2, + Maximum = 1 } export const enum AccessibilityVerbositySettingId { @@ -82,19 +94,41 @@ const configuration: IConfigurationNode = { [AccessibilityVerbositySettingId.EditorUntitledHint]: { description: localize('verbosity.editor.untitledhint', 'Provide information about relevant actions in an untitled text editor.'), ...baseProperty - }, - [AccessibilitySettingId.UnfocusedViewOpacity]: { - description: localize('unfocusedViewOpacity', 'The opacity fraction (0.2 to 1.0) to use for unfocused editors and terminals. This will dim inactive views to make the focused view more obvious.'), - type: 'number', - minimum: 0.2, - maximum: 1, - default: 1, - tags: ['accessibility'] } + // [AccessibilitySettingId.UnfocusedViewOpacity]: { + // description: localize('unfocusedViewOpacity', 'The opacity fraction (0.2 to 1.0) to use for unfocused editors and terminals. This will dim inactive views to make the focused view more obvious.'), + // type: 'number', + // minimum: 0.2, + // maximum: 1, + // default: 1, + // tags: ['accessibility'] + // } } }; export function registerAccessibilityConfiguration() { - const configurationRegistry = Registry.as(Extensions.Configuration); - configurationRegistry.registerConfiguration(configuration); + const registry = Registry.as(Extensions.Configuration); + registry.registerConfiguration(configuration); + + registry.registerConfiguration({ + ...workbenchConfigurationNodeBase, + properties: { + [AccessibilityWorkbenchSettingId.ViewDimUnfocusedEnabled]: { + description: localize('dimUnfocusedEnabled', 'Whether to dim unfocused editors and terminals, making the focused view more obvious.'), + type: 'boolean', + default: false, + tags: ['accessibility'], + scope: ConfigurationScope.MACHINE, + }, + [AccessibilityWorkbenchSettingId.ViewDimUnfocusedOpacity]: { + description: localize('dimUnfocusedOpacity', 'The opacity fraction (0.2 to 1.0) to use for unfocused editors and terminals. This will only take effect when {0} is enabled.', `\`#${AccessibilityWorkbenchSettingId.ViewDimUnfocusedEnabled}#\``), + type: 'number', + minimum: ViewDimUnfocusedOpacityProperties.Minimum, + maximum: ViewDimUnfocusedOpacityProperties.Maximum, + default: ViewDimUnfocusedOpacityProperties.Default, + tags: ['accessibility'], + scope: ConfigurationScope.MACHINE, + } + } + }); } diff --git a/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts b/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts index e865074afda..847eb63fd2d 100644 --- a/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts +++ b/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts @@ -8,7 +8,7 @@ import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { clamp } from 'vs/base/common/numbers'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { AccessibilitySettingId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; +import { AccessibilityWorkbenchSettingId, ViewDimUnfocusedOpacityProperties } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; export class UnfocusedViewDimmingContribution extends Disposable implements IWorkbenchContribution { constructor( @@ -22,24 +22,22 @@ export class UnfocusedViewDimmingContribution extends Disposable implements IWor this._register(toDisposable(() => elStyle.remove())); this._register(Event.runAndSubscribe(configurationService.onDidChangeConfiguration, e => { - if (e && !e.affectsConfiguration(AccessibilitySettingId.UnfocusedViewOpacity)) { + if (e && !e.affectsConfiguration(AccessibilityWorkbenchSettingId.ViewDimUnfocusedEnabled) && !e.affectsConfiguration(AccessibilityWorkbenchSettingId.ViewDimUnfocusedOpacity)) { return; } - let opacity: number; - const opacityConfig = configurationService.getValue(AccessibilitySettingId.UnfocusedViewOpacity); - if (typeof opacityConfig !== 'number') { - opacity = 1; - } else { - opacity = clamp(opacityConfig, 0.2, 1); - } - let cssTextContent = ''; - // Only add the styles if the feature is used - if (opacity !== 1) { - const rules = new Set(); + const enabled = ensureBoolean(configurationService.getValue(AccessibilityWorkbenchSettingId.ViewDimUnfocusedEnabled), false); + if (enabled) { + const opacity = clamp( + ensureNumber(configurationService.getValue(AccessibilityWorkbenchSettingId.ViewDimUnfocusedOpacity), ViewDimUnfocusedOpacityProperties.Default), + ViewDimUnfocusedOpacityProperties.Minimum, + ViewDimUnfocusedOpacityProperties.Maximum + ); + if (opacity !== 1) { + const rules = new Set(); const filterRule = `filter: opacity(${opacity});`; // Terminal tabs rules.add(`.monaco-workbench .pane-body.integrated-terminal:not(:focus-within) .tabs-container { ${filterRule} }`); @@ -49,11 +47,20 @@ export class UnfocusedViewDimmingContribution extends Disposable implements IWor rules.add(`.monaco-workbench .editor-instance:not(:focus-within) .monaco-editor { ${filterRule} }`); // Terminal editors rules.add(`.monaco-workbench .editor-instance:not(:focus-within) .terminal-wrapper { ${filterRule} }`); + cssTextContent = [...rules].join('\n'); } - cssTextContent = [...rules].join('\n'); - } - elStyle.textContent = cssTextContent; + elStyle.textContent = cssTextContent; + } })); } } + + +function ensureBoolean(value: unknown, defaultValue: boolean): boolean { + return typeof value === 'boolean' ? value : defaultValue; +} + +function ensureNumber(value: unknown, defaultValue: number): number { + return typeof value === 'number' ? value : defaultValue; +} From 42b6e31801cd4d8d327be1b5ec16582fc06b3137 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 18 Aug 2023 08:18:03 -0700 Subject: [PATCH 114/223] Don't add dim unfocused element if not used Fixes #30522 --- .../unfocusedViewDimmingContribution.ts | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts b/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts index 847eb63fd2d..1dc2b4b3605 100644 --- a/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts +++ b/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts @@ -11,15 +11,14 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { AccessibilityWorkbenchSettingId, ViewDimUnfocusedOpacityProperties } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; export class UnfocusedViewDimmingContribution extends Disposable implements IWorkbenchContribution { + private _styleElement?: HTMLStyleElement; + constructor( @IConfigurationService configurationService: IConfigurationService, ) { super(); - const elStyle = document.createElement('style'); - elStyle.className = 'accessibilityUnfocusedViewOpacity'; - document.head.appendChild(elStyle); - this._register(toDisposable(() => elStyle.remove())); + this._register(toDisposable(() => this._removeStyleElement())); this._register(Event.runAndSubscribe(configurationService.onDidChangeConfiguration, e => { if (e && !e.affectsConfiguration(AccessibilityWorkbenchSettingId.ViewDimUnfocusedEnabled) && !e.affectsConfiguration(AccessibilityWorkbenchSettingId.ViewDimUnfocusedOpacity)) { @@ -50,10 +49,29 @@ export class UnfocusedViewDimmingContribution extends Disposable implements IWor cssTextContent = [...rules].join('\n'); } - elStyle.textContent = cssTextContent; + } + + if (cssTextContent.length === 0) { + this._removeStyleElement(); + } else { + this._getStyleElement().textContent = cssTextContent; } })); } + + private _getStyleElement(): HTMLStyleElement { + if (!this._styleElement) { + this._styleElement = document.createElement('style'); + this._styleElement.className = 'accessibilityUnfocusedViewOpacity'; + document.head.appendChild(this._styleElement); + } + return this._styleElement; + } + + private _removeStyleElement(): void { + this._styleElement?.remove(); + this._styleElement = undefined; + } } From f0f36dff5efa066df997197e1d3a78e2727b70a9 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 18 Aug 2023 08:19:47 -0700 Subject: [PATCH 115/223] Remove commented out setting --- .../accessibility/browser/accessibilityConfiguration.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts index aac405116e1..b97be1b44df 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts @@ -95,14 +95,6 @@ const configuration: IConfigurationNode = { description: localize('verbosity.editor.untitledhint', 'Provide information about relevant actions in an untitled text editor.'), ...baseProperty } - // [AccessibilitySettingId.UnfocusedViewOpacity]: { - // description: localize('unfocusedViewOpacity', 'The opacity fraction (0.2 to 1.0) to use for unfocused editors and terminals. This will dim inactive views to make the focused view more obvious.'), - // type: 'number', - // minimum: 0.2, - // maximum: 1, - // default: 1, - // tags: ['accessibility'] - // } } }; From 3fe5a470363153bdf415eb39fca7cda471e08cda Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 18 Aug 2023 08:40:33 -0700 Subject: [PATCH 116/223] eng: revert #190623 until everyone can review it (#190752) This reverts commit 6336ee75304eaac32897d8e74c451863d877cb28. --- src/vs/base/browser/ui/grid/gridview.ts | 1 - src/vs/base/browser/ui/splitview/splitview.ts | 4 +-- src/vs/base/browser/ui/toolbar/toolbar.ts | 1 - src/vs/base/common/stream.ts | 31 +++++-------------- src/vs/base/test/common/stream.test.ts | 8 ++--- .../diffEditorWidget2/diffEditorEditors.ts | 4 +-- src/vs/editor/common/model/textModel.ts | 4 +-- .../suggest/browser/suggestController.ts | 2 +- src/vs/platform/actions/browser/buttonbar.ts | 2 +- .../platform/checksum/node/checksumService.ts | 12 ++----- src/vs/platform/files/common/fileService.ts | 12 ++----- .../node/diskFileSystemProviderServer.ts | 11 +++---- src/vs/workbench/browser/dnd.ts | 6 ++-- .../browser/parts/editor/editorGroupView.ts | 2 +- .../parts/editor/editorGroupWatermark.ts | 2 +- .../browser/parts/editor/tabsTitleControl.ts | 10 +++--- .../common/editor/editorGroupModel.ts | 2 +- .../browser/editors/textFileEditorTracker.ts | 2 +- .../browser/textFileEditorTracker.test.ts | 3 -- .../browser/inlineChatController.ts | 3 +- .../inlineChat/browser/inlineChatSession.ts | 2 -- .../inlineChat/browser/inlineChatWidget.ts | 8 ++--- .../test/browser/inlineChatController.test.ts | 26 ++++++++++++---- .../markers/test/browser/markersModel.test.ts | 3 -- .../editor/browser/codeEditorService.ts | 4 +-- .../services/editor/browser/editorService.ts | 6 ++-- .../browser/browserTextFileService.ts | 2 +- .../textfile/browser/textFileService.ts | 7 ++--- .../common/textFileSaveParticipant.ts | 5 +-- .../test/browser/textFileEditorModel.test.ts | 4 +-- .../storedFileWorkingCopySaveParticipant.ts | 1 - .../workingCopyFileOperationParticipant.ts | 1 - .../test/browser/workbenchTestServices.ts | 13 ++++---- 33 files changed, 80 insertions(+), 124 deletions(-) diff --git a/src/vs/base/browser/ui/grid/gridview.ts b/src/vs/base/browser/ui/grid/gridview.ts index 9445c64286a..c89c6a7a063 100644 --- a/src/vs/base/browser/ui/grid/gridview.ts +++ b/src/vs/base/browser/ui/grid/gridview.ts @@ -705,7 +705,6 @@ class BranchNode implements ISplitView, IDisposable { this.splitviewSashResetDisposable.dispose(); this.childrenSashResetDisposable.dispose(); this.childrenChangeDisposable.dispose(); - this.onDidScrollDisposable.dispose(); this.splitview.dispose(); } } diff --git a/src/vs/base/browser/ui/splitview/splitview.ts b/src/vs/base/browser/ui/splitview/splitview.ts index 28f18d42537..b822db43751 100644 --- a/src/vs/base/browser/ui/splitview/splitview.ts +++ b/src/vs/base/browser/ui/splitview/splitview.ts @@ -565,11 +565,11 @@ export class SplitView extends Disposable { this.sashContainer = append(this.el, $('.sash-container')); this.viewContainer = $('.split-view-container'); - this.scrollable = this._register(new Scrollable({ + this.scrollable = new Scrollable({ forceIntegerValues: true, smoothScrollDuration: 125, scheduleAtNextAnimationFrame - })); + }); this.scrollableElement = this._register(new SmoothScrollableElement(this.viewContainer, { vertical: this.orientation === Orientation.VERTICAL ? (options.scrollbarVisibility ?? ScrollbarVisibility.Auto) : ScrollbarVisibility.Hidden, horizontal: this.orientation === Orientation.HORIZONTAL ? (options.scrollbarVisibility ?? ScrollbarVisibility.Auto) : ScrollbarVisibility.Hidden diff --git a/src/vs/base/browser/ui/toolbar/toolbar.ts b/src/vs/base/browser/ui/toolbar/toolbar.ts index 67a2401ecfe..1a6089b5958 100644 --- a/src/vs/base/browser/ui/toolbar/toolbar.ts +++ b/src/vs/base/browser/ui/toolbar/toolbar.ts @@ -212,7 +212,6 @@ export class ToolBar extends Disposable { override dispose(): void { this.clear(); - this.disposables.dispose(); super.dispose(); } } diff --git a/src/vs/base/common/stream.ts b/src/vs/base/common/stream.ts index dca56d44d38..9f1039c0e52 100644 --- a/src/vs/base/common/stream.ts +++ b/src/vs/base/common/stream.ts @@ -519,14 +519,13 @@ export function consumeStream(stream: ReadableStreamEvents, reducer return new Promise((resolve, reject) => { const chunks: T[] = []; - const l = listenStream(stream, { + listenStream(stream, { onData: chunk => { if (reducer) { chunks.push(chunk); } }, onError: error => { - l.dispose(); if (reducer) { reject(error); } else { @@ -534,7 +533,6 @@ export function consumeStream(stream: ReadableStreamEvents, reducer } }, onEnd: () => { - l.dispose(); if (reducer) { resolve(reducer(chunks)); } else { @@ -572,18 +570,15 @@ export interface IStreamListener { export function listenStream(stream: ReadableStreamEvents, listener: IStreamListener): IDisposable { let destroyed = false; - // error and end events are in the next microtask so that a stream that is - // closed synchronously (e.g from a memory `toStream`) can get its disposable - // and destroy the stream. stream.on('error', error => { if (!destroyed) { - queueMicrotask(() => listener.onError(error)); + listener.onError(error); } }); stream.on('end', () => { if (!destroyed) { - queueMicrotask(() => listener.onEnd()); + listener.onEnd(); } }); @@ -697,16 +692,10 @@ export function toReadable(t: T): Readable { export function transform(stream: ReadableStreamEvents, transformer: ITransformer, reducer: IReducer): ReadableStream { const target = newWriteableStream(reducer); - const l = listenStream(stream, { + listenStream(stream, { onData: data => target.write(transformer.data(data)), - onError: error => { - l.dispose(); - target.error(transformer.error ? transformer.error(error) : error); - }, - onEnd: () => { - l.dispose(); - target.end(); - } + onError: error => target.error(transformer.error ? transformer.error(error) : error), + onEnd: () => target.end() }); return target; @@ -751,7 +740,7 @@ export function prefixedStream(prefix: T, stream: ReadableStream, reducer: const target = newWriteableStream(reducer); - const l = listenStream(stream, { + listenStream(stream, { onData: data => { // Handle prefix only once @@ -763,12 +752,8 @@ export function prefixedStream(prefix: T, stream: ReadableStream, reducer: return target.write(data); }, - onError: error => { - l.dispose(); - target.error(error); - }, + onError: error => target.error(error), onEnd: () => { - l.dispose(); // Handle prefix only once if (!prefixHandled) { diff --git a/src/vs/base/test/common/stream.test.ts b/src/vs/base/test/common/stream.test.ts index fe69e603827..78c38d691a4 100644 --- a/src/vs/base/test/common/stream.test.ts +++ b/src/vs/base/test/common/stream.test.ts @@ -315,14 +315,14 @@ suite('Stream', () => { assert.strictEqual(consumed, undefined); }); - test('listenStream', async () => { + test('listenStream', () => { const stream = newWriteableStream(strings => strings.join()); let error = false; let end = false; let data = ''; - const l = listenStream(stream, { + listenStream(stream, { onData: d => { data = d; }, @@ -345,14 +345,10 @@ suite('Stream', () => { assert.strictEqual(end, false); stream.error(new Error()); - await new Promise(r => queueMicrotask(r)); assert.strictEqual(error, true); stream.end('Final Bit'); - await new Promise(r => queueMicrotask(r)); assert.strictEqual(end, true); - - l.dispose(); }); test('listenStream - dispose', () => { diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts index ee764c81aed..cce99a94271 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts @@ -35,8 +35,8 @@ export class DiffEditorEditors extends Disposable { ) { super(); - this.original = this._register(this._createLeftHandSideEditor(_options.editorOptions.get(), codeEditorWidgetOptions.originalEditor || {})); - this.modified = this._register(this._createRightHandSideEditor(_options.editorOptions.get(), codeEditorWidgetOptions.modifiedEditor || {})); + this.original = this._createLeftHandSideEditor(_options.editorOptions.get(), codeEditorWidgetOptions.originalEditor || {}); + this.modified = this._createRightHandSideEditor(_options.editorOptions.get(), codeEditorWidgetOptions.modifiedEditor || {}); this._register(autorunHandleChanges({ createEmptyChangeSummary: () => ({} as IDiffEditorConstructionOptions), diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index 409216fb9d9..b600bd7e3bb 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -67,21 +67,19 @@ export function createTextBufferFactoryFromStream(stream: ITextStream | VSBuffer let done = false; - const l = listenStream(stream, { + listenStream(stream, { onData: chunk => { builder.acceptChunk((typeof chunk === 'string') ? chunk : chunk.toString()); }, onError: error => { if (!done) { done = true; - l.dispose(); reject(error); } }, onEnd: () => { if (!done) { done = true; - l.dispose(); resolve(builder.finish()); } } diff --git a/src/vs/editor/contrib/suggest/browser/suggestController.ts b/src/vs/editor/contrib/suggest/browser/suggestController.ts index 6450e989bce..887fb23cb2d 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestController.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestController.ts @@ -143,7 +143,7 @@ export class SuggestController implements IEditorContribution { // context key: update insert/replace mode const ctxInsertMode = SuggestContext.InsertMode.bindTo(_contextKeyService); ctxInsertMode.set(editor.getOption(EditorOption.suggest).insertMode); - this._toDispose.add(this.model.onDidTrigger(() => ctxInsertMode.set(editor.getOption(EditorOption.suggest).insertMode))); + this.model.onDidTrigger(() => ctxInsertMode.set(editor.getOption(EditorOption.suggest).insertMode)); this.widget = this._toDispose.add(new IdleValue(() => { diff --git a/src/vs/platform/actions/browser/buttonbar.ts b/src/vs/platform/actions/browser/buttonbar.ts index 4965d6bb5cd..cb6ab4c31df 100644 --- a/src/vs/platform/actions/browser/buttonbar.ts +++ b/src/vs/platform/actions/browser/buttonbar.ts @@ -55,7 +55,7 @@ export class MenuWorkbenchButtonBar extends ButtonBar { 'workbenchActionExecuted', { id: e.action.id, from: options.telemetrySource! } ); - }, undefined, this._store); + }, this._store); } const conifgProvider: IButtonConfigProvider = options?.buttonConfigProvider ?? (() => ({ showLabel: true })); diff --git a/src/vs/platform/checksum/node/checksumService.ts b/src/vs/platform/checksum/node/checksumService.ts index 0554a503960..e4214019ff1 100644 --- a/src/vs/platform/checksum/node/checksumService.ts +++ b/src/vs/platform/checksum/node/checksumService.ts @@ -20,16 +20,10 @@ export class ChecksumService implements IChecksumService { return new Promise((resolve, reject) => { const hash = createHash('md5'); - const l = listenStream(stream, { + listenStream(stream, { onData: data => hash.update(data.buffer), - onError: error => { - l.dispose(); - reject(error); - }, - onEnd: () => { - l.dispose(); - resolve(hash.digest('base64').replace(/=+$/, '')); - } + onError: error => reject(error), + onEnd: () => resolve(hash.digest('base64').replace(/=+$/, '')) }); }); } diff --git a/src/vs/platform/files/common/fileService.ts b/src/vs/platform/files/common/fileService.ts index dc22a8203e1..e88ff723b8b 100644 --- a/src/vs/platform/files/common/fileService.ts +++ b/src/vs/platform/files/common/fileService.ts @@ -1228,7 +1228,7 @@ export class FileService extends Disposable implements IFileService { } return new Promise((resolve, reject) => { - const l = listenStream(stream, { + listenStream(stream, { onData: async chunk => { // pause stream to perform async write operation @@ -1248,14 +1248,8 @@ export class FileService extends Disposable implements IFileService { // handler again before finishing. setTimeout(() => stream.resume()); }, - onError: error => { - l.dispose(); - reject(error); - }, - onEnd: () => { - l.dispose(); - resolve(); - } + onError: error => reject(error), + onEnd: () => resolve() }); }); } diff --git a/src/vs/platform/files/node/diskFileSystemProviderServer.ts b/src/vs/platform/files/node/diskFileSystemProviderServer.ts index 989a714094b..b7e81ab4491 100644 --- a/src/vs/platform/files/node/diskFileSystemProviderServer.ts +++ b/src/vs/platform/files/node/diskFileSystemProviderServer.ts @@ -106,19 +106,16 @@ export abstract class AbstractDiskFileSystemProviderChannel extends Disposabl // Ensure to cancel the read operation when there is no more // listener on the other side to prevent unneeded work. - cts.dispose(true); + cts.cancel(); } }); const fileStream = this.provider.readFileStream(resource, opts, cts.token); - const l = listenStream(fileStream, { + listenStream(fileStream, { onData: chunk => emitter.fire(VSBuffer.wrap(chunk)), - onError: error => { - l.dispose(); - emitter.fire(error); - }, + onError: error => emitter.fire(error), onEnd: () => { - l.dispose(); + // Forward event emitter.fire('end'); diff --git a/src/vs/workbench/browser/dnd.ts b/src/vs/workbench/browser/dnd.ts index a025892f899..c196ac2132e 100644 --- a/src/vs/workbench/browser/dnd.ts +++ b/src/vs/workbench/browser/dnd.ts @@ -12,7 +12,7 @@ import { ITreeDragOverReaction } from 'vs/base/browser/ui/tree/tree'; import { coalesce } from 'vs/base/common/arrays'; import { UriList, VSDataTransfer } from 'vs/base/common/dataTransfer'; import { Emitter } from 'vs/base/common/event'; -import { Disposable, DisposableStore, IDisposable, markAsSingleton } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { stringify } from 'vs/base/common/marshalling'; import { Mimes } from 'vs/base/common/mime'; import { FileAccess, Schemas } from 'vs/base/common/network'; @@ -427,10 +427,8 @@ export class CompositeDragAndDropObserver extends Disposable { static get INSTANCE(): CompositeDragAndDropObserver { if (!CompositeDragAndDropObserver.instance) { CompositeDragAndDropObserver.instance = new CompositeDragAndDropObserver(); - markAsSingleton(CompositeDragAndDropObserver.instance); } - return CompositeDragAndDropObserver.instance; } @@ -525,7 +523,7 @@ export class CompositeDragAndDropObserver extends Disposable { if (callbacks.onDragEnd) { this.onDragEnd.event(e => { callbacks.onDragEnd!(e); - }, this, disposableStore); + }); } return this._register(disposableStore); diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index 01b92a4b7e2..c80fa632d3e 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -244,7 +244,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { const groupEditorsCountContext = EditorGroupEditorsCountContext.bindTo(this.scopedContextKeyService); const groupLockedContext = ActiveEditorGroupLockedContext.bindTo(this.scopedContextKeyService); - const activeEditorListener = this._register(new MutableDisposable()); + const activeEditorListener = new MutableDisposable(); const observeActiveEditor = () => { activeEditorListener.clear(); diff --git a/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts b/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts index ff8a2778c7c..55d6506abab 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts @@ -90,7 +90,7 @@ export class EditorGroupWatermark extends Disposable { } private registerListeners(): void { - this._register(this.lifecycleService.onDidShutdown(() => this.dispose())); + this.lifecycleService.onDidShutdown(() => this.dispose()); this._register(this.configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('workbench.tips.enabled')) { diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index 3272b3e4839..d712b1a146b 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -184,7 +184,7 @@ export class TabsTitleControl extends TitleControl { this.updateTabSizing(false); // Tabs Scrollbar - this.tabsScrollbar = this.createTabsScrollbar(this.tabsContainer); + this.tabsScrollbar = this._register(this.createTabsScrollbar(this.tabsContainer)); this.tabsAndActionsContainer.appendChild(this.tabsScrollbar.getDomNode()); // Tabs Container listeners @@ -206,19 +206,19 @@ export class TabsTitleControl extends TitleControl { } private createTabsScrollbar(scrollable: HTMLElement): ScrollableElement { - const tabsScrollbar = this._register(new ScrollableElement(scrollable, { + const tabsScrollbar = new ScrollableElement(scrollable, { horizontal: ScrollbarVisibility.Auto, horizontalScrollbarSize: this.getTabsScrollbarSizing(), vertical: ScrollbarVisibility.Hidden, scrollYToX: true, useShadows: false - })); + }); - this._register(tabsScrollbar.onScroll(e => { + tabsScrollbar.onScroll(e => { if (e.scrollLeftChanged) { scrollable.scrollLeft = e.scrollLeft; } - })); + }); return tabsScrollbar; } diff --git a/src/vs/workbench/common/editor/editorGroupModel.ts b/src/vs/workbench/common/editor/editorGroupModel.ts index 0a31b47bcf7..7fc1ae6cbc0 100644 --- a/src/vs/workbench/common/editor/editorGroupModel.ts +++ b/src/vs/workbench/common/editor/editorGroupModel.ts @@ -404,7 +404,7 @@ export class EditorGroupModel extends Disposable { } private registerEditorListeners(editor: EditorInput): void { - const listeners = this._register(new DisposableStore()); + const listeners = new DisposableStore(); // Re-emit disposal of editor input as our own event listeners.add(Event.once(editor.onWillDispose)(() => { diff --git a/src/vs/workbench/contrib/files/browser/editors/textFileEditorTracker.ts b/src/vs/workbench/contrib/files/browser/editors/textFileEditorTracker.ts index afa94056cd5..fb876ba670a 100644 --- a/src/vs/workbench/contrib/files/browser/editors/textFileEditorTracker.ts +++ b/src/vs/workbench/contrib/files/browser/editors/textFileEditorTracker.ts @@ -47,7 +47,7 @@ export class TextFileEditorTracker extends Disposable implements IWorkbenchContr this._register(this.hostService.onDidChangeFocus(hasFocus => hasFocus ? this.reloadVisibleTextFileEditors() : undefined)); // Lifecycle - this._register(this.lifecycleService.onDidShutdown(() => this.dispose())); + this.lifecycleService.onDidShutdown(() => this.dispose()); } //#region Text File: Ensure every dirty text and untitled file is opened in an editor diff --git a/src/vs/workbench/contrib/files/test/browser/textFileEditorTracker.test.ts b/src/vs/workbench/contrib/files/test/browser/textFileEditorTracker.test.ts index b0afe7cc554..bc69b29a381 100644 --- a/src/vs/workbench/contrib/files/test/browser/textFileEditorTracker.test.ts +++ b/src/vs/workbench/contrib/files/test/browser/textFileEditorTracker.test.ts @@ -77,7 +77,6 @@ suite('Files - TextFileEditorTracker', () => { instantiationService.stub(IWorkspaceTrustRequestService, new TestWorkspaceTrustRequestService(false)); const editorService: EditorService = instantiationService.createInstance(EditorService); - disposables.add(editorService); instantiationService.stub(IEditorService, editorService); const accessor = instantiationService.createInstance(TestServiceAccessor); @@ -94,7 +93,6 @@ suite('Files - TextFileEditorTracker', () => { const resource = toResource.call(this, '/path/index.txt'); const model = await accessor.textFileService.files.resolve(resource) as IResolvedTextFileEditorModel; - disposables.add(model); model.textEditorModel.setValue('Super Good'); assert.strictEqual(snapshotToString(model.createSnapshot()!), 'Super Good'); @@ -143,7 +141,6 @@ suite('Files - TextFileEditorTracker', () => { } const model = await accessor.textFileService.files.resolve(resource) as IResolvedTextFileEditorModel; - disposables.add(model); model.textEditorModel.setValue('Super Good'); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index f91de7e3ee8..104830cc852 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -100,7 +100,7 @@ export class InlineChatController implements IEditorContribution { private _messages = this._store.add(new Emitter()); - private readonly _sessionStore: DisposableStore = this._store.add(new DisposableStore()); + private readonly _sessionStore: DisposableStore = new DisposableStore(); private readonly _stashedSession: MutableDisposable = this._store.add(new MutableDisposable()); private _activeSession?: Session; private _strategy?: EditModeStrategy; @@ -146,7 +146,6 @@ export class InlineChatController implements IEditorContribution { } dispose(): void { - this._strategy?.dispose(); this._stashedSession.clear(); this.finishExistingSession(); this._store.dispose(); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts index fcbb998dc79..434aea1f4d2 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts @@ -385,8 +385,6 @@ export interface IInlineChatSessionService { // recordings(): readonly Recording[]; - - dispose(): void; } type SessionData = { diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts index 8619088ce96..bf16c0aec05 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts @@ -230,7 +230,7 @@ export class InlineChatWidget { })); const uri = URI.from({ scheme: 'vscode', authority: 'inline-chat', path: `/inline-chat/model${InlineChatWidget._modelPool++}.txt` }); - this._inputModel = this._store.add(this._modelService.getModel(uri) ?? this._modelService.createModel('', null, uri)); + this._inputModel = this._modelService.getModel(uri) ?? this._modelService.createModel('', null, uri); this._inputEditor.setModel(this._inputModel); // --- context keys @@ -359,13 +359,13 @@ export class InlineChatWidget { this._store.add(feedbackToolbar); // preview editors - this._previewDiffEditor = this._store.add(new IdleValue(() => this._store.add(_instantiationService.createInstance(EmbeddedDiffEditorWidget2, this._elements.previewDiff, { + this._previewDiffEditor = new IdleValue(() => this._store.add(_instantiationService.createInstance(EmbeddedDiffEditorWidget2, this._elements.previewDiff, { ..._previewEditorEditorOptions, onlyShowAccessibleDiffViewer: this._accessibilityService.isScreenReaderOptimized(), - }, { modifiedEditor: codeEditorWidgetOptions, originalEditor: codeEditorWidgetOptions }, parentEditor)))); + }, { modifiedEditor: codeEditorWidgetOptions, originalEditor: codeEditorWidgetOptions }, parentEditor))); this._previewCreateTitle = this._store.add(_instantiationService.createInstance(ResourceLabel, this._elements.previewCreateTitle, { supportIcons: true })); - this._previewCreateEditor = this._store.add(new IdleValue(() => this._store.add(_instantiationService.createInstance(EmbeddedCodeEditorWidget, this._elements.previewCreate, _previewEditorEditorOptions, codeEditorWidgetOptions, parentEditor)))); + this._previewCreateEditor = new IdleValue(() => this._store.add(_instantiationService.createInstance(EmbeddedCodeEditorWidget, this._elements.previewCreate, _previewEditorEditorOptions, codeEditorWidgetOptions, parentEditor))); this._elements.message.tabIndex = 0; this._elements.message.ariaLabel = this._accessibleViewService.getOpenAriaHint(AccessibilityVerbositySettingId.InlineChat); diff --git a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts index 7a9beea7519..ab5e4b27e1b 100644 --- a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts +++ b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts @@ -24,6 +24,7 @@ import { IEditorProgressService, IProgressRunner } from 'vs/platform/progress/co import { mock } from 'vs/base/test/common/mock'; import { Emitter, Event } from 'vs/base/common/event'; import { equals } from 'vs/base/common/arrays'; +import { timeout } from 'vs/base/common/async'; import { IChatAccessibilityService } from 'vs/workbench/contrib/chat/browser/chat'; import { IChatResponseViewModel } from 'vs/workbench/contrib/chat/common/chatViewModel'; import { IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; @@ -113,11 +114,11 @@ suite('InteractiveChatController', function () { }] ); - instaService = store.add(workbenchInstantiationService(undefined, store).createChild(serviceCollection)); - inlineChatSessionService = store.add(instaService.get(IInlineChatSessionService)); + instaService = workbenchInstantiationService(undefined, store).createChild(serviceCollection); + inlineChatSessionService = instaService.get(IInlineChatSessionService); - model = store.add(instaService.get(IModelService).createModel('Hello\nWorld\nHello Again\nHello World\n', null)); - editor = store.add(instantiateTestCodeEditor(instaService, model)); + model = instaService.get(IModelService).createModel('Hello\nWorld\nHello Again\nHello World\n', null); + editor = instantiateTestCodeEditor(instaService, model); store.add(inlineChatService.addProvider({ debugName: 'Unit Test', @@ -141,6 +142,8 @@ suite('InteractiveChatController', function () { }); teardown(function () { + editor.dispose(); + model.dispose(); store.clear(); ctrl?.dispose(); }); @@ -292,8 +295,19 @@ suite('InteractiveChatController', function () { wholeRange: new Range(3, 1, 3, 3) }; }, - provideResponse(session, request) { - return new Promise(() => { }); + async provideResponse(session, request) { + + // SLOW response + await timeout(50000); + + return { + type: InlineChatResponseType.EditorEdit, + id: Math.random(), + edits: [{ + range: new Range(1, 1, 1, 1), // EDIT happens outside of whole range + text: `${request.prompt}\n${request.prompt}` + }] + }; } }); store.add(d); diff --git a/src/vs/workbench/contrib/markers/test/browser/markersModel.test.ts b/src/vs/workbench/contrib/markers/test/browser/markersModel.test.ts index b8334c948d6..9cb2c9dc650 100644 --- a/src/vs/workbench/contrib/markers/test/browser/markersModel.test.ts +++ b/src/vs/workbench/contrib/markers/test/browser/markersModel.test.ts @@ -8,7 +8,6 @@ import { URI } from 'vs/base/common/uri'; import { IMarker, MarkerSeverity, IRelatedInformation } from 'vs/platform/markers/common/markers'; import { MarkersModel, Marker, ResourceMarkers, RelatedInformation } from 'vs/workbench/contrib/markers/browser/markersModel'; import { groupBy } from 'vs/base/common/collections'; -import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; class TestMarkersModel extends MarkersModel { @@ -28,8 +27,6 @@ class TestMarkersModel extends MarkersModel { suite('MarkersModel Test', () => { - ensureNoDisposablesAreLeakedInTestSuite(); - test('marker ids are unique', function () { const marker1 = anErrorWithRange(3); const marker2 = anErrorWithRange(3); diff --git a/src/vs/workbench/services/editor/browser/codeEditorService.ts b/src/vs/workbench/services/editor/browser/codeEditorService.ts index 9930f85f58c..657f203312c 100644 --- a/src/vs/workbench/services/editor/browser/codeEditorService.ts +++ b/src/vs/workbench/services/editor/browser/codeEditorService.ts @@ -25,8 +25,8 @@ export class CodeEditorService extends AbstractCodeEditorService { ) { super(themeService); - this._register(this.registerCodeEditorOpenHandler(this.doOpenCodeEditor.bind(this))); - this._register(this.registerCodeEditorOpenHandler(this.doOpenCodeEditorFromDiff.bind(this))); + this.registerCodeEditorOpenHandler(this.doOpenCodeEditor.bind(this)); + this.registerCodeEditorOpenHandler(this.doOpenCodeEditorFromDiff.bind(this)); } getActiveCodeEditor(): ICodeEditor | null { diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index f997b3ecfbd..e241e493f65 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -83,9 +83,9 @@ export class EditorService extends Disposable implements EditorServiceImpl { // Editor & group changes this.editorGroupService.whenReady.then(() => this.onEditorGroupsReady()); - this._register(this.editorGroupService.onDidChangeActiveGroup(group => this.handleActiveEditorChange(group))); - this._register(this.editorGroupService.onDidAddGroup(group => this.registerGroupListeners(group as IEditorGroupView))); - this._register(this.editorsObserver.onDidMostRecentlyActiveEditorsChange(() => this._onDidMostRecentlyActiveEditorsChange.fire())); + this.editorGroupService.onDidChangeActiveGroup(group => this.handleActiveEditorChange(group)); + this.editorGroupService.onDidAddGroup(group => this.registerGroupListeners(group as IEditorGroupView)); + this.editorsObserver.onDidMostRecentlyActiveEditorsChange(() => this._onDidMostRecentlyActiveEditorsChange.fire()); // Out of workspace file watchers this._register(this.onDidVisibleEditorsChange(() => this.handleVisibleEditorsChange())); diff --git a/src/vs/workbench/services/textfile/browser/browserTextFileService.ts b/src/vs/workbench/services/textfile/browser/browserTextFileService.ts index 188ca299d5f..78957c01afd 100644 --- a/src/vs/workbench/services/textfile/browser/browserTextFileService.ts +++ b/src/vs/workbench/services/textfile/browser/browserTextFileService.ts @@ -54,7 +54,7 @@ export class BrowserTextFileService extends AbstractTextFileService { private registerListeners(): void { // Lifecycle - this._register(this.lifecycleService.onBeforeShutdown(event => event.veto(this.onBeforeShutdown(), 'veto.textFiles'))); + this.lifecycleService.onBeforeShutdown(event => event.veto(this.onBeforeShutdown(), 'veto.textFiles')); } private onBeforeShutdown(): boolean { diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index f80681a7058..45015baa458 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -89,7 +89,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex private provideDecorations(): void { // Text file model decorations - const provider = new class extends Disposable implements IDecorationsProvider { + this.decorationsService.registerDecorationsProvider(new class extends Disposable implements IDecorationsProvider { readonly label = localize('textFileModelDecorations', "Text File Model Decorations"); @@ -160,10 +160,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex return undefined; } - }(this.files); - - this._register(provider); - this._register(this.decorationsService.registerDecorationsProvider(provider)); + }(this.files)); } //#endregin diff --git a/src/vs/workbench/services/textfile/common/textFileSaveParticipant.ts b/src/vs/workbench/services/textfile/common/textFileSaveParticipant.ts index 12affdd638f..ab3cb0a0e8a 100644 --- a/src/vs/workbench/services/textfile/common/textFileSaveParticipant.ts +++ b/src/vs/workbench/services/textfile/common/textFileSaveParticipant.ts @@ -60,14 +60,11 @@ export class TextFileSaveParticipant extends Disposable { model.textEditorModel?.pushStackElement(); }, () => { // user cancel - cts.cancel(); - }).finally(() => { - cts.dispose(); + cts.dispose(true); }); } override dispose(): void { this.saveParticipants.splice(0, this.saveParticipants.length); - super.dispose(); } } diff --git a/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts b/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts index 0dcc1e614ee..7595d417a5f 100644 --- a/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts +++ b/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts @@ -638,8 +638,8 @@ suite('Files - TextFileEditorModel', () => { }); test('save() and isDirty() - proper with check for mtimes', async function () { - const input1 = disposables.add(createFileEditorInput(instantiationService, toResource.call(this, '/path/index_async2.txt'))); - const input2 = disposables.add(createFileEditorInput(instantiationService, toResource.call(this, '/path/index_async.txt'))); + const input1 = createFileEditorInput(instantiationService, toResource.call(this, '/path/index_async2.txt')); + const input2 = createFileEditorInput(instantiationService, toResource.call(this, '/path/index_async.txt')); const model1 = await input1.resolve() as TextFileEditorModel; const model2 = await input2.resolve() as TextFileEditorModel; diff --git a/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopySaveParticipant.ts b/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopySaveParticipant.ts index f66950e4281..18be856eb47 100644 --- a/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopySaveParticipant.ts +++ b/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopySaveParticipant.ts @@ -69,6 +69,5 @@ export class StoredFileWorkingCopySaveParticipant extends Disposable { override dispose(): void { this.saveParticipants.splice(0, this.saveParticipants.length); - super.dispose(); } } diff --git a/src/vs/workbench/services/workingCopy/common/workingCopyFileOperationParticipant.ts b/src/vs/workbench/services/workingCopy/common/workingCopyFileOperationParticipant.ts index 3d0f521c340..e75246b8b83 100644 --- a/src/vs/workbench/services/workingCopy/common/workingCopyFileOperationParticipant.ts +++ b/src/vs/workbench/services/workingCopy/common/workingCopyFileOperationParticipant.ts @@ -46,6 +46,5 @@ export class WorkingCopyFileOperationParticipant extends Disposable { override dispose(): void { this.participants.clear(); - super.dispose(); } } diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index c566bc46401..0fffe75c46f 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -283,16 +283,15 @@ export function workbenchInstantiationService( instantiationService.stub(IUndoRedoService, instantiationService.createInstance(UndoRedoService)); const themeService = new TestThemeService(); instantiationService.stub(IThemeService, themeService); - instantiationService.stub(ILanguageConfigurationService, disposables.add(new TestLanguageConfigurationService())); + instantiationService.stub(ILanguageConfigurationService, new TestLanguageConfigurationService()); instantiationService.stub(IModelService, disposables.add(instantiationService.createInstance(ModelService))); const fileService = overrides?.fileService ? overrides.fileService(instantiationService) : new TestFileService(); instantiationService.stub(IFileService, fileService); const uriIdentityService = new UriIdentityService(fileService); - disposables.add(uriIdentityService); instantiationService.stub(IFilesConfigurationService, disposables.add(new TestFilesConfigurationService(contextKeyService, configService, workspaceContextService, environmentService, uriIdentityService, fileService))); instantiationService.stub(IUriIdentityService, uriIdentityService); - const userDataProfilesService = instantiationService.stub(IUserDataProfilesService, disposables.add(new UserDataProfilesService(environmentService, fileService, uriIdentityService, new NullLogService()))); - instantiationService.stub(IUserDataProfileService, disposables.add(new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService))); + const userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, fileService, uriIdentityService, new NullLogService())); + instantiationService.stub(IUserDataProfileService, new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService)); instantiationService.stub(IWorkingCopyBackupService, overrides?.workingCopyBackupService ? overrides?.workingCopyBackupService(instantiationService) : new TestWorkingCopyBackupService()); instantiationService.stub(ITelemetryService, NullTelemetryService); instantiationService.stub(INotificationService, new TestNotificationService()); @@ -306,7 +305,7 @@ export function workbenchInstantiationService( instantiationService.stub(ITextFileService, overrides?.textFileService ? overrides.textFileService(instantiationService) : disposables.add(instantiationService.createInstance(TestTextFileService))); instantiationService.stub(IHostService, instantiationService.createInstance(TestHostService)); instantiationService.stub(ITextModelService, disposables.add(instantiationService.createInstance(TextModelResolverService))); - instantiationService.stub(ILoggerService, disposables.add(new TestLoggerService(TestEnvironmentService.logsHome))); + instantiationService.stub(ILoggerService, new TestLoggerService(TestEnvironmentService.logsHome)); instantiationService.stub(ILogService, new NullLogService()); const editorGroupService = new TestEditorGroupsService([new TestEditorGroupView(0)]); instantiationService.stub(IEditorGroupsService, editorGroupService); @@ -315,10 +314,10 @@ export function workbenchInstantiationService( instantiationService.stub(IEditorService, editorService); instantiationService.stub(IWorkingCopyEditorService, disposables.add(instantiationService.createInstance(WorkingCopyEditorService))); instantiationService.stub(IEditorResolverService, disposables.add(instantiationService.createInstance(EditorResolverService))); - const textEditorService = overrides?.textEditorService ? overrides.textEditorService(instantiationService) : disposables.add(instantiationService.createInstance(TextEditorService)); + const textEditorService = overrides?.textEditorService ? overrides.textEditorService(instantiationService) : instantiationService.createInstance(TextEditorService); instantiationService.stub(ITextEditorService, textEditorService); instantiationService.stub(ICodeEditorService, disposables.add(new CodeEditorService(editorService, themeService, configService))); - instantiationService.stub(IPaneCompositePartService, disposables.add(new TestPaneCompositeService())); + instantiationService.stub(IPaneCompositePartService, new TestPaneCompositeService()); instantiationService.stub(IListService, new TestListService()); const hoverService = instantiationService.stub(IHoverService, instantiationService.createInstance(TestHoverService)); instantiationService.stub(IQuickInputService, disposables.add(new QuickInputService(configService, instantiationService, keybindingService, contextKeyService, themeService, layoutService, hoverService))); From e40344eba7a43267a2c0e70cbbde29d955d42771 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 18 Aug 2023 09:00:21 -0700 Subject: [PATCH 117/223] ports: rename 'local address' -> 'forwarded address' (#190755) Caused some confusion in https://github.com/microsoft/vscode/issues/189678#issuecomment-1684091172, and it hasn't really been a "local address" in all cases for a while, e.g. on vscode.dev. --- src/vs/workbench/contrib/remote/browser/tunnelView.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/remote/browser/tunnelView.ts b/src/vs/workbench/contrib/remote/browser/tunnelView.ts index 037e25ff458..8f94225be44 100644 --- a/src/vs/workbench/contrib/remote/browser/tunnelView.ts +++ b/src/vs/workbench/contrib/remote/browser/tunnelView.ts @@ -218,8 +218,8 @@ class PortColumn implements ITableColumn { } class LocalAddressColumn implements ITableColumn { - readonly label: string = nls.localize('tunnel.addressColumn.label', "Local Address"); - readonly tooltip: string = nls.localize('tunnel.addressColumn.tooltip', "The address that the forwarded port is available at locally."); + readonly label: string = nls.localize('tunnel.addressColumn.label', "Forwarded Address"); + readonly tooltip: string = nls.localize('tunnel.addressColumn.tooltip', "The address that the forwarded port is available at."); readonly weight: number = 1; readonly templateId: string = 'actionbar'; project(row: ITunnelItem): ActionBarCell { From aa254f84f90816dacdb1d1d2ec48ede7ffb88c63 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 18 Aug 2023 09:15:32 -0700 Subject: [PATCH 118/223] handle escape while in toolbar --- .../accessibility/browser/accessibleView.ts | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index 71fcfb7a5a3..953d7284097 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -38,6 +38,7 @@ import { IAction } from 'vs/base/common/actions'; import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { Codicon } from 'vs/base/common/codicons'; import { ThemeIcon } from 'vs/base/common/themables'; +import { addDisposableListener } from 'vs/base/browser/dom'; const enum DIMENSIONS { MAX_WIDTH = 600 @@ -346,15 +347,18 @@ class AccessibleView extends Disposable { }); this._updateToolbar(provider.actions, provider.options.type); + const handleEscape = (e: KeyboardEvent | IKeyboardEvent): void => { + e.stopPropagation(); + this._contextViewService.hideContextView(); + this._updateContextKeys(provider, false); + // HACK: Delay to allow the context view to hide #186514 + setTimeout(() => provider.onClose(), 100); + }; const disposableStore = new DisposableStore(); disposableStore.add(this._editorWidget.onKeyUp((e) => provider.onKeyDown?.(e))); disposableStore.add(this._editorWidget.onKeyDown((e) => { if (e.keyCode === KeyCode.Escape) { - e.stopPropagation(); - this._contextViewService.hideContextView(); - this._updateContextKeys(provider, false); - // HACK: Delay to allow the context view to hide #186514 - setTimeout(() => provider.onClose(), 100); + handleEscape(e); } else if (e.keyCode === KeyCode.KeyH && provider.options.readMoreUrl) { const url: string = provider.options.readMoreUrl!; alert(AccessibilityHelpNLS.openingDocs); @@ -363,6 +367,11 @@ class AccessibleView extends Disposable { e.stopPropagation(); } })); + disposableStore.add(addDisposableListener(this._toolbar.getElement(), 'keydown', (e: KeyboardEvent) => { + if (e.key === 'Escape') { + handleEscape(e); + } + })); disposableStore.add(this._editorWidget.onDidBlurEditorWidget(() => { if (document.activeElement !== this._toolbar.getElement()) { this._contextViewService.hideContextView(); From c6ac21f37b6521c7bf27b7782005182a30781910 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 18 Aug 2023 09:16:28 -0700 Subject: [PATCH 119/223] appropriately rename property --- .../workbench/contrib/accessibility/browser/accessibleView.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index 953d7284097..71e366bfcce 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -54,7 +54,7 @@ export interface IAccessibleContentProvider { actions?: IAction[]; provideContent(): string; onClose(): void; - onKeyDown?(e: IKeyboardEvent): void; + onKeyUp?(e: IKeyboardEvent): void; previous?(): void; next?(): void; /** @@ -355,7 +355,7 @@ class AccessibleView extends Disposable { setTimeout(() => provider.onClose(), 100); }; const disposableStore = new DisposableStore(); - disposableStore.add(this._editorWidget.onKeyUp((e) => provider.onKeyDown?.(e))); + disposableStore.add(this._editorWidget.onKeyUp((e) => provider.onKeyUp?.(e))); disposableStore.add(this._editorWidget.onKeyDown((e) => { if (e.keyCode === KeyCode.Escape) { handleEscape(e); From 5e6c5a167a7ac02c4818d6bf6442160f73a968e1 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 18 Aug 2023 09:32:35 -0700 Subject: [PATCH 120/223] fix #190716 --- .../browser/accessibilityContributions.ts | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts index ccfc48a979b..21ef08a3bd6 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts @@ -301,6 +301,13 @@ export class InlineCompletionsAccessibleViewContribution extends Disposable { if (!ghostText) { return false; } + const accept = () => { + model.accept(editor).then(() => { + alert('Accepted'); + model.stop(); + editor.focus(); + }); + }; this._options.language = editor.getModel()?.getLanguageId() ?? undefined; accessibleViewService.show({ verbositySettingKey: AccessibilityVerbositySettingId.InlineCompletions, @@ -315,17 +322,18 @@ export class InlineCompletionsAccessibleViewContribution extends Disposable { previous() { model.previous().then(() => show()); }, + onKeyDown: (e) => { + if (e.ctrlKey && e.browserEvent.key === '/') { + accept(); + } + }, actions: [ { id: 'inlineCompletions.accept', - label: localize('inlineCompletions.accept', "Accept Completion"), - tooltip: localize('inlineCompletions.accept', "Accept Completion"), + label: localize('inlineCompletions.accept', "Accept Completion (Ctrl+/)"), + tooltip: localize('inlineCompletions.accept', "Accept Completion (Ctrl+/)"), run: () => { - model.accept(editor).then(() => { - alert('Accepted'); - model.stop(); - editor.focus(); - }); + accept(); }, class: ThemeIcon.asClassName(Codicon.check), enabled: true From bd60cc529ca82647a759c3c90a63bd1f8495c7b3 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Fri, 18 Aug 2023 10:43:43 -0700 Subject: [PATCH 121/223] allow copying cell output images from context menu --- extensions/ipynb/package.json | 254 +++++++++--------- extensions/ipynb/package.nls.json | 1 + extensions/notebook-renderers/src/index.ts | 1 + .../browser/controller/cellOutputActions.ts | 62 +++-- .../view/renderers/backLayerWebView.ts | 4 +- 5 files changed, 182 insertions(+), 140 deletions(-) diff --git a/extensions/ipynb/package.json b/extensions/ipynb/package.json index ce667a5d277..f9251ae54e8 100644 --- a/extensions/ipynb/package.json +++ b/extensions/ipynb/package.json @@ -1,124 +1,134 @@ { - "name": "ipynb", - "displayName": "%displayName%", - "description": "%description%", - "publisher": "vscode", - "version": "1.0.0", - "license": "MIT", - "engines": { - "vscode": "^1.57.0" - }, - "enabledApiProposals": [ - "documentPaste", - "diffContentOptions", - "dropMetadata" - ], - "activationEvents": [ - "onNotebook:jupyter-notebook", - "onNotebookSerializer:interactive" - ], - "extensionKind": [ - "workspace", - "ui" - ], - "main": "./out/ipynbMain.js", - "browser": "./dist/browser/ipynbMain.js", - "capabilities": { - "virtualWorkspaces": true, - "untrustedWorkspaces": { - "supported": true - } - }, - "contributes": { - "configuration": [ - { - "properties": { - "ipynb.pasteImagesAsAttachments.enabled": { - "type": "boolean", - "scope": "resource", - "markdownDescription": "%ipynb.pasteImagesAsAttachments.enabled%", - "default": true - } - } - } - ], - "commands": [ - { - "command": "ipynb.newUntitledIpynb", - "title": "%newUntitledIpynb.title%", - "shortTitle": "%newUntitledIpynb.shortTitle%", - "category": "Create" - }, - { - "command": "ipynb.openIpynbInNotebookEditor", - "title": "%openIpynbInNotebookEditor.title%" - }, - { - "command": "ipynb.cleanInvalidImageAttachment", - "title": "%cleanInvalidImageAttachment.title%" - } - ], - "notebooks": [ - { - "type": "jupyter-notebook", - "displayName": "Jupyter Notebook", - "selector": [ - { - "filenamePattern": "*.ipynb" - } - ], - "priority": "default" - } - ], - "notebookRenderer": [ - { - "id": "vscode.markdown-it-cell-attachment-renderer", - "displayName": "%markdownAttachmentRenderer.displayName%", - "entrypoint": { - "extends": "vscode.markdown-it-renderer", - "path": "./notebook-out/cellAttachmentRenderer.js" - } - } - ], - "menus": { - "file/newFile": [ - { - "command": "ipynb.newUntitledIpynb", - "group": "notebook" - } - ], - "commandPalette": [ - { - "command": "ipynb.newUntitledIpynb" - }, - { - "command": "ipynb.openIpynbInNotebookEditor", - "when": "false" - }, - { - "command": "ipynb.cleanInvalidImageAttachment", - "when": "false" - } - ] - } - }, - "scripts": { - "compile": "npx gulp compile-extension:ipynb && npm run build-notebook", - "watch": "npx gulp watch-extension:ipynb", - "build-notebook": "node ./esbuild" - }, - "dependencies": { - "@enonic/fnv-plus": "^1.3.0", - "detect-indent": "^6.0.0", - "uuid": "^8.3.2" - }, - "devDependencies": { - "@jupyterlab/nbformat": "^3.2.9", - "@types/markdown-it": "12.2.3", - "@types/uuid": "^8.3.1" - }, - "repository": { - "type": "git", - "url": "https://github.com/microsoft/vscode.git" - } + "name": "ipynb", + "displayName": "%displayName%", + "description": "%description%", + "publisher": "vscode", + "version": "1.0.0", + "license": "MIT", + "engines": { + "vscode": "^1.57.0" + }, + "enabledApiProposals": [ + "documentPaste", + "diffContentOptions", + "dropMetadata" + ], + "activationEvents": [ + "onNotebook:jupyter-notebook", + "onNotebookSerializer:interactive" + ], + "extensionKind": [ + "workspace", + "ui" + ], + "main": "./out/ipynbMain.js", + "browser": "./dist/browser/ipynbMain.js", + "capabilities": { + "virtualWorkspaces": true, + "untrustedWorkspaces": { + "supported": true + } + }, + "contributes": { + "configuration": [ + { + "properties": { + "ipynb.pasteImagesAsAttachments.enabled": { + "type": "boolean", + "scope": "resource", + "markdownDescription": "%ipynb.pasteImagesAsAttachments.enabled%", + "default": true + } + } + } + ], + "commands": [ + { + "command": "ipynb.newUntitledIpynb", + "title": "%newUntitledIpynb.title%", + "shortTitle": "%newUntitledIpynb.shortTitle%", + "category": "Create" + }, + { + "command": "ipynb.openIpynbInNotebookEditor", + "title": "%openIpynbInNotebookEditor.title%" + }, + { + "command": "ipynb.cleanInvalidImageAttachment", + "title": "%cleanInvalidImageAttachment.title%" + }, + { + "command": "notebook.cellOutput.copyToClipboard", + "title": "%copyOutputToClipboard.title%" + } + ], + "notebooks": [ + { + "type": "jupyter-notebook", + "displayName": "Jupyter Notebook", + "selector": [ + { + "filenamePattern": "*.ipynb" + } + ], + "priority": "default" + } + ], + "notebookRenderer": [ + { + "id": "vscode.markdown-it-cell-attachment-renderer", + "displayName": "%markdownAttachmentRenderer.displayName%", + "entrypoint": { + "extends": "vscode.markdown-it-renderer", + "path": "./notebook-out/cellAttachmentRenderer.js" + } + } + ], + "menus": { + "file/newFile": [ + { + "command": "ipynb.newUntitledIpynb", + "group": "notebook" + } + ], + "commandPalette": [ + { + "command": "ipynb.newUntitledIpynb" + }, + { + "command": "ipynb.openIpynbInNotebookEditor", + "when": "false" + }, + { + "command": "ipynb.cleanInvalidImageAttachment", + "when": "false" + } + ], + "webview/context": [ + { + "command": "notebook.cellOutput.copyToClipboard", + "when": "webviewId == 'notebook.output' && webviewSection == 'image'" + } + ] + } + }, + "scripts": { + "compile": "npx gulp compile-extension:ipynb && npm run build-notebook", + "watch": "npx gulp watch-extension:ipynb", + "build-notebook": "node ./esbuild" + }, + "dependencies": { + "@enonic/fnv-plus": "^1.3.0", + "detect-indent": "^6.0.0", + "uuid": "^8.3.2" + }, + "devDependencies": { + "@jupyterlab/nbformat": "^3.2.9", + "@types/markdown-it": "12.2.3", + "@types/uuid": "^8.3.1" + }, + "repository": { + "type": "git", + "url": "https://github.com/microsoft/vscode.git" + } } diff --git a/extensions/ipynb/package.nls.json b/extensions/ipynb/package.nls.json index bd8e0ab1da0..45aa2aa03e8 100644 --- a/extensions/ipynb/package.nls.json +++ b/extensions/ipynb/package.nls.json @@ -6,6 +6,7 @@ "newUntitledIpynb.shortTitle": "Jupyter Notebook", "openIpynbInNotebookEditor.title": "Open IPYNB File In Notebook Editor", "cleanInvalidImageAttachment.title": "Clean Invalid Image Attachment Reference", + "copyOutputToClipboard.title": "Copy Output to Clipboard", "markdownAttachmentRenderer.displayName": { "message": "Markdown-It ipynb Cell Attachment renderer", "comment": [ diff --git a/extensions/notebook-renderers/src/index.ts b/extensions/notebook-renderers/src/index.ts index 090e9719420..c5b4a631afc 100644 --- a/extensions/notebook-renderers/src/index.ts +++ b/extensions/notebook-renderers/src/index.ts @@ -37,6 +37,7 @@ function renderImage(outputInfo: OutputItem, element: HTMLElement): IDisposable if (alt) { image.alt = alt; } + image.setAttribute('data-vscode-context', JSON.stringify({ webviewSection: 'image', outputId: outputInfo.id, 'preventDefaultContextMenuItems': true })); const display = document.createElement('div'); display.classList.add('display'); display.appendChild(image); diff --git a/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts index d71503c9cb6..f6f859db2a4 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts @@ -5,39 +5,51 @@ import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { localize } from 'vs/nls'; -import { MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; +import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { INotebookOutputActionContext, NotebookAction } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; +import { INotebookOutputActionContext, NOTEBOOK_ACTIONS_CATEGORY } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; import { NOTEBOOK_CELL_HAS_OUTPUTS } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; import * as icons from 'vs/workbench/contrib/notebook/browser/notebookIcons'; import { ILogService } from 'vs/platform/log/common/log'; import { copyCellOutput } from 'vs/workbench/contrib/notebook/browser/contrib/clipboard/cellOutputClipboard'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { ICellViewModel, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { ICellOutputViewModel, ICellViewModel, INotebookEditor, getNotebookEditorFromEditorPane } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; export const COPY_OUTPUT_COMMAND_ID = 'notebook.cellOutput.copyToClipboard'; -registerAction2(class CopyCellOutputAction extends NotebookAction { +registerAction2(class CopyCellOutputAction extends Action2 { constructor() { - super( - { - id: COPY_OUTPUT_COMMAND_ID, - title: localize('notebookActions.copyOutput', "Copy Output to Clipboard"), - menu: { - id: MenuId.NotebookOutputToolbar, - when: NOTEBOOK_CELL_HAS_OUTPUTS - }, - icon: icons.copyIcon, - }); + super({ + id: COPY_OUTPUT_COMMAND_ID, + title: localize('notebookActions.copyOutput', "Copy Output to Clipboard"), + menu: { + id: MenuId.NotebookOutputToolbar, + when: NOTEBOOK_CELL_HAS_OUTPUTS + }, + category: NOTEBOOK_ACTIONS_CATEGORY, + icon: icons.copyIcon, + }); } - async runWithContext(accessor: ServicesAccessor, context: INotebookOutputActionContext): Promise { - const outputViewModel = context.outputViewModel; + async run(accessor: ServicesAccessor, outputContext: INotebookOutputActionContext | { outputViewModel: ICellOutputViewModel }): Promise { + const editorService = accessor.get(IEditorService); + let outputViewModel: ICellOutputViewModel | undefined; + + if ('outputId' in outputContext && typeof outputContext.outputId === 'string') { + outputViewModel = getOutputViewModelFromId(outputContext.outputId, editorService); + } else { + outputViewModel = outputContext.outputViewModel; + } + + if (!outputViewModel) { + return; + } const mimeType = outputViewModel.pickedMimeType?.mimeType; if (mimeType?.startsWith('image/')) { - const editorService = accessor.get(IEditorService); const editor = editorService.activeEditorPane?.getControl() as INotebookEditor; await editor.focusNotebookCell(outputViewModel.cellViewModel as ICellViewModel, 'output', { skipReveal: true, outputId: outputViewModel.model.outputId }); editor.copyOutputImage(outputViewModel); @@ -48,4 +60,20 @@ registerAction2(class CopyCellOutputAction extends NotebookAction { copyCellOutput(mimeType, outputViewModel, clipboardService, logService); } } + }); + +function getOutputViewModelFromId(outputId: string, editorService: IEditorService): ICellOutputViewModel | undefined { + const notebookViewModel = getNotebookEditorFromEditorPane(editorService.activeEditorPane)?.getViewModel(); + if (notebookViewModel) { + const codeCells = notebookViewModel.viewCells.filter(cell => cell.cellKind === CellKind.Code) as CodeCellViewModel[]; + for (const cell of codeCells) { + const output = cell.outputsViewModels.find(output => output.model.outputId === outputId); + if (output) { + return output; + } + } + } + + return undefined; +} diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index 1b67d83daf6..b2359655e6d 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -1074,10 +1074,12 @@ export class BackLayerWebView extends Themable { allowScripts: true, localResourceRoots: this.localResourceRootsCache, }, - extension: undefined + extension: undefined, + providedViewType: 'notebook.output' }); webview.setHtml(content); + webview.setContextKeyService(this.contextKeyService); return webview; } From f1b9a60d62b66fb02db467d294250a150a0720d1 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Fri, 18 Aug 2023 10:55:06 -0700 Subject: [PATCH 122/223] disable default context menu items for the rest of the output webview --- .../contrib/notebook/browser/view/renderers/webviewPreloads.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts index ca2ad71955c..92bf95a2111 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -2561,6 +2561,7 @@ async function webviewPreloads(ctx: PreloadContext) { ) { this.element = document.createElement('div'); this.element.classList.add('output_container'); + this.element.setAttribute('data-vscode-context', JSON.stringify({ 'preventDefaultContextMenuItems': true })); this.element.style.position = 'absolute'; this.element.style.overflow = 'hidden'; } From 967f27ba3a0758f64ce26ff14cd47acf446ee009 Mon Sep 17 00:00:00 2001 From: hsfzxjy Date: Sat, 19 Aug 2023 02:03:33 +0800 Subject: [PATCH 123/223] Fix overflow of setting list (#190721) Don't set height for AbstractListSettingWidget Fixes #187467 --- .../workbench/contrib/preferences/browser/settingsWidgets.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts b/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts index 94dc42655cd..ad2a9b4a633 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts @@ -208,18 +208,13 @@ export abstract class AbstractListSettingWidget extend } const header = this.renderHeader(); - const ITEM_HEIGHT = 24; - let listHeight = ITEM_HEIGHT * this.model.items.length; if (header) { - listHeight += ITEM_HEIGHT; this.listElement.appendChild(header); } this.rowElements = this.model.items.map((item, i) => this.renderDataOrEditItem(item, i, focused)); this.rowElements.forEach(rowElement => this.listElement.appendChild(rowElement)); - - this.listElement.style.height = listHeight + 'px'; } protected createBasicSelectBox(value: IObjectEnumData): SelectBox { From 7ebfc44283ae07e41147dffa65eb910739494f7e Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Fri, 18 Aug 2023 11:04:43 -0700 Subject: [PATCH 124/223] removed formatting changes --- extensions/ipynb/package.json | 244 +++++++++++++++++----------------- 1 file changed, 122 insertions(+), 122 deletions(-) diff --git a/extensions/ipynb/package.json b/extensions/ipynb/package.json index f9251ae54e8..f5e25ac3695 100644 --- a/extensions/ipynb/package.json +++ b/extensions/ipynb/package.json @@ -1,134 +1,134 @@ { - "name": "ipynb", - "displayName": "%displayName%", - "description": "%description%", - "publisher": "vscode", - "version": "1.0.0", - "license": "MIT", - "engines": { - "vscode": "^1.57.0" - }, - "enabledApiProposals": [ - "documentPaste", - "diffContentOptions", - "dropMetadata" - ], - "activationEvents": [ - "onNotebook:jupyter-notebook", - "onNotebookSerializer:interactive" - ], - "extensionKind": [ - "workspace", - "ui" - ], - "main": "./out/ipynbMain.js", - "browser": "./dist/browser/ipynbMain.js", - "capabilities": { - "virtualWorkspaces": true, - "untrustedWorkspaces": { - "supported": true - } - }, - "contributes": { - "configuration": [ - { - "properties": { - "ipynb.pasteImagesAsAttachments.enabled": { - "type": "boolean", - "scope": "resource", - "markdownDescription": "%ipynb.pasteImagesAsAttachments.enabled%", - "default": true - } - } - } - ], - "commands": [ - { - "command": "ipynb.newUntitledIpynb", - "title": "%newUntitledIpynb.title%", - "shortTitle": "%newUntitledIpynb.shortTitle%", - "category": "Create" - }, - { - "command": "ipynb.openIpynbInNotebookEditor", - "title": "%openIpynbInNotebookEditor.title%" - }, - { - "command": "ipynb.cleanInvalidImageAttachment", - "title": "%cleanInvalidImageAttachment.title%" - }, + "name": "ipynb", + "displayName": "%displayName%", + "description": "%description%", + "publisher": "vscode", + "version": "1.0.0", + "license": "MIT", + "engines": { + "vscode": "^1.57.0" + }, + "enabledApiProposals": [ + "documentPaste", + "diffContentOptions", + "dropMetadata" + ], + "activationEvents": [ + "onNotebook:jupyter-notebook", + "onNotebookSerializer:interactive" + ], + "extensionKind": [ + "workspace", + "ui" + ], + "main": "./out/ipynbMain.js", + "browser": "./dist/browser/ipynbMain.js", + "capabilities": { + "virtualWorkspaces": true, + "untrustedWorkspaces": { + "supported": true + } + }, + "contributes": { + "configuration": [ + { + "properties": { + "ipynb.pasteImagesAsAttachments.enabled": { + "type": "boolean", + "scope": "resource", + "markdownDescription": "%ipynb.pasteImagesAsAttachments.enabled%", + "default": true + } + } + } + ], + "commands": [ + { + "command": "ipynb.newUntitledIpynb", + "title": "%newUntitledIpynb.title%", + "shortTitle": "%newUntitledIpynb.shortTitle%", + "category": "Create" + }, + { + "command": "ipynb.openIpynbInNotebookEditor", + "title": "%openIpynbInNotebookEditor.title%" + }, + { + "command": "ipynb.cleanInvalidImageAttachment", + "title": "%cleanInvalidImageAttachment.title%" + }, { "command": "notebook.cellOutput.copyToClipboard", "title": "%copyOutputToClipboard.title%" } - ], - "notebooks": [ - { - "type": "jupyter-notebook", - "displayName": "Jupyter Notebook", - "selector": [ - { - "filenamePattern": "*.ipynb" - } - ], - "priority": "default" - } - ], - "notebookRenderer": [ - { - "id": "vscode.markdown-it-cell-attachment-renderer", - "displayName": "%markdownAttachmentRenderer.displayName%", - "entrypoint": { - "extends": "vscode.markdown-it-renderer", - "path": "./notebook-out/cellAttachmentRenderer.js" - } - } - ], - "menus": { - "file/newFile": [ - { - "command": "ipynb.newUntitledIpynb", - "group": "notebook" - } - ], - "commandPalette": [ - { - "command": "ipynb.newUntitledIpynb" - }, - { - "command": "ipynb.openIpynbInNotebookEditor", - "when": "false" - }, - { - "command": "ipynb.cleanInvalidImageAttachment", - "when": "false" - } - ], + ], + "notebooks": [ + { + "type": "jupyter-notebook", + "displayName": "Jupyter Notebook", + "selector": [ + { + "filenamePattern": "*.ipynb" + } + ], + "priority": "default" + } + ], + "notebookRenderer": [ + { + "id": "vscode.markdown-it-cell-attachment-renderer", + "displayName": "%markdownAttachmentRenderer.displayName%", + "entrypoint": { + "extends": "vscode.markdown-it-renderer", + "path": "./notebook-out/cellAttachmentRenderer.js" + } + } + ], + "menus": { + "file/newFile": [ + { + "command": "ipynb.newUntitledIpynb", + "group": "notebook" + } + ], + "commandPalette": [ + { + "command": "ipynb.newUntitledIpynb" + }, + { + "command": "ipynb.openIpynbInNotebookEditor", + "when": "false" + }, + { + "command": "ipynb.cleanInvalidImageAttachment", + "when": "false" + } + ], "webview/context": [ { "command": "notebook.cellOutput.copyToClipboard", "when": "webviewId == 'notebook.output' && webviewSection == 'image'" } ] - } - }, - "scripts": { - "compile": "npx gulp compile-extension:ipynb && npm run build-notebook", - "watch": "npx gulp watch-extension:ipynb", - "build-notebook": "node ./esbuild" - }, - "dependencies": { - "@enonic/fnv-plus": "^1.3.0", - "detect-indent": "^6.0.0", - "uuid": "^8.3.2" - }, - "devDependencies": { - "@jupyterlab/nbformat": "^3.2.9", - "@types/markdown-it": "12.2.3", - "@types/uuid": "^8.3.1" - }, - "repository": { - "type": "git", - "url": "https://github.com/microsoft/vscode.git" - } + } + }, + "scripts": { + "compile": "npx gulp compile-extension:ipynb && npm run build-notebook", + "watch": "npx gulp watch-extension:ipynb", + "build-notebook": "node ./esbuild" + }, + "dependencies": { + "@enonic/fnv-plus": "^1.3.0", + "detect-indent": "^6.0.0", + "uuid": "^8.3.2" + }, + "devDependencies": { + "@jupyterlab/nbformat": "^3.2.9", + "@types/markdown-it": "12.2.3", + "@types/uuid": "^8.3.1" + }, + "repository": { + "type": "git", + "url": "https://github.com/microsoft/vscode.git" + } } From c73043e10b5b083e081e427978dbba179668e233 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 18 Aug 2023 11:11:23 -0700 Subject: [PATCH 125/223] terminal: enable link detectors in detached terminals (#190699) Initially the direction was to make `TerminalLinkManager` more general and use it explicitly in detached terminals. But I ended up getting nudged in the direction of making terminalContribs work with detached terminals instead. All the link detection logic is in terminalContrib which is a layer that can't be imported from the main terminal code. And it also depends on the raw xterm instance, which we don't want to expose to callers, so I wouldn't want to have the caller manually do something like new `TerminalLinkManager(deatchedXterm.raw, ...)` So that made me think I should either make a new contrib system for detached terminals, or allow existing contribs to signal that they could run for detached terminals too. In the PR, I did the latter. ![](https://memes.peet.io/img/23-08-cce27de3-f2da-45cf-acee-6731480722ed.png) --- .../terminal/browser/detachedTerminal.ts | 101 ++++++++++++++++++ .../contrib/terminal/browser/terminal.ts | 29 ++++- .../terminal/browser/terminalExtensions.ts | 25 +++-- .../terminal/browser/terminalService.ts | 18 ++-- .../contrib/terminal/common/terminal.ts | 7 +- .../browser/terminal.links.contribution.ts | 30 +++--- .../links/browser/terminalLinkManager.ts | 16 +-- .../testing/browser/testingOutputPeek.ts | 36 ++++--- 8 files changed, 204 insertions(+), 58 deletions(-) create mode 100644 src/vs/workbench/contrib/terminal/browser/detachedTerminal.ts diff --git a/src/vs/workbench/contrib/terminal/browser/detachedTerminal.ts b/src/vs/workbench/contrib/terminal/browser/detachedTerminal.ts new file mode 100644 index 00000000000..9902a5483e5 --- /dev/null +++ b/src/vs/workbench/contrib/terminal/browser/detachedTerminal.ts @@ -0,0 +1,101 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Delayer } from 'vs/base/common/async'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { OperatingSystem } from 'vs/base/common/platform'; +import { MicrotaskDelay } from 'vs/base/common/symbols'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { TerminalCapabilityStore } from 'vs/platform/terminal/common/capabilities/terminalCapabilityStore'; +import { IMergedEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariable'; +import { ITerminalBackend } from 'vs/platform/terminal/common/terminal'; +import { IDetachedTerminalInstance, IDetachedXTermOptions, IDetachedXtermTerminal, ITerminalContribution, IXtermAttachToElementOptions } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { TerminalExtensionsRegistry } from 'vs/workbench/contrib/terminal/browser/terminalExtensions'; +import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; +import { XtermTerminal } from 'vs/workbench/contrib/terminal/browser/xterm/xtermTerminal'; +import { IEnvironmentVariableInfo } from 'vs/workbench/contrib/terminal/common/environmentVariable'; +import { ITerminalProcessInfo, ProcessState } from 'vs/workbench/contrib/terminal/common/terminal'; + +export class DeatachedTerminal extends Disposable implements IDetachedTerminalInstance { + private readonly _widgets = this._register(new TerminalWidgetManager()); + public readonly capabilities = new TerminalCapabilityStore(); + private readonly _contributions: Map = new Map(); + + public get xterm(): IDetachedXtermTerminal { + return this._xterm; + } + + constructor( + private readonly _xterm: XtermTerminal, + options: IDetachedXTermOptions, + @IInstantiationService instantiationService: IInstantiationService, + ) { + super(); + this._register(_xterm); + + // Initialize contributions + const contributionDescs = TerminalExtensionsRegistry.getTerminalContributions(); + for (const desc of contributionDescs) { + if (this._contributions.has(desc.id)) { + onUnexpectedError(new Error(`Cannot have two terminal contributions with the same id ${desc.id}`)); + continue; + } + if (desc.canRunInDetachedTerminals === false) { + continue; + } + + let contribution: ITerminalContribution; + try { + contribution = instantiationService.createInstance(desc.ctor, this, options.processInfo, this._widgets); + this._contributions.set(desc.id, contribution); + this._register(contribution); + } catch (err) { + onUnexpectedError(err); + } + } + + // xterm is already by the time DetachedTerminal is created, so trigger everything + // on the next microtask, allowing the caller to do any extra initialization + this._register(new Delayer(MicrotaskDelay)).trigger(() => { + for (const contr of this._contributions.values()) { + contr.xtermReady?.(this._xterm); + } + }); + } + + attachToElement(container: HTMLElement, options?: Partial | undefined): void { + const screenElement = this._xterm.attachToElement(container, options); + this._widgets.attachToElement(screenElement); + } +} + +/** + * Implements {@link ITerminalProcessInfo} for a detached terminal where most + * properties are stubbed. Properties are mutable and can be updated by + * the instantiator. + */ +export class DetachedProcessInfo implements ITerminalProcessInfo { + processState = ProcessState.Running; + ptyProcessReady = Promise.resolve(); + shellProcessId: number | undefined; + remoteAuthority: string | undefined; + os: OperatingSystem | undefined; + userHome: string | undefined; + initialCwd = ''; + environmentVariableInfo: IEnvironmentVariableInfo | undefined; + persistentProcessId: number | undefined; + shouldPersist = false; + hasWrittenData = false; + hasChildProcesses = false; + backend: ITerminalBackend | undefined; + capabilities = new TerminalCapabilityStore(); + shellIntegrationNonce = ''; + extEnvironmentVariableCollection: IMergedEnvironmentVariableCollection | undefined; + + constructor(initialValues: Partial) { + Object.assign(this, initialValues); + } +} diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 6d199b0cd13..61b92bd54d9 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -22,7 +22,7 @@ import { IEditableData } from 'vs/workbench/common/views'; import { ITerminalStatusList } from 'vs/workbench/contrib/terminal/browser/terminalStatusList'; import { ScrollPosition } from 'vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon'; import { XtermTerminal } from 'vs/workbench/contrib/terminal/browser/xterm/xtermTerminal'; -import { IRegisterContributedProfileArgs, IRemoteTerminalAttachTarget, IStartExtensionTerminalRequest, ITerminalConfigHelper, ITerminalFont, ITerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IRegisterContributedProfileArgs, IRemoteTerminalAttachTarget, IStartExtensionTerminalRequest, ITerminalConfigHelper, ITerminalFont, ITerminalProcessExtHostProxy, ITerminalProcessInfo } from 'vs/workbench/contrib/terminal/common/terminal'; import { EditorGroupColumn } from 'vs/workbench/services/editor/common/editorGroupColumn'; import { ISimpleSelectedSuggestion } from 'vs/workbench/services/suggest/browser/simpleSuggestWidget'; import type { IMarker, Terminal as RawXtermTerminal } from 'xterm'; @@ -146,8 +146,32 @@ export interface IDetachedXTermOptions { colorProvider: IXtermColorProvider; capabilities?: ITerminalCapabilityStore; readonly?: boolean; + processInfo: ITerminalProcessInfo; } +/** + * A {@link ITerminalInstance}-like object that emulates a subset of + * capabilities. This instance is returned from {@link ITerminalService.createDetachedTerminal} + * to represent terminals that appear in other parts of the VS Code UI outside + * of the "Terminal" view or editors. + */ +export interface IDetachedTerminalInstance extends IDisposable { + readonly xterm: IDetachedXtermTerminal; + readonly capabilities: ITerminalCapabilityStore; + + /** + * Attached the terminal to the given element. This should be preferred over + * calling {@link IXtermTerminal.attachToElement} so that extra DOM elements + * for contributions are initialized. + * + * @param container Container the terminal will be rendered in + * @param options Additional options for mounting the terminal in an element + */ + attachToElement(container: HTMLElement, options?: Partial): void; +} + +export const isDetachedTerminalInstance = (t: ITerminalInstance | IDetachedTerminalInstance): t is IDetachedTerminalInstance => typeof (t as ITerminalInstance).instanceId === 'number'; + export interface ITerminalService extends ITerminalInstanceHost { readonly _serviceBrand: undefined; @@ -192,7 +216,7 @@ export interface ITerminalService extends ITerminalInstanceHost { * tracked as a terminal instance. * @params options The options to create the terminal with */ - createDetachedXterm(options: IDetachedXTermOptions): Promise; + createDetachedTerminal(options: IDetachedXTermOptions): Promise; /** * Creates a raw terminal instance, this should not be used outside of the terminal part. @@ -1107,7 +1131,6 @@ export interface IXtermTerminal extends IDisposable { } export interface IDetachedXtermTerminal extends IXtermTerminal { - /** * Writes data to the terminal. * @param data data to write diff --git a/src/vs/workbench/contrib/terminal/browser/terminalExtensions.ts b/src/vs/workbench/contrib/terminal/browser/terminalExtensions.ts index a9e85fa6eb4..6657524b166 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalExtensions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalExtensions.ts @@ -5,19 +5,24 @@ import { BrandedService, IConstructorSignature } from 'vs/platform/instantiation/common/instantiation'; import { Registry } from 'vs/platform/registry/common/platform'; -import { ITerminalContribution, ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { IDetachedTerminalInstance, ITerminalContribution, ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; -import { ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalProcessInfo, ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; +/** Constructor compatible with full terminal instances, is assignable to {@link DetachedCompatibleTerminalContributionCtor} */ export type TerminalContributionCtor = IConstructorSignature; +/** Constructor compatible with detached terminals */ +export type DetachedCompatibleTerminalContributionCtor = IConstructorSignature; -export interface ITerminalContributionDescription { - readonly id: string; - readonly ctor: TerminalContributionCtor; -} +export type ITerminalContributionDescription = { readonly id: string } & ( + | { readonly canRunInDetachedTerminals: false; readonly ctor: TerminalContributionCtor } + | { readonly canRunInDetachedTerminals: true; readonly ctor: DetachedCompatibleTerminalContributionCtor } +); -export function registerTerminalContribution(id: string, ctor: { new(instance: ITerminalInstance, processManager: ITerminalProcessManager, widgetManager: TerminalWidgetManager, ...services: Services): ITerminalContribution }): void { - TerminalContributionRegistry.INSTANCE.registerTerminalContribution(id, ctor); +export function registerTerminalContribution(id: string, ctor: { new(instance: ITerminalInstance, processManager: ITerminalProcessManager, widgetManager: TerminalWidgetManager, ...services: Services): ITerminalContribution }, canRunInDetachedTerminals?: false): void; +export function registerTerminalContribution(id: string, ctor: { new(instance: ITerminalInstance, processManager: ITerminalProcessInfo, widgetManager: TerminalWidgetManager, ...services: Services): ITerminalContribution }, canRunInDetachedTerminals: true): void; +export function registerTerminalContribution(id: string, ctor: { new(instance: ITerminalInstance, processManager: ITerminalProcessManager, widgetManager: TerminalWidgetManager, ...services: Services): ITerminalContribution }, canRunInDetachedTerminals = false): void { + TerminalContributionRegistry.INSTANCE.registerTerminalContribution({ id, ctor, canRunInDetachedTerminals } as ITerminalContributionDescription); } export namespace TerminalExtensionsRegistry { @@ -35,8 +40,8 @@ class TerminalContributionRegistry { constructor() { } - public registerTerminalContribution(id: string, ctor: { new(instance: ITerminalInstance, processManager: ITerminalProcessManager, widgetManager: TerminalWidgetManager, ...services: Services): ITerminalContribution }): void { - this._terminalContributions.push({ id, ctor: ctor as TerminalContributionCtor }); + public registerTerminalContribution(description: ITerminalContributionDescription): void { + this._terminalContributions.push(description); } public getTerminalContributions(): ITerminalContributionDescription[] { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 23b4cbdfd3d..2423c3958b1 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -29,7 +29,7 @@ import { ThemeIcon } from 'vs/base/common/themables'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { VirtualWorkspaceContext } from 'vs/workbench/common/contextkeys'; import { IEditableData, IViewsService } from 'vs/workbench/common/views'; -import { ICreateTerminalOptions, IDetachedXTermOptions, IDetachedXtermTerminal, IRequestAddInstanceToGroupEvent, ITerminalEditorService, ITerminalGroup, ITerminalGroupService, ITerminalInstance, ITerminalInstanceHost, ITerminalInstanceService, ITerminalLocationOptions, ITerminalService, ITerminalServiceNativeDelegate, IXtermTerminal, TerminalConnectionState, TerminalEditorLocation } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ICreateTerminalOptions, IDetachedTerminalInstance, IDetachedXTermOptions, IRequestAddInstanceToGroupEvent, ITerminalEditorService, ITerminalGroup, ITerminalGroupService, ITerminalInstance, ITerminalInstanceHost, ITerminalInstanceService, ITerminalLocationOptions, ITerminalService, ITerminalServiceNativeDelegate, IXtermTerminal, TerminalConnectionState, TerminalEditorLocation } from 'vs/workbench/contrib/terminal/browser/terminal'; import { getCwdForSplit } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { TerminalEditorInput } from 'vs/workbench/contrib/terminal/browser/terminalEditorInput'; @@ -52,6 +52,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { TerminalCapabilityStore } from 'vs/platform/terminal/common/capabilities/terminalCapabilityStore'; import { ITimerService } from 'vs/workbench/services/timer/browser/timerService'; import { mark } from 'vs/base/common/performance'; +import { DeatachedTerminal } from 'vs/workbench/contrib/terminal/browser/detachedTerminal'; export class TerminalService extends Disposable implements ITerminalService { declare _serviceBrand: undefined; @@ -1018,9 +1019,9 @@ export class TerminalService extends Disposable implements ITerminalService { return this._createTerminal(shellLaunchConfig, location, options); } - async createDetachedXterm(options: IDetachedXTermOptions): Promise { + async createDetachedTerminal(options: IDetachedXTermOptions): Promise { const ctor = await TerminalInstance.getXtermConstructor(this._keybindingService, this._contextKeyService); - const instance = this._instantiationService.createInstance( + const xterm = this._instantiationService.createInstance( XtermTerminal, ctor, this._configHelper, @@ -1034,13 +1035,16 @@ export class TerminalService extends Disposable implements ITerminalService { ); if (options.readonly) { - instance.raw.attachCustomKeyEventHandler(() => false); + xterm.raw.attachCustomKeyEventHandler(() => false); } - this._detachedXterms.add(instance); - instance.onDidDispose(() => this._detachedXterms.delete(instance)); + this._detachedXterms.add(xterm); + const l = xterm.onDidDispose(() => { + this._detachedXterms.delete(xterm); + l.dispose(); + }); - return instance; + return new DeatachedTerminal(xterm, options, this._instantiationService); } private async _resolveCwd(shellLaunchConfig: IShellLaunchConfig, splitActiveTerminal: boolean, options?: ICreateTerminalOptions): Promise { diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 3f528b3908c..77e37c5b574 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -253,7 +253,8 @@ export interface IDefaultShellAndArgsRequest { callback: (shell: string, args: string[] | string | undefined) => void; } -export interface ITerminalProcessManager extends IDisposable { +/** Read-only process information that can apply to detached terminals. */ +export interface ITerminalProcessInfo { readonly processState: ProcessState; readonly ptyProcessReady: Promise; readonly shellProcessId: number | undefined; @@ -270,7 +271,11 @@ export interface ITerminalProcessManager extends IDisposable { readonly capabilities: ITerminalCapabilityStore; readonly shellIntegrationNonce: string; readonly extEnvironmentVariableCollection: IMergedEnvironmentVariableCollection | undefined; +} +export const isTerminalProcessManager = (t: ITerminalProcessInfo | ITerminalProcessManager): t is ITerminalProcessManager => typeof (t as ITerminalProcessManager).write === 'function'; + +export interface ITerminalProcessManager extends IDisposable, ITerminalProcessInfo { readonly onPtyDisconnect: Event; readonly onPtyReconnect: Event; diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminal.links.contribution.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminal.links.contribution.ts index bfc8d964116..5d144353e9f 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminal.links.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminal.links.contribution.ts @@ -10,11 +10,11 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { ITerminalContribution, ITerminalInstance, IXtermTerminal } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { IDetachedTerminalInstance, ITerminalContribution, ITerminalInstance, IXtermTerminal, isDetachedTerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { registerActiveInstanceAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { registerTerminalContribution } from 'vs/workbench/contrib/terminal/browser/terminalExtensions'; import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; -import { ITerminalProcessManager, TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalProcessInfo, ITerminalProcessManager, TerminalCommandId, isTerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; import { terminalStrings } from 'vs/workbench/contrib/terminal/common/terminalStrings'; import { ITerminalLinkProviderService } from 'vs/workbench/contrib/terminalContrib/links/browser/links'; @@ -40,8 +40,8 @@ class TerminalLinkContribution extends DisposableStore implements ITerminalContr private _linkResolver: TerminalLinkResolver; constructor( - private readonly _instance: ITerminalInstance, - private readonly _processManager: ITerminalProcessManager, + private readonly _instance: ITerminalInstance | IDetachedTerminalInstance, + private readonly _processManager: ITerminalProcessManager | ITerminalProcessInfo, private readonly _widgetManager: TerminalWidgetManager, @IInstantiationService private readonly _instantiationService: IInstantiationService, @ITerminalLinkProviderService private readonly _terminalLinkProviderService: ITerminalLinkProviderService @@ -52,18 +52,24 @@ class TerminalLinkContribution extends DisposableStore implements ITerminalContr xtermReady(xterm: IXtermTerminal & { raw: RawXtermTerminal }): void { const linkManager = this._instantiationService.createInstance(TerminalLinkManager, xterm.raw, this._processManager, this._instance.capabilities, this._linkResolver); - this._processManager.onProcessReady(() => { + if (isTerminalProcessManager(this._processManager)) { + this._processManager.onProcessReady(() => { + linkManager.setWidgetManager(this._widgetManager); + }); + } else { linkManager.setWidgetManager(this._widgetManager); - }); + } this._linkManager = this.add(linkManager); // Attach the link provider(s) to the instance and listen for changes - for (const linkProvider of this._terminalLinkProviderService.linkProviders) { - this._linkManager.registerExternalLinkProvider(linkProvider.provideLinks.bind(linkProvider, this._instance)); + if (!isDetachedTerminalInstance(this._instance)) { + for (const linkProvider of this._terminalLinkProviderService.linkProviders) { + this._linkManager.registerExternalLinkProvider(linkProvider.provideLinks.bind(linkProvider, this._instance)); + } + this.add(this._terminalLinkProviderService.onDidAddLinkProvider(e => { + linkManager.registerExternalLinkProvider(e.provideLinks.bind(e, this._instance as ITerminalInstance)); + })); } - this.add(this._terminalLinkProviderService.onDidAddLinkProvider(e => { - linkManager.registerExternalLinkProvider(e.provideLinks.bind(e, this._instance)); - })); // TODO: Currently only a single link provider is supported; the one registered by the ext host this.add(this._terminalLinkProviderService.onDidRemoveLinkProvider(e => { linkManager.dispose(); @@ -97,7 +103,7 @@ class TerminalLinkContribution extends DisposableStore implements ITerminalContr } } -registerTerminalContribution(TerminalLinkContribution.ID, TerminalLinkContribution); +registerTerminalContribution(TerminalLinkContribution.ID, TerminalLinkContribution, true); const category = terminalStrings.actionCategory; diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkManager.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkManager.ts index 3cce34e79b6..93105d6e3c0 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkManager.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkManager.ts @@ -25,7 +25,7 @@ import { ILinkHoverTargetOptions, TerminalHover } from 'vs/workbench/contrib/ter import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; import { IXtermCore } from 'vs/workbench/contrib/terminal/browser/xterm-private'; import { ITerminalCapabilityStore } from 'vs/platform/terminal/common/capabilities/capabilities'; -import { ITerminalConfiguration, ITerminalProcessManager, TERMINAL_CONFIG_SECTION } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalConfiguration, ITerminalProcessInfo, TERMINAL_CONFIG_SECTION } from 'vs/workbench/contrib/terminal/common/terminal'; import { IHoverAction } from 'vs/workbench/services/hover/browser/hover'; import type { ILink, ILinkProvider, IViewportRange, Terminal } from 'xterm'; import { convertBufferRangeToViewport } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLinkHelpers'; @@ -47,7 +47,7 @@ export class TerminalLinkManager extends DisposableStore { constructor( private readonly _xterm: Terminal, - private readonly _processManager: ITerminalProcessManager, + private readonly _processInfo: ITerminalProcessInfo, capabilities: ITerminalCapabilityStore, private readonly _linkResolver: ITerminalLinkResolver, @IConfigurationService private readonly _configurationService: IConfigurationService, @@ -65,15 +65,15 @@ export class TerminalLinkManager extends DisposableStore { enableFileLinks = false; break; case 'notRemote': - enableFileLinks = !this._processManager.remoteAuthority; + enableFileLinks = !this._processInfo.remoteAuthority; break; } // Setup link detectors in their order of priority - this._setupLinkDetector(TerminalUriLinkDetector.id, this._instantiationService.createInstance(TerminalUriLinkDetector, this._xterm, this._processManager, this._linkResolver)); + this._setupLinkDetector(TerminalUriLinkDetector.id, this._instantiationService.createInstance(TerminalUriLinkDetector, this._xterm, this._processInfo, this._linkResolver)); if (enableFileLinks) { - this._setupLinkDetector(TerminalMultiLineLinkDetector.id, this._instantiationService.createInstance(TerminalMultiLineLinkDetector, this._xterm, this._processManager, this._linkResolver)); - this._setupLinkDetector(TerminalLocalLinkDetector.id, this._instantiationService.createInstance(TerminalLocalLinkDetector, this._xterm, capabilities, this._processManager, this._linkResolver)); + this._setupLinkDetector(TerminalMultiLineLinkDetector.id, this._instantiationService.createInstance(TerminalMultiLineLinkDetector, this._xterm, this._processInfo, this._linkResolver)); + this._setupLinkDetector(TerminalLocalLinkDetector.id, this._instantiationService.createInstance(TerminalLocalLinkDetector, this._xterm, capabilities, this._processInfo, this._linkResolver)); } this._setupLinkDetector(TerminalWordLinkDetector.id, this.add(this._instantiationService.createInstance(TerminalWordLinkDetector, this._xterm))); @@ -83,8 +83,8 @@ export class TerminalLinkManager extends DisposableStore { this._openers.set(TerminalBuiltinLinkType.LocalFile, localFileOpener); this._openers.set(TerminalBuiltinLinkType.LocalFolderInWorkspace, localFolderInWorkspaceOpener); this._openers.set(TerminalBuiltinLinkType.LocalFolderOutsideWorkspace, this._instantiationService.createInstance(TerminalLocalFolderOutsideWorkspaceLinkOpener)); - this._openers.set(TerminalBuiltinLinkType.Search, this._instantiationService.createInstance(TerminalSearchLinkOpener, capabilities, this._processManager.initialCwd, localFileOpener, localFolderInWorkspaceOpener, () => this._processManager.os || OS)); - this._openers.set(TerminalBuiltinLinkType.Url, this._instantiationService.createInstance(TerminalUrlLinkOpener, !!this._processManager.remoteAuthority)); + this._openers.set(TerminalBuiltinLinkType.Search, this._instantiationService.createInstance(TerminalSearchLinkOpener, capabilities, this._processInfo.initialCwd, localFileOpener, localFolderInWorkspaceOpener, () => this._processInfo.os || OS)); + this._openers.set(TerminalBuiltinLinkType.Url, this._instantiationService.createInstance(TerminalUrlLinkOpener, !!this._processInfo.remoteAuthority)); this._registerStandardLinkProviders(); diff --git a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts index 047d8486c0a..6223c1c211f 100644 --- a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts +++ b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts @@ -74,7 +74,8 @@ import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/vie import { EditorModel } from 'vs/workbench/common/editor/editorModel'; import { PANEL_BACKGROUND, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { IViewDescriptorService, IViewsService, ViewContainerLocation } from 'vs/workbench/common/views'; -import { IDetachedXtermTerminal, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { DetachedProcessInfo } from 'vs/workbench/contrib/terminal/browser/detachedTerminal'; +import { IDetachedTerminalInstance, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { getXtermScaledDimensions } from 'vs/workbench/contrib/terminal/browser/xterm/xtermTerminal'; import { TERMINAL_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; import { flatTestItemDelimiter } from 'vs/workbench/contrib/testing/browser/explorerProjections/display'; @@ -1352,7 +1353,7 @@ class TerminalMessagePeek extends Disposable implements IPeekOutputRenderer { private readonly xtermLayoutDelayer = this._register(new Delayer(50)); /** Active terminal instance. */ - private readonly terminal = this._register(new MutableDisposable()); + private readonly terminal = this._register(new MutableDisposable()); /** Listener for streaming result data */ private readonly outputDataListener = this._register(new MutableDisposable()); @@ -1369,11 +1370,11 @@ class TerminalMessagePeek extends Disposable implements IPeekOutputRenderer { private async makeTerminal() { const prev = this.terminal.value; if (prev) { - prev.clearBuffer(); - prev.clearSearchDecorations(); + prev.xterm.clearBuffer(); + prev.xterm.clearSearchDecorations(); // clearBuffer tries to retain the prompt line, but this doesn't exist for tests. // So clear the screen (J) and move to home (H) to ensure previous data is cleaned up. - prev.write(`\x1b[2J\x1b[0;0H`); + prev.xterm.write(`\x1b[2J\x1b[0;0H`); return prev; } @@ -1387,11 +1388,12 @@ class TerminalMessagePeek extends Disposable implements IPeekOutputRenderer { updateCwd: () => { }, }); - return this.terminal.value = await this.terminalService.createDetachedXterm({ + return this.terminal.value = await this.terminalService.createDetachedTerminal({ rows: 10, cols: 80, readonly: true, capabilities, + processInfo: new DetachedProcessInfo({ initialCwd: cwd.value }), colorProvider: { getBackgroundColor: theme => { const terminalBackground = theme.getColor(TERMINAL_BACKGROUND_COLOR); @@ -1440,17 +1442,17 @@ class TerminalMessagePeek extends Disposable implements IPeekOutputRenderer { } } }, - doListenForMoreData: (output, result, terminal) => result.onChange(e => { + doListenForMoreData: (output, result, { xterm }) => result.onChange(e => { if (e.reason === TestResultItemChangeReason.NewMessage && e.item.item.extId === testItem.extId && e.message.type === TestMessageType.Output) { for (const chunk of output.getRangeIter(e.message.offset, e.message.length)) { - terminal.write(chunk.buffer); + xterm.write(chunk.buffer); } } }), }); if (subject instanceof MessageSubject && subject.message.type === TestMessageType.Output && subject.message.marker !== undefined) { - terminal?.selectMarkedRange(getMarkId(subject.message.marker, true), getMarkId(subject.message.marker, false), /* scrollIntoView= */ true); + terminal?.xterm.selectMarkedRange(getMarkId(subject.message.marker, true), getMarkId(subject.message.marker, false), /* scrollIntoView= */ true); } } @@ -1464,7 +1466,7 @@ class TerminalMessagePeek extends Disposable implements IPeekOutputRenderer { this.updateCwd(Iterable.find(result.tests, t => !!t.item.uri)?.item.uri); return task.output.buffers; }, - doListenForMoreData: (task, _result, terminal) => task.output.onDidWriteData(e => terminal.write(e.buffer)), + doListenForMoreData: (task, _result, { xterm }) => task.output.onDidWriteData(e => xterm.write(e.buffer)), }); } @@ -1472,7 +1474,7 @@ class TerminalMessagePeek extends Disposable implements IPeekOutputRenderer { subject: InspectSubject; getTarget: (result: ITestResult) => T | undefined; doInitialWrite: (target: T, result: LiveTestResult) => Iterable; - doListenForMoreData: (target: T, result: LiveTestResult, terminal: IDetachedXtermTerminal) => IDisposable | undefined; + doListenForMoreData: (target: T, result: LiveTestResult, terminal: IDetachedTerminalInstance) => IDisposable | undefined; }) { const result = opts.subject.result; const target = opts.getTarget(result); @@ -1488,7 +1490,7 @@ class TerminalMessagePeek extends Disposable implements IPeekOutputRenderer { for (const chunk of opts.doInitialWrite(target, result)) { didWriteData ||= chunk.byteLength > 0; pendingWrites.value++; - terminal.write(chunk.buffer, () => pendingWrites.value--); + terminal.xterm.write(chunk.buffer, () => pendingWrites.value--); } } else { this.writeNotice(terminal, localize('runNoOutputForPast', 'Test output is only available for new test runs.')); @@ -1525,12 +1527,12 @@ class TerminalMessagePeek extends Disposable implements IPeekOutputRenderer { } } - private writeNotice(terminal: IDetachedXtermTerminal, str: string) { - terminal.write(`\x1b[2m${str}\x1b[0m`); + private writeNotice(terminal: IDetachedTerminalInstance, str: string) { + terminal.xterm.write(`\x1b[2m${str}\x1b[0m`); } - private attachTerminalToDom(terminal: IDetachedXtermTerminal) { - terminal.write('\x1b[?25l'); // hide cursor + private attachTerminalToDom(terminal: IDetachedTerminalInstance) { + terminal.xterm.write('\x1b[?25l'); // hide cursor requestAnimationFrame(() => this.layoutTerminal(terminal)); terminal.attachToElement(this.container, { enableGpu: false }); } @@ -1549,7 +1551,7 @@ class TerminalMessagePeek extends Disposable implements IPeekOutputRenderer { } private layoutTerminal( - xterm: IDetachedXtermTerminal, + { xterm }: IDetachedTerminalInstance, width = this.dimensions?.width ?? this.container.clientWidth, height = this.dimensions?.height ?? this.container.clientHeight ) { From 810611318eb169b5dfad250408d33b1805220a0b Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Fri, 18 Aug 2023 12:12:40 -0700 Subject: [PATCH 126/223] Update src/vs/workbench/contrib/accessibility/browser/accessibleView.ts Co-authored-by: Joyce Er --- .../contrib/accessibility/browser/accessibleView.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index 71e366bfcce..8d7c443f9e6 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -367,8 +367,9 @@ class AccessibleView extends Disposable { e.stopPropagation(); } })); - disposableStore.add(addDisposableListener(this._toolbar.getElement(), 'keydown', (e: KeyboardEvent) => { - if (e.key === 'Escape') { + disposableStore.add(addDisposableListener(this._toolbar.getElement(), DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => { + const keyboardEvent = new StandardKeyboardEvent(e); + if (keyboardEvent.equals(KeyCode.Escape)) { handleEscape(e); } })); From d1a2b7eac949d1c3064898e6cb8ed310445ceff0 Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Fri, 18 Aug 2023 12:23:18 -0700 Subject: [PATCH 127/223] Add debug logging for Cloud Changes enablement (#190764) --- .../editSessions/browser/editSessions.contribution.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts b/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts index 0f6e359a961..2ac22f1d0d7 100644 --- a/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts +++ b/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts @@ -206,14 +206,17 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo } else if (shouldAutoResumeOnReload) { // The application has previously launched via a protocol URL Continue On flow const hasApplicationLaunchedFromContinueOnFlow = this.storageService.getBoolean(EditSessionsContribution.APPLICATION_LAUNCHED_VIA_CONTINUE_ON_STORAGE_KEY, StorageScope.APPLICATION, false); + this.logService.info(`Prompting to enable cloud changes, has application previously launched from Continue On flow: ${hasApplicationLaunchedFromContinueOnFlow}`); const handlePendingEditSessions = () => { // display a badge in the accounts menu but do not prompt the user to sign in again + this.logService.info('Showing badge to enable cloud changes in accounts menu...'); this.updateAccountsMenuBadge(); this.pendingEditSessionsContext.set(true); // attempt a resume if we are in a pending state and the user just signed in const disposable = this.editSessionsStorageService.onDidSignIn(async () => { disposable.dispose(); + this.logService.info('Showing badge to enable cloud changes in accounts menu succeeded, resuming cloud changes...'); await this.progressService.withProgress(resumeProgressOptions, async (progress) => await this.resumeEditSession(undefined, true, undefined, undefined, progress)); this.storageService.remove(EditSessionsContribution.APPLICATION_LAUNCHED_VIA_CONTINUE_ON_STORAGE_KEY, StorageScope.APPLICATION); this.environmentService.continueOn = undefined; @@ -227,8 +230,10 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo ) { // store the fact that we prompted the user this.storageService.store(EditSessionsContribution.APPLICATION_LAUNCHED_VIA_CONTINUE_ON_STORAGE_KEY, true, StorageScope.APPLICATION, StorageTarget.MACHINE); + this.logService.info('Prompting to enable cloud changes...'); await this.editSessionsStorageService.initialize('read'); if (this.editSessionsStorageService.isSignedIn) { + this.logService.info('Prompting to enable cloud changes succeeded, resuming cloud changes...'); await this.progressService.withProgress(resumeProgressOptions, async (progress) => await this.resumeEditSession(undefined, true, undefined, undefined, progress)); } else { handlePendingEditSessions(); @@ -239,6 +244,8 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo ) { handlePendingEditSessions(); } + } else { + this.logService.debug('Auto resuming cloud changes disabled.'); } } From 66eb90b07ce470642e7f5ac8439323127581740f Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Fri, 18 Aug 2023 13:17:10 -0700 Subject: [PATCH 128/223] Add missing imports --- .../contrib/accessibility/browser/accessibleView.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index 8d7c443f9e6..3ae64e09409 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -38,7 +38,8 @@ import { IAction } from 'vs/base/common/actions'; import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { Codicon } from 'vs/base/common/codicons'; import { ThemeIcon } from 'vs/base/common/themables'; -import { addDisposableListener } from 'vs/base/browser/dom'; +import { addDisposableListener, EventType } from 'vs/base/browser/dom'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; const enum DIMENSIONS { MAX_WIDTH = 600 @@ -367,9 +368,9 @@ class AccessibleView extends Disposable { e.stopPropagation(); } })); - disposableStore.add(addDisposableListener(this._toolbar.getElement(), DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => { + disposableStore.add(addDisposableListener(this._toolbar.getElement(), EventType.KEY_DOWN, (e: KeyboardEvent) => { const keyboardEvent = new StandardKeyboardEvent(e); - if (keyboardEvent.equals(KeyCode.Escape)) { + if (keyboardEvent.equals(KeyCode.Escape)) { handleEscape(e); } })); From 4d0e68207ea272b279e5994be5211de691d2af17 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Fri, 18 Aug 2023 13:27:06 -0700 Subject: [PATCH 129/223] fixed copying image in Interactive Window --- .../browser/controller/cellOutputActions.ts | 19 ++++--- .../browser/view/renderers/webviewPreloads.ts | 49 ++++++++++--------- 2 files changed, 37 insertions(+), 31 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts index f6f859db2a4..956222d7a36 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts @@ -35,10 +35,15 @@ registerAction2(class CopyCellOutputAction extends Action2 { async run(accessor: ServicesAccessor, outputContext: INotebookOutputActionContext | { outputViewModel: ICellOutputViewModel }): Promise { const editorService = accessor.get(IEditorService); - let outputViewModel: ICellOutputViewModel | undefined; + const notebookEditor = getNotebookEditorFromEditorPane(editorService.activeEditorPane); + if (!notebookEditor) { + return; + } + + let outputViewModel: ICellOutputViewModel | undefined; if ('outputId' in outputContext && typeof outputContext.outputId === 'string') { - outputViewModel = getOutputViewModelFromId(outputContext.outputId, editorService); + outputViewModel = getOutputViewModelFromId(outputContext.outputId, notebookEditor); } else { outputViewModel = outputContext.outputViewModel; } @@ -50,9 +55,9 @@ registerAction2(class CopyCellOutputAction extends Action2 { const mimeType = outputViewModel.pickedMimeType?.mimeType; if (mimeType?.startsWith('image/')) { - const editor = editorService.activeEditorPane?.getControl() as INotebookEditor; - await editor.focusNotebookCell(outputViewModel.cellViewModel as ICellViewModel, 'output', { skipReveal: true, outputId: outputViewModel.model.outputId }); - editor.copyOutputImage(outputViewModel); + const focusOptions = { skipReveal: true, outputId: outputViewModel.model.outputId }; + await notebookEditor.focusNotebookCell(outputViewModel.cellViewModel as ICellViewModel, 'output', focusOptions); + notebookEditor.copyOutputImage(outputViewModel); } else { const clipboardService = accessor.get(IClipboardService); const logService = accessor.get(ILogService); @@ -63,8 +68,8 @@ registerAction2(class CopyCellOutputAction extends Action2 { }); -function getOutputViewModelFromId(outputId: string, editorService: IEditorService): ICellOutputViewModel | undefined { - const notebookViewModel = getNotebookEditorFromEditorPane(editorService.activeEditorPane)?.getViewModel(); +function getOutputViewModelFromId(outputId: string, notebookEditor: INotebookEditor): ICellOutputViewModel | undefined { + const notebookViewModel = notebookEditor.getViewModel(); if (notebookViewModel) { const codeCells = notebookViewModel.viewCells.filter(cell => cell.cellKind === CellKind.Code) as CodeCellViewModel[]; for (const cell of codeCells) { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts index 92bf95a2111..4715e64aa08 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -1362,33 +1362,38 @@ async function webviewPreloads(ctx: PreloadContext) { }); }; - const copyImage = async (image: HTMLImageElement, retries = 5) => { + const copyOutputImage = async (outputId: string, retries = 5) => { if (!document.hasFocus() && retries > 0) { // copyImage can be called from outside of the webview, which means this function may be running whilst the webview is gaining focus. // Since navigator.clipboard.write requires the document to be focused, we need to wait for focus. // We cannot use a listener, as there is a high chance the focus is gained during the setup of the listener resulting in us missing it. - setTimeout(() => { copyImage(image, retries - 1); }, 20); + setTimeout(() => { copyOutputImage(outputId, retries - 1); }, 20); return; } try { - await navigator.clipboard.write([new ClipboardItem({ - 'image/png': new Promise((resolve) => { - const canvas = document.createElement('canvas'); - if (canvas !== null) { - canvas.width = image.naturalWidth; - canvas.height = image.naturalHeight; - const context = canvas.getContext('2d'); - context?.drawImage(image, 0, 0); - } - canvas.toBlob((blob) => { - if (blob) { - resolve(blob); + const image = document.getElementById(outputId)?.querySelector('img'); + if (image) { + await navigator.clipboard.write([new ClipboardItem({ + 'image/png': new Promise((resolve) => { + const canvas = document.createElement('canvas'); + if (canvas !== null) { + canvas.width = image.naturalWidth; + canvas.height = image.naturalHeight; + const context = canvas.getContext('2d'); + context?.drawImage(image, 0, 0); } - canvas.remove(); - }, 'image/png'); - }) - })]); + canvas.toBlob((blob) => { + if (blob) { + resolve(blob); + } + canvas.remove(); + }, 'image/png'); + }) + })]); + } else { + console.error('Could not find image element to copy for output with id', outputId); + } } catch (e) { console.error('Could not copy image:', e); } @@ -1495,12 +1500,8 @@ async function webviewPreloads(ctx: PreloadContext) { break; } case 'copyImage': { - const image = document.getElementById(event.data.outputId)?.querySelector('img'); - if (image) { - await copyImage(image); - } else { - console.warn('Could not find image element to copy for output with id', event.data.outputId); - } + + await copyOutputImage(event.data.outputId); break; } From de63553929b5077bc8db7ad609da5924dd737230 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 18 Aug 2023 13:37:40 -0700 Subject: [PATCH 130/223] add an action --- .../browser/accessibilityConfiguration.ts | 1 + .../browser/accessibilityContributions.ts | 28 +---------- .../accessibility/browser/accessibleView.ts | 6 ++- .../browser/accessibleViewActions.ts | 46 ++++++++++++++++++- .../common/accessibilityCommands.ts | 3 +- 5 files changed, 55 insertions(+), 29 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts index b97be1b44df..c8272bc512d 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts @@ -14,6 +14,7 @@ export const accessibleViewIsShown = new RawContextKey('accessibleViewI export const accessibleViewSupportsNavigation = new RawContextKey('accessibleViewSupportsNavigation', false, true); export const accessibleViewVerbosityEnabled = new RawContextKey('accessibleViewVerbosityEnabled', false, true); export const accessibleViewGoToSymbolSupported = new RawContextKey('accessibleViewGoToSymbolSupported', false, true); +export const accessibleViewCurrentProviderId = new RawContextKey('accessibleViewCurrentProviderId', undefined, undefined); /** * Miscellaneous settings tagged with accessibility and implemented in the accessibility contrib but diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts index 21ef08a3bd6..8b1095ce97d 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts @@ -301,13 +301,6 @@ export class InlineCompletionsAccessibleViewContribution extends Disposable { if (!ghostText) { return false; } - const accept = () => { - model.accept(editor).then(() => { - alert('Accepted'); - model.stop(); - editor.focus(); - }); - }; this._options.language = editor.getModel()?.getLanguageId() ?? undefined; accessibleViewService.show({ verbositySettingKey: AccessibilityVerbositySettingId.InlineCompletions, @@ -322,29 +315,12 @@ export class InlineCompletionsAccessibleViewContribution extends Disposable { previous() { model.previous().then(() => show()); }, - onKeyDown: (e) => { - if (e.ctrlKey && e.browserEvent.key === '/') { - accept(); - } - }, - actions: [ - { - id: 'inlineCompletions.accept', - label: localize('inlineCompletions.accept', "Accept Completion (Ctrl+/)"), - tooltip: localize('inlineCompletions.accept', "Accept Completion (Ctrl+/)"), - run: () => { - accept(); - }, - class: ThemeIcon.asClassName(Codicon.check), - enabled: true - } - ], options: this._options }); return true; - }; + }; ContextKeyExpr.and(InlineCompletionContextKeys.inlineSuggestionVisible); return show(); - }, ContextKeyExpr.and(InlineCompletionContextKeys.inlineSuggestionVisible, EditorContextKeys.focus, EditorContextKeys.hasCodeActionsProvider) + }, ) ); } diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index 71fcfb7a5a3..c48af34270d 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -32,7 +32,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IPickerQuickAccessItem } from 'vs/platform/quickinput/browser/pickerQuickAccess'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { AccessibilityCommandId } from 'vs/workbench/contrib/accessibility/common/accessibilityCommands'; -import { AccessibilityVerbositySettingId, accessibilityHelpIsShown, accessibleViewGoToSymbolSupported, accessibleViewIsShown, accessibleViewSupportsNavigation, accessibleViewVerbosityEnabled } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; +import { AccessibilityVerbositySettingId, accessibilityHelpIsShown, accessibleViewCurrentProviderId, accessibleViewGoToSymbolSupported, accessibleViewIsShown, accessibleViewSupportsNavigation, accessibleViewVerbosityEnabled } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; import { getSimpleEditorOptions } from 'vs/workbench/contrib/codeEditor/browser/simpleEditorOptions'; import { IAction } from 'vs/base/common/actions'; import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; @@ -101,6 +101,7 @@ class AccessibleView extends Disposable { private _accessibleViewSupportsNavigation: IContextKey; private _accessibleViewVerbosityEnabled: IContextKey; private _accessibleViewGoToSymbolSupported: IContextKey; + private _accessibleViewCurrentProviderId: IContextKey; get editorWidget() { return this._editorWidget; } private _editorContainer: HTMLElement; @@ -126,6 +127,7 @@ class AccessibleView extends Disposable { this._accessibleViewSupportsNavigation = accessibleViewSupportsNavigation.bindTo(this._contextKeyService); this._accessibleViewVerbosityEnabled = accessibleViewVerbosityEnabled.bindTo(this._contextKeyService); this._accessibleViewGoToSymbolSupported = accessibleViewGoToSymbolSupported.bindTo(this._contextKeyService); + this._accessibleViewCurrentProviderId = accessibleViewCurrentProviderId.bindTo(this._contextKeyService); this._editorContainer = document.createElement('div'); this._editorContainer.classList.add('accessible-view'); @@ -182,6 +184,7 @@ class AccessibleView extends Disposable { onHide: () => { if (!showAccessibleViewHelp) { this._currentProvider = undefined; + this._accessibleViewCurrentProviderId.reset(); } } }; @@ -291,6 +294,7 @@ class AccessibleView extends Disposable { if (!showAccessibleViewHelp) { // don't overwrite the current provider this._currentProvider = provider; + this._accessibleViewCurrentProviderId.set(provider.verbositySettingKey); } this._updateContextKeys(provider, true); const value = this._configurationService.getValue(provider.verbositySettingKey); diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts index b9a3d3f141e..ab3b221030d 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts @@ -11,8 +11,11 @@ import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/act import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { AccessibilityCommandId } from 'vs/workbench/contrib/accessibility/common/accessibilityCommands'; -import { accessibilityHelpIsShown, accessibleViewGoToSymbolSupported, accessibleViewIsShown, accessibleViewSupportsNavigation, accessibleViewVerbosityEnabled } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; +import { AccessibilityVerbositySettingId, accessibilityHelpIsShown, accessibleViewCurrentProviderId, accessibleViewGoToSymbolSupported, accessibleViewIsShown, accessibleViewSupportsNavigation, accessibleViewVerbosityEnabled } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; import { IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { InlineCompletionsController } from 'vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController'; +import { alert } from 'vs/base/browser/ui/aria/aria'; const accessibleViewMenu = { id: MenuId.AccessibleView, @@ -153,3 +156,44 @@ class AccessibleViewDisableHintAction extends Action2 { } } registerAction2(AccessibleViewDisableHintAction); + +class AccessibleViewAcceptInlineCompletionAction extends Action2 { + constructor() { + super({ + id: AccessibilityCommandId.AccessibleViewAcceptInlineCompletionAction, + precondition: ContextKeyExpr.and(accessibleViewIsShown, ContextKeyExpr.equals(accessibleViewCurrentProviderId.key, AccessibilityVerbositySettingId.InlineCompletions)), + keybinding: { + primary: KeyMod.CtrlCmd | KeyCode.Slash, + mac: { primary: KeyMod.WinCtrl | KeyCode.Slash }, + weight: KeybindingWeight.WorkbenchContrib + }, + icon: Codicon.check, + menu: [ + commandPalette, + { + id: MenuId.AccessibleView, + group: 'navigation', + order: 0, + when: ContextKeyExpr.and(accessibleViewIsShown, ContextKeyExpr.equals(accessibleViewCurrentProviderId.key, AccessibilityVerbositySettingId.InlineCompletions)) + }], + title: localize('editor.action.accessibleViewAcceptInlineCompletionAction', "Accept Inline Completion") + }); + } + async run(accessor: ServicesAccessor): Promise { + const codeEditorService = accessor.get(ICodeEditorService); + const editor = codeEditorService.getActiveCodeEditor() || codeEditorService.getFocusedCodeEditor(); + if (!editor) { + return; + } + const model = InlineCompletionsController.get(editor)?.model.get(); + const state = model?.state.get(); + if (!model || !state) { + return; + } + await model.accept(editor); + alert('Accepted'); + model.stop(); + editor.focus(); + } +} +registerAction2(AccessibleViewAcceptInlineCompletionAction); diff --git a/src/vs/workbench/contrib/accessibility/common/accessibilityCommands.ts b/src/vs/workbench/contrib/accessibility/common/accessibilityCommands.ts index 8475978456d..6e73600a925 100644 --- a/src/vs/workbench/contrib/accessibility/common/accessibilityCommands.ts +++ b/src/vs/workbench/contrib/accessibility/common/accessibilityCommands.ts @@ -9,5 +9,6 @@ export const enum AccessibilityCommandId { DisableVerbosityHint = 'editor.action.accessibleViewDisableHint', GoToSymbol = 'editor.action.accessibleViewGoToSymbol', ShowNext = 'editor.action.accessibleViewNext', - ShowPrevious = 'editor.action.accessibleViewPrevious' + ShowPrevious = 'editor.action.accessibleViewPrevious', + AccessibleViewAcceptInlineCompletionAction = 'editor.action.accessibleViewAcceptInlineCompletion' } From 61d996db162360862effe0a3574f3a4b1b5d2ee7 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 18 Aug 2023 13:40:36 -0700 Subject: [PATCH 131/223] improve consistency --- .../contrib/accessibility/browser/accessibleViewActions.ts | 2 +- .../contrib/accessibility/common/accessibilityCommands.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts index ab3b221030d..530917842cc 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts @@ -160,7 +160,7 @@ registerAction2(AccessibleViewDisableHintAction); class AccessibleViewAcceptInlineCompletionAction extends Action2 { constructor() { super({ - id: AccessibilityCommandId.AccessibleViewAcceptInlineCompletionAction, + id: AccessibilityCommandId.AccessibleViewAcceptInlineCompletion, precondition: ContextKeyExpr.and(accessibleViewIsShown, ContextKeyExpr.equals(accessibleViewCurrentProviderId.key, AccessibilityVerbositySettingId.InlineCompletions)), keybinding: { primary: KeyMod.CtrlCmd | KeyCode.Slash, diff --git a/src/vs/workbench/contrib/accessibility/common/accessibilityCommands.ts b/src/vs/workbench/contrib/accessibility/common/accessibilityCommands.ts index 6e73600a925..2fb8316bdc8 100644 --- a/src/vs/workbench/contrib/accessibility/common/accessibilityCommands.ts +++ b/src/vs/workbench/contrib/accessibility/common/accessibilityCommands.ts @@ -10,5 +10,5 @@ export const enum AccessibilityCommandId { GoToSymbol = 'editor.action.accessibleViewGoToSymbol', ShowNext = 'editor.action.accessibleViewNext', ShowPrevious = 'editor.action.accessibleViewPrevious', - AccessibleViewAcceptInlineCompletionAction = 'editor.action.accessibleViewAcceptInlineCompletion' + AccessibleViewAcceptInlineCompletion = 'editor.action.accessibleViewAcceptInlineCompletion' } From 6909c0b4701ffe69ba919734dc088e8802effbba Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 18 Aug 2023 13:43:20 -0700 Subject: [PATCH 132/223] use id --- .../workbench/contrib/accessibility/browser/accessibleView.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index c48af34270d..16095809d8c 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -294,7 +294,7 @@ class AccessibleView extends Disposable { if (!showAccessibleViewHelp) { // don't overwrite the current provider this._currentProvider = provider; - this._accessibleViewCurrentProviderId.set(provider.verbositySettingKey); + this._accessibleViewCurrentProviderId.set(provider.verbositySettingKey.replaceAll('accessibility.verbosity.', '')); } this._updateContextKeys(provider, true); const value = this._configurationService.getValue(provider.verbositySettingKey); From dc2bcb11be090c957b881ee986f28783ff61824c Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 18 Aug 2023 14:05:25 -0700 Subject: [PATCH 133/223] fix #190766 --- .../browser/accessibleViewActions.ts | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts index b9a3d3f141e..8e58dbb60b0 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts @@ -33,7 +33,12 @@ class AccessibleViewNextAction extends Action2 { primary: KeyMod.Alt | KeyCode.BracketRight, weight: KeybindingWeight.WorkbenchContrib }, - menu: [commandPalette, accessibleViewMenu], + menu: [ + commandPalette, + { + ...accessibleViewMenu, + when: ContextKeyExpr.and(accessibleViewIsShown, accessibleViewSupportsNavigation), + }], icon: Codicon.chevronRight, title: localize('editor.action.accessibleViewNext', "Show Next in Accessible View") }); @@ -55,7 +60,13 @@ class AccessibleViewPreviousAction extends Action2 { weight: KeybindingWeight.WorkbenchContrib }, icon: Codicon.chevronLeft, - menu: [commandPalette, accessibleViewMenu], + menu: [ + commandPalette, + { + ...accessibleViewMenu, + when: ContextKeyExpr.and(accessibleViewIsShown, accessibleViewSupportsNavigation), + } + ], title: localize('editor.action.accessibleViewPrevious', "Show Previous in Accessible View") }); } @@ -76,7 +87,13 @@ class AccessibleViewGoToSymbolAction extends Action2 { weight: KeybindingWeight.WorkbenchContrib + 10 }, icon: Codicon.symbolField, - menu: [commandPalette, accessibleViewMenu], + menu: [ + commandPalette, + { + ...accessibleViewMenu, + when: ContextKeyExpr.and(accessibleViewIsShown, accessibleViewSupportsNavigation), + } + ], title: localize('editor.action.accessibleViewGoToSymbol', "Go To Symbol in Accessible View") }); } @@ -140,11 +157,14 @@ class AccessibleViewDisableHintAction extends Action2 { weight: KeybindingWeight.WorkbenchContrib }, icon: Codicon.treeFilterClear, - menu: [commandPalette, + menu: [ + commandPalette, { id: MenuId.AccessibleView, - group: 'navigation' - }], + group: 'navigation', + when: ContextKeyExpr.and(ContextKeyExpr.or(accessibleViewIsShown, accessibilityHelpIsShown), accessibleViewVerbosityEnabled), + } + ], title: localize('editor.action.accessibleViewDisableHint', "Disable Accessible View Hint") }); } From e988d9d71c1356d0f0c1576da7b58943ff6b496d Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 18 Aug 2023 14:09:12 -0700 Subject: [PATCH 134/223] fix #189974 --- .../contrib/accessibility/browser/accessibleViewActions.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts index 530917842cc..9ebe9bb817e 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts @@ -76,6 +76,7 @@ class AccessibleViewGoToSymbolAction extends Action2 { precondition: ContextKeyExpr.and(accessibleViewIsShown, accessibleViewGoToSymbolSupported), keybinding: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyO, + secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Period], weight: KeybindingWeight.WorkbenchContrib + 10 }, icon: Codicon.symbolField, From 12746bae34a36f37452d8188902cb3b01491f80b Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Fri, 18 Aug 2023 14:11:00 -0700 Subject: [PATCH 135/223] Fix import --- .../workbench/contrib/accessibility/browser/accessibleView.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index 3ae64e09409..8acbdb7b870 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { IKeyboardEvent, StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; import { alert } from 'vs/base/browser/ui/aria/aria'; import { KeyCode } from 'vs/base/common/keyCodes'; @@ -39,7 +39,6 @@ import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/men import { Codicon } from 'vs/base/common/codicons'; import { ThemeIcon } from 'vs/base/common/themables'; import { addDisposableListener, EventType } from 'vs/base/browser/dom'; -import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; const enum DIMENSIONS { MAX_WIDTH = 600 From 1d9febc37eebedc3a44c86bb131becf5f30dd0f3 Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Fri, 18 Aug 2023 14:15:12 -0700 Subject: [PATCH 136/223] Render file tree during progressive rendering (#190697) * Render file tree during progressive rendering --- .../contrib/chat/browser/chatListRenderer.ts | 125 +++++++++++++----- .../contrib/chat/common/chatViewModel.ts | 17 +-- 2 files changed, 98 insertions(+), 44 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index add3f8bafb7..8a367015e9e 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -63,7 +63,7 @@ import { ChatFollowups } from 'vs/workbench/contrib/chat/browser/chatFollowups'; import { ChatEditorOptions } from 'vs/workbench/contrib/chat/browser/chatOptions'; import { CONTEXT_REQUEST, CONTEXT_RESPONSE, CONTEXT_RESPONSE_HAS_PROVIDER_ID, CONTEXT_RESPONSE_VOTE } from 'vs/workbench/contrib/chat/common/chatContextKeys'; import { IChatReplyFollowup, IChatResponseProgressFileTreeData, IChatService, ISlashCommand, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService'; -import { IChatResponseViewModel, IChatWelcomeMessageViewModel, isRequestVM, isResponseVM, isWelcomeVM } from 'vs/workbench/contrib/chat/common/chatViewModel'; +import { IChatResponseMarkdownRenderData, IChatResponseRenderData, IChatResponseViewModel, IChatWelcomeMessageViewModel, isRequestVM, isResponseVM, isWelcomeVM } from 'vs/workbench/contrib/chat/common/chatViewModel'; import { IWordCountResult, getNWords } from 'vs/workbench/contrib/chat/common/chatWordCounter'; import { MenuPreventer } from 'vs/workbench/contrib/codeEditor/browser/menuPreventer'; import { SelectionClipboardContributionID } from 'vs/workbench/contrib/codeEditor/browser/selectionClipboard'; @@ -103,7 +103,6 @@ export interface IChatListItemRendererOptions { } export class ChatListItemRenderer extends Disposable implements ITreeRenderer { - static readonly cursorCharacter = '\u258c'; static readonly ID = 'item'; private readonly codeBlocksByResponseId = new Map(); @@ -399,29 +398,92 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer { + const renderedPart = renderedParts[index]; + // Is this part completely new? + if (!renderedPart) { + if (isInteractiveProgressTreeData(part)) { + partsToRender[index] = part; + } else { + const wordCountResult = this.getDataForProgressiveRender(element, part); + if (wordCountResult !== undefined) { + partsToRender[index] = { + renderedWordCount: wordCountResult.actualWordCount, + lastRenderTime: Date.now(), + isFullyRendered: wordCountResult.isFullString, + }; + wordCountResults[index] = wordCountResult; + } + } + } + + // Did this part go from being a placeholder string to resolved tree data? + else if (isInteractiveProgressTreeData(part) && !isInteractiveProgressTreeData(renderedPart)) { + partsToRender[index] = part; + } + + // Did this part's content change? + else if (!isInteractiveProgressTreeData(part) && !isInteractiveProgressTreeData(renderedPart) && !renderedPart.isFullyRendered) { + const wordCountResult = this.getDataForProgressiveRender(element, part, renderedPart); + if (wordCountResult !== undefined) { + partsToRender[index] = { + renderedWordCount: wordCountResult.actualWordCount, + lastRenderTime: Date.now(), + isFullyRendered: wordCountResult.isFullString, + }; + wordCountResults[index] = wordCountResult; + } + } + }); + + isFullyRendered = partsToRender.length === 0; + if (isFullyRendered && element.isComplete) { // Response is done and content is rendered, so do a normal render this.traceLayout('runProgressiveRender', `end progressive render, index=${index} and clearing renderData, response is complete, index=${index}`); element.renderData = undefined; disposables.clear(); this.basicRenderElement(element.response.value, element, index, templateData); - } else if (renderValue) { - element.renderData = { - renderedWordCount: renderValue.actualWordCount, - lastRenderTime: Date.now(), - isFullyRendered: renderValue.isFullString - }; + } else if (!isFullyRendered) { + let hasRenderedOneMarkdownBlock = false; + partsToRender.forEach((partToRender, index) => { + if (!partToRender) { + return; + } - const plusCursor = (renderValue.value.match(/```\s*$/) ? - renderValue.value + '\n\n' : - renderValue.value) + ` ${ChatListItemRenderer.cursorCharacter}`; - const result = this.renderMarkdown(new MarkdownString(plusCursor), element, disposables, templateData, true); - // Doing the progressive render - dom.clearNode(templateData.value); - templateData.value.appendChild(result.element); - disposables.add(result); + let result; + if (isInteractiveProgressTreeData(partToRender)) { + result = this.renderTreeData(partToRender, element, disposables, templateData, index); + } + + // Avoid doing progressive rendering for multiple markdown parts simultaneously + else if (!hasRenderedOneMarkdownBlock) { + const value = wordCountResults[index].value; + result = this.renderMarkdown(new MarkdownString(value), element, disposables, templateData, true); + hasRenderedOneMarkdownBlock = true; + } + + if (!result) { + return; + } + + // Doing the progressive render + renderedParts[index] = partToRender; + const existingElement = templateData.value.children[index]; + if (existingElement) { + templateData.value.replaceChild(result.element, existingElement); + } else { + templateData.value.appendChild(result.element); + } + disposables.add(result); + }); } else { // Nothing new to render, not done, keep waiting return false; @@ -435,7 +497,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer void } { @@ -558,8 +620,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer, index: number, templateData: IChatListItemTemplate): void { @@ -886,28 +947,16 @@ class CodeBlockPart extends Disposable implements IChatResultCodeBlockPart { } private setText(newText: string): void { - let currentText = this.textModel.getLinesContent().join('\n'); + const currentText = this.textModel.getLinesContent().join('\n'); if (newText === currentText) { return; } - let removedChars = 0; - if (currentText.endsWith(` ${ChatListItemRenderer.cursorCharacter}`)) { - removedChars = 2; - } else if (currentText.endsWith(ChatListItemRenderer.cursorCharacter)) { - removedChars = 1; - } - - if (removedChars > 0) { - currentText = currentText.slice(0, currentText.length - removedChars); - } - if (newText.startsWith(currentText)) { const text = newText.slice(currentText.length); const lastLine = this.textModel.getLineCount(); const lastCol = this.textModel.getLineMaxColumn(lastLine); - const insertAtCol = lastCol - removedChars; - this.textModel.applyEdits([{ range: new Range(lastLine, insertAtCol, lastLine, lastCol), text }]); + this.textModel.applyEdits([{ range: new Range(lastLine, lastCol, lastLine, lastCol), text }]); } else { // console.log(`Failed to optimize setText`); this.textModel.setValue(newText); @@ -1133,3 +1182,7 @@ class ChatListTreeDataSource implements IAsyncDataSource acc += ('label' in part ? 0 : part.renderedWordCount), 0); if (!this.isComplete) { - this.trace('onDidChange', `Update- got ${wordCount} words over ${timeDiff}ms = ${impliedWordLoadRate} words/s. ${this.renderData?.renderedWordCount} words are rendered.`); + this.trace('onDidChange', `Update- got ${wordCount} words over ${timeDiff}ms = ${impliedWordLoadRate} words/s. ${renderedWordCount} words are rendered.`); this._contentUpdateTimings = { loadingStartTime: this._contentUpdateTimings!.loadingStartTime, lastUpdateTime: now, @@ -336,7 +341,7 @@ export class ChatResponseViewModel extends Disposable implements IChatResponseVi impliedWordLoadRate }; } else { - this.trace(`onDidChange`, `Done- got ${wordCount} words over ${timeDiff}ms = ${impliedWordLoadRate} words/s. ${this.renderData?.renderedWordCount} words are rendered.`); + this.trace(`onDidChange`, `Done- got ${wordCount} words over ${timeDiff}ms = ${impliedWordLoadRate} words/s. ${renderedWordCount} words are rendered.`); } } else { this.logService.warn('ChatResponseViewModel#onDidChange: got model update but contentUpdateTimings is not initialized'); @@ -344,10 +349,6 @@ export class ChatResponseViewModel extends Disposable implements IChatResponseVi // new data -> new id, new content to render this._modelChangeCount++; - if (this.renderData) { - this.renderData.isFullyRendered = false; - this.renderData.lastRenderTime = Date.now(); - } this._onDidChange.fire(); })); From f6c280baf84d9a6c6f9f5911d809f96a1df9ce3b Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 18 Aug 2023 14:16:17 -0700 Subject: [PATCH 137/223] fix #188755 --- .../contrib/accessibility/browser/accessibilityContributions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts index 8b1095ce97d..3d6968b88c4 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts @@ -54,7 +54,7 @@ export class EditorAccessibilityHelpContribution extends Disposable { codeEditor = codeEditorService.getActiveCodeEditor()!; } accessibleViewService.show(instantiationService.createInstance(AccessibilityHelpProvider, codeEditor)); - })); + }, EditorContextKeys.focus)); } } From 41a5fc5945ad1f97173b85e643f906d73ddb729e Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 18 Aug 2023 14:37:03 -0700 Subject: [PATCH 138/223] fix #190718 --- .../accessibility/browser/accessibilityContributions.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts index 3d6968b88c4..80cdebaab8e 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts @@ -282,6 +282,7 @@ export class InlineCompletionsAccessibleViewContribution extends Disposable { this._register(AccessibleViewAction.addImplementation(95, 'inline-completions', accessor => { const accessibleViewService = accessor.get(IAccessibleViewService); const codeEditorService = accessor.get(ICodeEditorService); + const contextViewService = accessor.get(IContextViewService); const show = () => { const editor = codeEditorService.getActiveCodeEditor() || codeEditorService.getFocusedCodeEditor(); if (!editor) { @@ -310,10 +311,12 @@ export class InlineCompletionsAccessibleViewContribution extends Disposable { editor.focus(); }, next() { - model.next().then(() => show()); + contextViewService.hideContextView(); + setTimeout(() => model.next().then(() => show()), 50); }, previous() { - model.previous().then(() => show()); + contextViewService.hideContextView(); + setTimeout(() => model.previous().then(() => show()), 50); }, options: this._options }); From 349c93df2a8e531d0353853ca08af968a121033f Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Fri, 18 Aug 2023 15:20:46 -0700 Subject: [PATCH 139/223] Add progressbar to Settings editor (#190708) --- .../preferences/browser/settingsEditor2.ts | 86 ++++++++++--------- 1 file changed, 44 insertions(+), 42 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index 1dac2bd1e62..d0666ca8be5 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -66,6 +66,7 @@ import { IWorkbenchAssignmentService } from 'vs/workbench/services/assignment/co import { IProductService } from 'vs/platform/product/common/productService'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { registerNavigableContainer } from 'vs/workbench/browser/actions/widgetNavigationCommands'; +import { IEditorProgressService } from 'vs/platform/progress/common/progress'; export const enum SettingsFocusContext { @@ -237,6 +238,7 @@ export class SettingsEditor2 extends EditorPane { @IProductService private readonly productService: IProductService, @IEnvironmentService private readonly environmentService: IEnvironmentService, @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, + @IEditorProgressService private readonly editorProgressService: IEditorProgressService, ) { super(SettingsEditor2.ID, telemetryService, themeService, storageService); this.delayedFilterLogging = new Delayer(1000); @@ -566,6 +568,9 @@ export class SettingsEditor2 extends EditorPane { this.searchWidget.updateAriaLabel(label); } + /** + * Render the header of the Settings editor, which includes the content above the splitview. + */ private createHeader(parent: HTMLElement): void { this.headerContainer = DOM.append(parent, $('.settings-header')); @@ -1622,19 +1627,20 @@ export class SettingsEditor2 extends EditorPane { // Trigger the local search. If it didn't find an exact match, trigger the remote search. const searchInProgress = this.searchInProgress = new CancellationTokenSource(); - return this.localSearchDelayer.trigger(() => { + return this.localSearchDelayer.trigger(async () => { if (searchInProgress && !searchInProgress.token.isCancellationRequested) { - return this.localFilterPreferences(query).then(result => { - if (result && !result.exactMatch) { - this.remoteSearchThrottle.trigger(() => { - return searchInProgress && !searchInProgress.token.isCancellationRequested ? - this.remoteSearchPreferences(query, this.searchInProgress!.token) : - Promise.resolve(); - }); - } - }); - } else { - return Promise.resolve(); + const progressRunner = this.editorProgressService.show(true); + const result = await this.localFilterPreferences(query); + if (result && !result.exactMatch) { + this.remoteSearchThrottle.trigger(async () => { + if (searchInProgress && !searchInProgress.token.isCancellationRequested) { + await this.remoteSearchPreferences(query, this.searchInProgress!.token); + } + progressRunner.done(); + }); + } else { + progressRunner.done(); + } } }); } @@ -1654,36 +1660,32 @@ export class SettingsEditor2 extends EditorPane { ]).then(() => { }); } - private filterOrSearchPreferences(query: string, type: SearchResultIdx, searchProvider?: ISearchProvider, token?: CancellationToken): Promise { - return this._filterOrSearchPreferencesModel(query, this.defaultSettingsEditorModel, searchProvider, token).then(result => { - if (token && token.isCancellationRequested) { - // Handle cancellation like this because cancellation is lost inside the search provider due to async/await - return null; - } - - if (!this.searchResultModel) { - this.searchResultModel = this.instantiationService.createInstance(SearchResultModel, this.viewState, this.workspaceTrustManagementService.isWorkspaceTrusted()); - // Must be called before this.renderTree() - // to make sure the search results count is set. - this.searchResultModel.setResult(type, result); - this.tocTreeModel.currentSearchModel = this.searchResultModel; - this.onSearchModeToggled(); - } else { - this.searchResultModel.setResult(type, result); - this.tocTreeModel.update(); - } - - if (type === SearchResultIdx.Local) { - this.tocTree.setFocus([]); - this.viewState.filterToCategory = undefined; - this.tocTree.expandAll(); - } - - this.settingsTree.scrollTop = 0; - this.refreshTOCTree(); - this.renderTree(undefined, true); - return result; - }); + private async filterOrSearchPreferences(query: string, type: SearchResultIdx, searchProvider?: ISearchProvider, token?: CancellationToken): Promise { + const result = await this._filterOrSearchPreferencesModel(query, this.defaultSettingsEditorModel, searchProvider, token); + if (token?.isCancellationRequested) { + // Handle cancellation like this because cancellation is lost inside the search provider due to async/await + return null; + } + if (!this.searchResultModel) { + this.searchResultModel = this.instantiationService.createInstance(SearchResultModel, this.viewState, this.workspaceTrustManagementService.isWorkspaceTrusted()); + // Must be called before this.renderTree() + // to make sure the search results count is set. + this.searchResultModel.setResult(type, result); + this.tocTreeModel.currentSearchModel = this.searchResultModel; + this.onSearchModeToggled(); + } else { + this.searchResultModel.setResult(type, result); + this.tocTreeModel.update(); + } + if (type === SearchResultIdx.Local) { + this.tocTree.setFocus([]); + this.viewState.filterToCategory = undefined; + this.tocTree.expandAll(); + } + this.settingsTree.scrollTop = 0; + this.refreshTOCTree(); + this.renderTree(undefined, true); + return result; } private renderResultCountMessages() { From e92a7f0f6968abd596434b57a7647e36c1ed5eaf Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Fri, 18 Aug 2023 15:32:17 -0700 Subject: [PATCH 140/223] Remove titlebar and include side buttons in quick chat (#190780) This adds buttons to the right of the chat box. These buttons were previously on the quick pick's titlebar, but in order to have a smooth transition between Command Palette and Quick Chat we wanted to move away from the title bar. --- .../platform/quickinput/browser/quickInput.ts | 3 +- .../browser/actions/chatQuickInputActions.ts | 274 ++++-------------- .../contrib/chat/browser/chat.contribution.ts | 4 +- src/vs/workbench/contrib/chat/browser/chat.ts | 8 + .../contrib/chat/browser/chatQuick.ts | 209 +++++++++++++ 5 files changed, 281 insertions(+), 217 deletions(-) create mode 100644 src/vs/workbench/contrib/chat/browser/chatQuick.ts diff --git a/src/vs/platform/quickinput/browser/quickInput.ts b/src/vs/platform/quickinput/browser/quickInput.ts index 072f2f56e89..c56674f2f78 100644 --- a/src/vs/platform/quickinput/browser/quickInput.ts +++ b/src/vs/platform/quickinput/browser/quickInput.ts @@ -1264,8 +1264,7 @@ export class QuickWidget extends QuickInput implements IQuickWidget { const visibilities: Visibilities = { title: !!this.title || !!this.step || !!this.buttons.length, - description: !!this.description || !!this.step, - progressBar: true + description: !!this.description || !!this.step }; this.ui.setVisibilities(visibilities); diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatQuickInputActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatQuickInputActions.ts index c644cc34fbe..31c6361beaa 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatQuickInputActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatQuickInputActions.ts @@ -3,32 +3,71 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as dom from 'vs/base/browser/dom'; -import { CancellationToken } from 'vs/base/common/cancellation'; import { Codicon } from 'vs/base/common/codicons'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { ThemeIcon } from 'vs/base/common/themables'; import { localize } from 'vs/nls'; import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; -import { ICommandService } from 'vs/platform/commands/common/commands'; -import { ContextKeyExpr, IContextKeyService, IScopedContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { IQuickInputService, IQuickWidget } from 'vs/platform/quickinput/common/quickInput'; -import { editorBackground, editorForeground, inputBackground } from 'vs/platform/theme/common/colorRegistry'; import { CHAT_CATEGORY } from 'vs/workbench/contrib/chat/browser/actions/chatActions'; -import { IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat'; -import { IChatViewOptions } from 'vs/workbench/contrib/chat/browser/chatViewPane'; -import { ChatWidget } from 'vs/workbench/contrib/chat/browser/chatWidget'; +import { IQuickChatService } from 'vs/workbench/contrib/chat/browser/chat'; import { CONTEXT_PROVIDER_EXISTS } from 'vs/workbench/contrib/chat/common/chatContextKeys'; -import { ChatModel } from 'vs/workbench/contrib/chat/common/chatModel'; import { IChatService } from 'vs/workbench/contrib/chat/common/chatService'; -export const ASK_QUICK_QUESTION_ACTION_ID = 'chat.action.askQuickQuestion'; +export const ASK_QUICK_QUESTION_ACTION_ID = 'workbench.action.quickchat.toggle'; export function registerQuickChatActions() { registerAction2(QuickChatGlobalAction); + + registerAction2(class OpenInChatViewAction extends Action2 { + constructor() { + super({ + id: 'workbench.action.quickchat.openInChatView', + title: { + value: localize('chat.openInChatView.label', "Open in Chat View"), + original: 'Open in Chat View' + }, + f1: false, + category: CHAT_CATEGORY, + icon: Codicon.commentDiscussion, + menu: { + id: MenuId.ChatInputSide, + group: 'navigation', + order: 10 + } + }); + } + + run(accessor: ServicesAccessor) { + const quickChatService = accessor.get(IQuickChatService); + quickChatService.openInChatView(); + } + }); + + registerAction2(class CloseQuickChatAction extends Action2 { + constructor() { + super({ + id: 'workbench.action.quickchat.close', + title: { + value: localize('chat.closeQuickChat.label', "Close Quick Chat"), + original: 'Close Quick Chat' + }, + f1: false, + category: CHAT_CATEGORY, + icon: Codicon.close, + menu: { + id: MenuId.ChatInputSide, + group: 'navigation', + order: 20 + } + }); + } + + run(accessor: ServicesAccessor) { + const quickChatService = accessor.get(IQuickChatService); + quickChatService.close(); + } + }); } class QuickChatGlobalAction extends Action2 { @@ -56,13 +95,13 @@ class QuickChatGlobalAction extends Action2 { }); } - override async run(accessor: ServicesAccessor, query: string): Promise { + override run(accessor: ServicesAccessor, query?: string): void { const chatService = accessor.get(IChatService); - const commandService = accessor.get(ICommandService); + const quickChatService = accessor.get(IQuickChatService); // Grab the first provider and run its command const info = chatService.getProviderInfos()[0]; if (info) { - await commandService.executeCommand(`workbench.action.openQuickChat.${info.id}`, query); + quickChatService.toggle(info.id, query); } } } @@ -77,10 +116,6 @@ class QuickChatGlobalAction extends Action2 { */ export function getQuickChatActionForProvider(id: string, label: string) { return class AskQuickChatAction extends Action2 { - _currentTimer: any | undefined; - _input: IQuickWidget | undefined; - _currentChat: QuickChat | undefined; - constructor() { super({ id: `workbench.action.openQuickChat.${id}`, @@ -90,198 +125,9 @@ export function getQuickChatActionForProvider(id: string, label: string) { }); } - override run(accessor: ServicesAccessor, query: string): void { - const quickInputService = accessor.get(IQuickInputService); - const chatService = accessor.get(IChatService); - const instantiationService = accessor.get(IInstantiationService); - - // First things first, clear the existing timer that will dispose the session - clearTimeout(this._currentTimer); - this._currentTimer = undefined; - - // If the input is already shown, hide it. This provides a toggle behavior of the quick pick - if (this._input !== undefined) { - this._input.hide(); - return; - } - - // Check if any providers are available. If not, show nothing - // This shouldn't be needed because of the precondition, but just in case - const providerInfo = chatService.getProviderInfos()[0]; - if (!providerInfo) { - return; - } - - const disposableStore = new DisposableStore(); - - //#region Setup quick pick - - this._input = quickInputService.createQuickWidget(); - disposableStore.add(this._input); - - const containerSession = dom.$('.interactive-session'); - this._input.widget = containerSession; - - this._currentChat ??= instantiationService.createInstance(QuickChat, { - providerId: providerInfo.id, - }); - // show needs to come before the current chat rendering - this._input.show(); - this._currentChat.render(containerSession); - - const clearButton = { - iconClass: ThemeIcon.asClassName(Codicon.clearAll), - tooltip: localize('clear', "Clear"), - }; - this._input.buttons = [ - clearButton, - { - iconClass: ThemeIcon.asClassName(Codicon.commentDiscussion), - tooltip: localize('openInChat', "Open In Chat View"), - } - ]; - this._input.title = providerInfo.displayName; - - disposableStore.add(this._input.onDidHide(() => { - disposableStore.dispose(); - this._input = undefined; - this._currentTimer = setTimeout(() => { - this._currentChat?.dispose(); - this._currentChat = undefined; - }, 1000 * 30); // 30 seconds - })); - - disposableStore.add(this._input.onDidTriggerButton((e) => { - if (e === clearButton) { - this._currentChat?.clear(); - } else { - this._currentChat?.openChatView(); - } - })); - - //#endregion - - this._currentChat.focus(); - - if (query) { - this._currentChat.setValue(query); - this._currentChat.acceptInput(); - } + override run(accessor: ServicesAccessor, query?: string): void { + const quickChatService = accessor.get(IQuickChatService); + quickChatService.toggle(id, query); } }; } - -class QuickChat extends Disposable { - private widget!: ChatWidget; - private model: ChatModel | undefined; - private _currentQuery: string | undefined; - - private _scopedContextKeyService!: IScopedContextKeyService; - get scopedContextKeyService() { - return this._scopedContextKeyService; - } - - constructor( - private readonly _options: IChatViewOptions, - @IInstantiationService private readonly instantiationService: IInstantiationService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, - @IChatService private readonly chatService: IChatService, - @IChatWidgetService private readonly _chatWidgetService: IChatWidgetService - ) { - super(); - } - - clear() { - this.model?.dispose(); - this.model = undefined; - this.updateModel(); - this.widget.inputEditor.setValue(''); - } - - focus(): void { - if (this.widget) { - this.widget.focusInput(); - } - } - - render(parent: HTMLElement): void { - this._scopedContextKeyService?.dispose(); - this._scopedContextKeyService = this._register(this.contextKeyService.createScoped(parent)); - const scopedInstantiationService = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, this.scopedContextKeyService])); - this.widget?.dispose(); - this.widget = this._register( - scopedInstantiationService.createInstance( - ChatWidget, - { resource: true, renderInputOnTop: true, renderStyle: 'compact' }, - { - listForeground: editorForeground, - listBackground: editorBackground, - inputEditorBackground: inputBackground, - resultEditorBackground: editorBackground - })); - this.widget.render(parent); - this.widget.setVisible(true); - this.widget.setDynamicChatTreeItemLayout(2, 600); - this.updateModel(); - if (this._currentQuery) { - this.widget.inputEditor.setSelection({ - startLineNumber: 1, - startColumn: 1, - endLineNumber: 1, - endColumn: this._currentQuery.length + 1 - }); - } - - this.registerListeners(); - } - - private registerListeners(): void { - this._register(this.widget.inputEditor.onDidChangeModelContent((e) => { - this._currentQuery = this.widget.inputEditor.getValue(); - })); - this._register(this.widget.onDidClear(() => this.clear())); - } - - async acceptInput(): Promise { - return this.widget.acceptInput(); - } - - async openChatView(): Promise { - const widget = await this._chatWidgetService.revealViewForProvider(this._options.providerId); - if (!widget?.viewModel || !this.model) { - return; - } - - for (const request of this.model.getRequests()) { - if (request.response?.response.value || request.response?.errorDetails) { - this.chatService.addCompleteRequest(widget.viewModel.sessionId, - request.message as string, - { - message: request.response.response.asString(), - errorDetails: request.response.errorDetails - }); - } else if (request.message) { - - } - } - - const value = this.widget.inputEditor.getValue(); - if (value) { - widget.inputEditor.setValue(value); - } - widget.focusInput(); - } - - setValue(value: string): void { - this.widget.inputEditor.setValue(value); - } - - private updateModel(): void { - this.model ??= this.chatService.startSession(this._options.providerId, CancellationToken.None); - if (!this.model) { - throw new Error('Could not start chat session'); - } - - this.widget.setModel(this.model, { inputValue: this._currentQuery }); - } -} diff --git a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts index 01068dfc64f..09d6c46267b 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts @@ -22,7 +22,7 @@ import { registerChatExecuteActions } from 'vs/workbench/contrib/chat/browser/ac import { registerQuickChatActions } from 'vs/workbench/contrib/chat/browser/actions/chatQuickInputActions'; import { registerChatTitleActions } from 'vs/workbench/contrib/chat/browser/actions/chatTitleActions'; import { registerChatExportActions } from 'vs/workbench/contrib/chat/browser/actions/chatImportExport'; -import { IChatAccessibilityService, IChatWidget, IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat'; +import { IChatAccessibilityService, IChatWidget, IChatWidgetService, IQuickChatService } from 'vs/workbench/contrib/chat/browser/chat'; import { ChatContributionService } from 'vs/workbench/contrib/chat/browser/chatContributionServiceImpl'; import { ChatEditor, IChatEditorOptions } from 'vs/workbench/contrib/chat/browser/chatEditor'; import { ChatEditorInput, ChatEditorInputSerializer } from 'vs/workbench/contrib/chat/browser/chatEditorInput'; @@ -52,6 +52,7 @@ import { AccessibleViewAction } from 'vs/workbench/contrib/accessibility/browser import { ICommandService } from 'vs/platform/commands/common/commands'; import { ChatVariablesService, IChatVariablesService } from 'vs/workbench/contrib/chat/common/chatVariables'; import { registerChatFileTreeActions } from 'vs/workbench/contrib/chat/browser/actions/chatFileTreeActions'; +import { QuickChatService } from 'vs/workbench/contrib/chat/browser/chatQuick'; // Register configuration const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); @@ -258,6 +259,7 @@ registerClearActions(); registerSingleton(IChatService, ChatService, InstantiationType.Delayed); registerSingleton(IChatContributionService, ChatContributionService, InstantiationType.Delayed); registerSingleton(IChatWidgetService, ChatWidgetService, InstantiationType.Delayed); +registerSingleton(IQuickChatService, QuickChatService, InstantiationType.Delayed); registerSingleton(IChatAccessibilityService, ChatAccessibilityService, InstantiationType.Delayed); registerSingleton(IChatWidgetHistoryService, ChatWidgetHistoryService, InstantiationType.Delayed); registerSingleton(IChatProviderService, ChatProviderService, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/chat/browser/chat.ts b/src/vs/workbench/contrib/chat/browser/chat.ts index 59e1f4181ef..8e1c6ee182c 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.ts @@ -11,6 +11,7 @@ import { URI } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; export const IChatWidgetService = createDecorator('chatWidgetService'); +export const IQuickChatService = createDecorator('quickChatService'); export const IChatAccessibilityService = createDecorator('chatAccessibilityService'); export interface IChatWidgetService { @@ -32,6 +33,13 @@ export interface IChatWidgetService { getWidgetBySessionId(sessionId: string): IChatWidget | undefined; } +export interface IQuickChatService { + readonly _serviceBrand: undefined; + toggle(providerId: string, query?: string): void; + focus(): void; + close(): void; + openInChatView(): void; +} export interface IChatAccessibilityService { readonly _serviceBrand: undefined; diff --git a/src/vs/workbench/contrib/chat/browser/chatQuick.ts b/src/vs/workbench/contrib/chat/browser/chatQuick.ts new file mode 100644 index 00000000000..ec90707236b --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/chatQuick.ts @@ -0,0 +1,209 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as dom from 'vs/base/browser/dom'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { IContextKeyService, IScopedContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { IQuickInputService, IQuickWidget } from 'vs/platform/quickinput/common/quickInput'; +import { editorBackground, editorForeground, inputBackground } from 'vs/platform/theme/common/colorRegistry'; +import { IChatWidgetService, IQuickChatService } from 'vs/workbench/contrib/chat/browser/chat'; +import { IChatViewOptions } from 'vs/workbench/contrib/chat/browser/chatViewPane'; +import { ChatWidget } from 'vs/workbench/contrib/chat/browser/chatWidget'; +import { ChatModel } from 'vs/workbench/contrib/chat/common/chatModel'; +import { IChatService } from 'vs/workbench/contrib/chat/common/chatService'; + +export class QuickChatService implements IQuickChatService { + readonly _serviceBrand: undefined; + + _input: IQuickWidget | undefined; + _currentChat: QuickChat | undefined; + + constructor( + @IQuickInputService private readonly quickInputService: IQuickInputService, + @IChatService private readonly chatService: IChatService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + ) { } + + get focused(): boolean { + const widget = this._input?.widget as HTMLElement; + if (!widget) { + return false; + } + return dom.isAncestor(document.activeElement, widget); + } + + toggle(providerId: string, query?: string | undefined): void { + // If the input is already shown, hide it. This provides a toggle behavior of the quick pick + if (this.focused) { + this.close(); + return; + } + + // Check if any providers are available. If not, show nothing + // This shouldn't be needed because of the precondition, but just in case + const providerInfo = this.chatService.getProviderInfos().find(info => info.id === providerId); + if (!providerInfo) { + return; + } + + const disposableStore = new DisposableStore(); + + this._input = this.quickInputService.createQuickWidget(); + this._input.contextKey = 'chatInputVisible'; + this._input.ignoreFocusOut = true; + disposableStore.add(this._input); + + const containerSession = dom.$('.interactive-session'); + this._input.widget = containerSession; + + this._currentChat ??= this.instantiationService.createInstance(QuickChat, { + providerId: providerInfo.id, + }); + + // show needs to come before the current chat rendering + this._input.show(); + this._currentChat.render(containerSession); + + disposableStore.add(this._input.onDidHide(() => { + disposableStore.dispose(); + this._input = undefined; + })); + + this._currentChat.focus(); + + if (query) { + this._currentChat.setValue(query); + this._currentChat.acceptInput(); + } + } + focus(): void { + this._currentChat?.focus(); + } + close(): void { + this._input?.dispose(); + } + async openInChatView(): Promise { + await this._currentChat?.openChatView(); + this.close(); + } +} + +class QuickChat extends Disposable { + private widget!: ChatWidget; + private model: ChatModel | undefined; + private _currentQuery: string | undefined; + + private _scopedContextKeyService!: IScopedContextKeyService; + get scopedContextKeyService() { + return this._scopedContextKeyService; + } + + constructor( + private readonly _options: IChatViewOptions, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IContextKeyService private readonly contextKeyService: IContextKeyService, + @IChatService private readonly chatService: IChatService, + @IChatWidgetService private readonly _chatWidgetService: IChatWidgetService + ) { + super(); + } + + clear() { + this.model?.dispose(); + this.model = undefined; + this.updateModel(); + this.widget.inputEditor.setValue(''); + } + + focus(): void { + if (this.widget) { + this.widget.focusInput(); + } + } + + render(parent: HTMLElement): void { + this._scopedContextKeyService?.dispose(); + this._scopedContextKeyService = this._register(this.contextKeyService.createScoped(parent)); + const scopedInstantiationService = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, this.scopedContextKeyService])); + this.widget?.dispose(); + this.widget = this._register( + scopedInstantiationService.createInstance( + ChatWidget, + { resource: true, renderInputOnTop: true, renderStyle: 'compact' }, + { + listForeground: editorForeground, + listBackground: editorBackground, + inputEditorBackground: inputBackground, + resultEditorBackground: editorBackground + })); + this.widget.render(parent); + this.widget.setVisible(true); + this.widget.setDynamicChatTreeItemLayout(2, 600); + this.updateModel(); + if (this._currentQuery) { + this.widget.inputEditor.setSelection({ + startLineNumber: 1, + startColumn: 1, + endLineNumber: 1, + endColumn: this._currentQuery.length + 1 + }); + } + + this.registerListeners(); + } + + private registerListeners(): void { + this._register(this.widget.inputEditor.onDidChangeModelContent((e) => { + this._currentQuery = this.widget.inputEditor.getValue(); + })); + this._register(this.widget.onDidClear(() => this.clear())); + } + + async acceptInput(): Promise { + return this.widget.acceptInput(); + } + + async openChatView(): Promise { + const widget = await this._chatWidgetService.revealViewForProvider(this._options.providerId); + if (!widget?.viewModel || !this.model) { + return; + } + + for (const request of this.model.getRequests()) { + if (request.response?.response.value || request.response?.errorDetails) { + this.chatService.addCompleteRequest(widget.viewModel.sessionId, + request.message as string, + { + message: request.response.response.asString(), + errorDetails: request.response.errorDetails + }); + } else if (request.message) { + + } + } + + const value = this.widget.inputEditor.getValue(); + if (value) { + widget.inputEditor.setValue(value); + } + widget.focusInput(); + } + + setValue(value: string): void { + this.widget.inputEditor.setValue(value); + } + + private updateModel(): void { + this.model ??= this.chatService.startSession(this._options.providerId, CancellationToken.None); + if (!this.model) { + throw new Error('Could not start chat session'); + } + + this.widget.setModel(this.model, { inputValue: this._currentQuery }); + } +} From 6562544dc3cf77eff45113e17162fa2dc60b8dff Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Fri, 18 Aug 2023 16:03:52 -0700 Subject: [PATCH 141/223] Don't use `remoteCredentialsService` when client specified a `secretStorageProvider` (#190781) * Don't use `remoteCredentialsService` when client specified a `secretStorageProvider` This is a temporary fix for https://github.com/microsoft/vscode/issues/190537 until we clean up the last of the CredentialsService (which should be in debt week in Sept). If the client embedder declared a secretStorageProvider, they likely don't want the old credentialsProvider to go over the remote connection... so we use an in-memory provider instead. Fixed https://github.com/microsoft/vscode/issues/190537 * remove stale comment --- .../services/credentials/browser/credentialsService.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/services/credentials/browser/credentialsService.ts b/src/vs/workbench/services/credentials/browser/credentialsService.ts index 2ee077e32a2..80a551f4cc3 100644 --- a/src/vs/workbench/services/credentials/browser/credentialsService.ts +++ b/src/vs/workbench/services/credentials/browser/credentialsService.ts @@ -30,15 +30,18 @@ export class BrowserCredentialsService extends Disposable implements ICredential ) { super(); - if (environmentService.remoteAuthority && !environmentService.options?.credentialsProvider) { + if ( + environmentService.remoteAuthority + && !environmentService.options?.credentialsProvider + && !environmentService.options?.secretStorageProvider + ) { // If we have a remote authority but the embedder didn't provide a credentialsProvider, // we can use the CredentialsService on the remote side const remoteCredentialsService = ProxyChannel.toService(remoteAgentService.getConnection()!.getChannel('credentials')); this.credentialsProvider = remoteCredentialsService; this._secretStoragePrefix = remoteCredentialsService.getSecretStoragePrefix(); } else { - // fall back to InMemoryCredentialsProvider if none was given to us. This should really only be used - // when running tests. + // fall back to InMemoryCredentialsProvider if none was given to us. this.credentialsProvider = environmentService.options?.credentialsProvider ?? new InMemoryCredentialsProvider(); this._secretStoragePrefix = Promise.resolve(this.productService.urlProtocol); } From b2a482303cdb82bbc14a9677cf5e5caaf8e07646 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 18 Aug 2023 16:38:52 -0700 Subject: [PATCH 142/223] xterm@5.3.0-beta.46 --- package.json | 6 +++--- remote/package.json | 6 +++--- remote/web/package.json | 4 ++-- remote/web/yarn.lock | 16 ++++++++-------- remote/yarn.lock | 24 ++++++++++++------------ yarn.lock | 24 ++++++++++++------------ 6 files changed, 40 insertions(+), 40 deletions(-) diff --git a/package.json b/package.json index 97143500048..7c5d9cb3444 100644 --- a/package.json +++ b/package.json @@ -95,14 +95,14 @@ "vscode-oniguruma": "1.7.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "9.0.0", - "xterm": "5.3.0-beta.42", + "xterm": "5.3.0-beta.46", "xterm-addon-canvas": "0.5.0-beta.9", "xterm-addon-image": "0.6.0-beta.2", "xterm-addon-search": "0.13.0-beta.8", "xterm-addon-serialize": "0.11.0-beta.8", "xterm-addon-unicode11": "0.5.0", - "xterm-addon-webgl": "0.16.0-beta.14", - "xterm-headless": "5.3.0-beta.42", + "xterm-addon-webgl": "0.16.0-beta.16", + "xterm-headless": "5.3.0-beta.46", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, diff --git a/remote/package.json b/remote/package.json index 0edbe410766..28cf60a0252 100644 --- a/remote/package.json +++ b/remote/package.json @@ -27,14 +27,14 @@ "vscode-oniguruma": "1.7.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "9.0.0", - "xterm": "5.3.0-beta.42", + "xterm": "5.3.0-beta.46", "xterm-addon-canvas": "0.5.0-beta.9", "xterm-addon-image": "0.6.0-beta.2", "xterm-addon-search": "0.13.0-beta.8", "xterm-addon-serialize": "0.11.0-beta.8", "xterm-addon-unicode11": "0.5.0", - "xterm-addon-webgl": "0.16.0-beta.14", - "xterm-headless": "5.3.0-beta.42", + "xterm-addon-webgl": "0.16.0-beta.16", + "xterm-headless": "5.3.0-beta.46", "yauzl": "^2.9.2", "yazl": "^2.4.3" } diff --git a/remote/web/package.json b/remote/web/package.json index 9c1b107dd78..bd22a24243a 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -11,11 +11,11 @@ "tas-client-umd": "0.1.8", "vscode-oniguruma": "1.7.0", "vscode-textmate": "9.0.0", - "xterm": "5.3.0-beta.42", + "xterm": "5.3.0-beta.46", "xterm-addon-canvas": "0.5.0-beta.9", "xterm-addon-image": "0.6.0-beta.2", "xterm-addon-search": "0.13.0-beta.8", "xterm-addon-unicode11": "0.5.0", - "xterm-addon-webgl": "0.16.0-beta.14" + "xterm-addon-webgl": "0.16.0-beta.16" } } diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index a5a1fd6c1ee..b57b5aa165d 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -88,12 +88,12 @@ xterm-addon-unicode11@0.5.0: resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.5.0.tgz#41c0d96acc1e3bb6c6596eee64e163b6bca74be7" integrity sha512-Jm4/g4QiTxiKiTbYICQgC791ubhIZyoIwxAIgOW8z8HWFNY+lwk+dwaKEaEeGBfM48Vk8fklsUW9u/PlenYEBg== -xterm-addon-webgl@0.16.0-beta.14: - version "0.16.0-beta.14" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.14.tgz#de5bf6c97e31f62d13907a875ffc6be154bc4a02" - integrity sha512-cj+rQWqOeZYJ6JMjMuTTKu3OnP+1D+sqSZPuwZAd6s4NkY4dFXFQlKxJ2yvDHOU/jrgpIYR1c3Cnu0AOWZpS3w== +xterm-addon-webgl@0.16.0-beta.16: + version "0.16.0-beta.16" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.16.tgz#2c31308f8c7f636576720adca529297f8fff3224" + integrity sha512-k0ZSwpBVtxXaqUc29CmlfFgIK7LEYAmwcDC3QVp2ESZo2JHYuQrWFpImjbzbYu0ON6qqcfbX8SnbzRid/XfGUg== -xterm@5.3.0-beta.42: - version "5.3.0-beta.42" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.42.tgz#f0806ebe9b033530c54ac5b03955827fb729c38f" - integrity sha512-3OLXTfwtSy9UKo8Uzm3/kd7qBg0JD/qFpotg8iKJ2aB9zx0fkJnysl4aIuNleUJzgvuxzBUHkcRZUmEw1XCKdw== +xterm@5.3.0-beta.46: + version "5.3.0-beta.46" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.46.tgz#9bd2b2a588c88ae64f1989ca3bf5bd9116554b5a" + integrity sha512-keB3C6sXm56ug1+hylAQiNyZP4YtPgd2W+Gu/ZKwJMk5haD25DEnenBYL++Cl3+sNGYxOGS42QlmYj6Mrq1LPw== diff --git a/remote/yarn.lock b/remote/yarn.lock index 15f25782773..e222314587d 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -902,20 +902,20 @@ xterm-addon-unicode11@0.5.0: resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.5.0.tgz#41c0d96acc1e3bb6c6596eee64e163b6bca74be7" integrity sha512-Jm4/g4QiTxiKiTbYICQgC791ubhIZyoIwxAIgOW8z8HWFNY+lwk+dwaKEaEeGBfM48Vk8fklsUW9u/PlenYEBg== -xterm-addon-webgl@0.16.0-beta.14: - version "0.16.0-beta.14" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.14.tgz#de5bf6c97e31f62d13907a875ffc6be154bc4a02" - integrity sha512-cj+rQWqOeZYJ6JMjMuTTKu3OnP+1D+sqSZPuwZAd6s4NkY4dFXFQlKxJ2yvDHOU/jrgpIYR1c3Cnu0AOWZpS3w== +xterm-addon-webgl@0.16.0-beta.16: + version "0.16.0-beta.16" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.16.tgz#2c31308f8c7f636576720adca529297f8fff3224" + integrity sha512-k0ZSwpBVtxXaqUc29CmlfFgIK7LEYAmwcDC3QVp2ESZo2JHYuQrWFpImjbzbYu0ON6qqcfbX8SnbzRid/XfGUg== -xterm-headless@5.3.0-beta.42: - version "5.3.0-beta.42" - resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.3.0-beta.42.tgz#714d167dc0af010067420956ffca80149dddeca9" - integrity sha512-3xhyy9DaPpadd1iiuKjOn4Zvntnx8WSBMEXPJbrORmy3ZbIwmLf/HYvg7iZfVreSDH+UFlm06uq3ictXpv7YvA== +xterm-headless@5.3.0-beta.46: + version "5.3.0-beta.46" + resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.3.0-beta.46.tgz#3f941f673e3c61aad2705e4b34b42fbe467aed48" + integrity sha512-e/VbZKrfyD1TTxOY5/jah3dUih6q8nTO5v5GQV7YOASp+yyI0XHKiDjLrCyFPQOLTKxCcRutMHvr+CoCcPpcHg== -xterm@5.3.0-beta.42: - version "5.3.0-beta.42" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.42.tgz#f0806ebe9b033530c54ac5b03955827fb729c38f" - integrity sha512-3OLXTfwtSy9UKo8Uzm3/kd7qBg0JD/qFpotg8iKJ2aB9zx0fkJnysl4aIuNleUJzgvuxzBUHkcRZUmEw1XCKdw== +xterm@5.3.0-beta.46: + version "5.3.0-beta.46" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.46.tgz#9bd2b2a588c88ae64f1989ca3bf5bd9116554b5a" + integrity sha512-keB3C6sXm56ug1+hylAQiNyZP4YtPgd2W+Gu/ZKwJMk5haD25DEnenBYL++Cl3+sNGYxOGS42QlmYj6Mrq1LPw== yallist@^4.0.0: version "4.0.0" diff --git a/yarn.lock b/yarn.lock index 17b3729bc6a..b2b8a437cc0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10784,20 +10784,20 @@ xterm-addon-unicode11@0.5.0: resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.5.0.tgz#41c0d96acc1e3bb6c6596eee64e163b6bca74be7" integrity sha512-Jm4/g4QiTxiKiTbYICQgC791ubhIZyoIwxAIgOW8z8HWFNY+lwk+dwaKEaEeGBfM48Vk8fklsUW9u/PlenYEBg== -xterm-addon-webgl@0.16.0-beta.14: - version "0.16.0-beta.14" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.14.tgz#de5bf6c97e31f62d13907a875ffc6be154bc4a02" - integrity sha512-cj+rQWqOeZYJ6JMjMuTTKu3OnP+1D+sqSZPuwZAd6s4NkY4dFXFQlKxJ2yvDHOU/jrgpIYR1c3Cnu0AOWZpS3w== +xterm-addon-webgl@0.16.0-beta.16: + version "0.16.0-beta.16" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.16.tgz#2c31308f8c7f636576720adca529297f8fff3224" + integrity sha512-k0ZSwpBVtxXaqUc29CmlfFgIK7LEYAmwcDC3QVp2ESZo2JHYuQrWFpImjbzbYu0ON6qqcfbX8SnbzRid/XfGUg== -xterm-headless@5.3.0-beta.42: - version "5.3.0-beta.42" - resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.3.0-beta.42.tgz#714d167dc0af010067420956ffca80149dddeca9" - integrity sha512-3xhyy9DaPpadd1iiuKjOn4Zvntnx8WSBMEXPJbrORmy3ZbIwmLf/HYvg7iZfVreSDH+UFlm06uq3ictXpv7YvA== +xterm-headless@5.3.0-beta.46: + version "5.3.0-beta.46" + resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.3.0-beta.46.tgz#3f941f673e3c61aad2705e4b34b42fbe467aed48" + integrity sha512-e/VbZKrfyD1TTxOY5/jah3dUih6q8nTO5v5GQV7YOASp+yyI0XHKiDjLrCyFPQOLTKxCcRutMHvr+CoCcPpcHg== -xterm@5.3.0-beta.42: - version "5.3.0-beta.42" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.42.tgz#f0806ebe9b033530c54ac5b03955827fb729c38f" - integrity sha512-3OLXTfwtSy9UKo8Uzm3/kd7qBg0JD/qFpotg8iKJ2aB9zx0fkJnysl4aIuNleUJzgvuxzBUHkcRZUmEw1XCKdw== +xterm@5.3.0-beta.46: + version "5.3.0-beta.46" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.46.tgz#9bd2b2a588c88ae64f1989ca3bf5bd9116554b5a" + integrity sha512-keB3C6sXm56ug1+hylAQiNyZP4YtPgd2W+Gu/ZKwJMk5haD25DEnenBYL++Cl3+sNGYxOGS42QlmYj6Mrq1LPw== y18n@^3.2.1: version "3.2.2" From 3cc27e7f16de312602f0b92c0d24c488d3200402 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Sun, 20 Aug 2023 09:51:01 -0700 Subject: [PATCH 143/223] Make telemetry more resilient to bad networks (#190810) * More resiliance towards bad network * Spelling --- src/vs/platform/telemetry/node/1dsAppender.ts | 102 +++++++++--------- 1 file changed, 54 insertions(+), 48 deletions(-) diff --git a/src/vs/platform/telemetry/node/1dsAppender.ts b/src/vs/platform/telemetry/node/1dsAppender.ts index 94f0dc30692..63805143d49 100644 --- a/src/vs/platform/telemetry/node/1dsAppender.ts +++ b/src/vs/platform/telemetry/node/1dsAppender.ts @@ -8,15 +8,24 @@ import { streamToBuffer } from 'vs/base/common/buffer'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IRequestOptions } from 'vs/base/parts/request/common/request'; import { IRequestService } from 'vs/platform/request/common/request'; +import * as https from 'https'; import { AbstractOneDataSystemAppender, IAppInsightsCore } from 'vs/platform/telemetry/common/1dsAppender'; +type OnCompleteFunc = (status: number, headers: { [headerName: string]: string }, response?: string) => void; + +interface IResponseData { + headers: { [headerName: string]: string }; + statusCode: number; + responseData: string; +} + /** * Completes a request to submit telemetry to the server utilizing the request service * @param options The options which will be used to make the request * @param requestService The request service * @returns An object containing the headers, statusCode, and responseData */ -async function makeTelemetryRequest(options: IRequestOptions, requestService: IRequestService) { +async function makeTelemetryRequest(options: IRequestOptions, requestService: IRequestService): Promise { const response = await requestService.request(options, CancellationToken.None); const responseData = (await streamToBuffer(response.stream)).toString(); const statusCode = response.res.statusCode ?? 200; @@ -31,30 +40,57 @@ async function makeTelemetryRequest(options: IRequestOptions, requestService: IR /** * Complete a request to submit telemetry to the server utilizing the https module. Only used when the request service is not available * @param options The options which will be used to make the request - * @param httpsModule The https node module * @returns An object containing the headers, statusCode, and responseData */ -function makeLegacyTelemetryRequest(options: IRequestOptions, httpsModule: typeof import('https')) { +async function makeLegacyTelemetryRequest(options: IRequestOptions): Promise { const httpsOptions = { method: options.type, headers: options.headers }; - const req = httpsModule.request(options.url ?? '', httpsOptions, res => { - res.on('data', function (responseData) { - return { - headers: res.headers as Record, - statusCode: res.statusCode ?? 200, - responseData: responseData.toString() - }; + const responsePromise = new Promise((resolve, reject) => { + const req = https.request(options.url ?? '', httpsOptions, res => { + res.on('data', function (responseData) { + resolve({ + headers: res.headers as Record, + statusCode: res.statusCode ?? 200, + responseData: responseData.toString() + }); + }); + // On response with error send status of 0 and a blank response to oncomplete so we can retry events + res.on('error', function (err) { + reject(err); + }); }); - // On response with error send status of 0 and a blank response to oncomplete so we can retry events - res.on('error', function (err) { - throw err; + req.write(options.data, (err) => { + if (err) { + reject(err); + } }); + req.end(); }); - req.write(options.data); - req.end(); - return; + return responsePromise; +} + +async function sendPostAsync(requestService: IRequestService | undefined, payload: IPayloadData, oncomplete: OnCompleteFunc) { + const telemetryRequestData = typeof payload.data === 'string' ? payload.data : new TextDecoder().decode(payload.data); + const requestOptions: IRequestOptions = { + type: 'POST', + headers: { + ...payload.headers, + 'Content-Type': 'application/json', + 'Content-Length': Buffer.byteLength(payload.data).toString() + }, + url: payload.urlString, + data: telemetryRequestData + }; + + try { + const responseData = requestService ? await makeTelemetryRequest(requestOptions, requestService) : await makeLegacyTelemetryRequest(requestOptions); + oncomplete(responseData.statusCode, responseData.headers, responseData.responseData); + } catch { + // If it errors out, send status of 0 and a blank response to oncomplete so we can retry events + oncomplete(0, {}); + } } @@ -67,41 +103,11 @@ export class OneDataSystemAppender extends AbstractOneDataSystemAppender { defaultData: { [key: string]: any } | null, iKeyOrClientFactory: string | (() => IAppInsightsCore), // allow factory function for testing ) { - let httpsModule: typeof import('https') | undefined; - if (!requestService) { - httpsModule = require('https'); - } // Override the way events get sent since node doesn't have XHTMLRequest const customHttpXHROverride: IXHROverride = { sendPOST: (payload: IPayloadData, oncomplete) => { - - const telemetryRequestData = typeof payload.data === 'string' ? payload.data : new TextDecoder().decode(payload.data); - const requestOptions: IRequestOptions = { - type: 'POST', - headers: { - ...payload.headers, - 'Content-Type': 'application/json', - 'Content-Length': Buffer.byteLength(payload.data).toString() - }, - url: payload.urlString, - data: telemetryRequestData - }; - - try { - if (requestService) { - makeTelemetryRequest(requestOptions, requestService).then(({ statusCode, headers, responseData }) => { - oncomplete(statusCode, headers, responseData); - }); - } else { - if (!httpsModule) { - throw new Error('https module is undefined'); - } - makeLegacyTelemetryRequest(requestOptions, httpsModule); - } - } catch { - // If it errors out, send status of 0 and a blank response to oncomplete so we can retry events - oncomplete(0, {}); - } + // Fire off the async request without awaiting it + sendPostAsync(requestService, payload, oncomplete); } }; From f125afbc800ec611f5a9ab1333c769832ce424b3 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Sun, 20 Aug 2023 09:51:31 -0700 Subject: [PATCH 144/223] Move contributing to Command Center to registration time & have Chat show up in Command Center (#190786) --- .../platform/quickinput/common/quickAccess.ts | 13 +++ .../browser/actions/chatQuickInputActions.ts | 15 +-- .../contrib/chat/browser/chat.contribution.ts | 12 -- src/vs/workbench/contrib/chat/browser/chat.ts | 3 +- .../browser/chatContributionServiceImpl.ts | 5 +- .../contrib/chat/browser/chatQuick.ts | 10 +- .../quickaccess/gotoSymbolQuickAccess.ts | 12 +- .../debug/browser/debug.contribution.ts | 6 +- .../browser/quickAccess.contribution.ts | 8 +- .../search/browser/anythingQuickAccess.ts | 104 ++++++++---------- .../search/browser/search.contribution.ts | 6 +- .../tasks/browser/task.contribution.ts | 2 +- 12 files changed, 98 insertions(+), 98 deletions(-) diff --git a/src/vs/platform/quickinput/common/quickAccess.ts b/src/vs/platform/quickinput/common/quickAccess.ts index e5c5dee9173..47dc660daca 100644 --- a/src/vs/platform/quickinput/common/quickAccess.ts +++ b/src/vs/platform/quickinput/common/quickAccess.ts @@ -125,6 +125,19 @@ export interface IQuickAccessProviderHelp { * The command to bring up this quick access provider. */ readonly commandId?: string; + + /** + * The order of help entries in the Command Center. + * Lower values will be placed above higher values. + * No value will hide this help entry from the Command Center. + */ + readonly commandCenterOrder?: number; + + /** + * An optional label to use for the Command Center entry. If not set + * the description will be used instead. + */ + readonly commandCenterLabel?: string; } export interface IQuickAccessProviderDescriptor { diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatQuickInputActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatQuickInputActions.ts index 31c6361beaa..07575215554 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatQuickInputActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatQuickInputActions.ts @@ -7,13 +7,11 @@ import { Codicon } from 'vs/base/common/codicons'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { localize } from 'vs/nls'; import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; -import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { CHAT_CATEGORY } from 'vs/workbench/contrib/chat/browser/actions/chatActions'; import { IQuickChatService } from 'vs/workbench/contrib/chat/browser/chat'; import { CONTEXT_PROVIDER_EXISTS } from 'vs/workbench/contrib/chat/common/chatContextKeys'; -import { IChatService } from 'vs/workbench/contrib/chat/common/chatService'; export const ASK_QUICK_QUESTION_ACTION_ID = 'workbench.action.quickchat.toggle'; export function registerQuickChatActions() { @@ -85,24 +83,13 @@ class QuickChatGlobalAction extends Action2 { linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyCode.KeyI } - }, - menu: { - id: MenuId.LayoutControlMenu, - group: '0_workbench_toggles', - when: ContextKeyExpr.notEquals('config.chat.experimental.defaultMode', 'chatView'), - order: 0 } }); } override run(accessor: ServicesAccessor, query?: string): void { - const chatService = accessor.get(IChatService); const quickChatService = accessor.get(IQuickChatService); - // Grab the first provider and run its command - const info = chatService.getProviderInfos()[0]; - if (info) { - quickChatService.toggle(info.id, query); - } + quickChatService.toggle(undefined, query); } } diff --git a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts index 09d6c46267b..9e6bf370a27 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts @@ -86,18 +86,6 @@ configurationRegistry.registerConfiguration({ type: 'number', description: nls.localize('interactiveSession.editor.lineHeight', "Controls the line height in pixels in chat codeblocks. Use 0 to compute the line height from the font size."), default: 0 - }, - 'chat.experimental.defaultMode': { - type: 'string', - tags: ['experimental'], - enum: ['chatView', 'quickQuestion', 'both'], - enumDescriptions: [ - nls.localize('interactiveSession.defaultMode.chatView', "Use the chat view as the default mode. Displays the chat icon in the Activity Bar."), - nls.localize('interactiveSession.defaultMode.quickQuestion', "Use the quick question as the default mode. Displays the chat icon in the Title Bar."), - nls.localize('interactiveSession.defaultMode.both', "Displays the chat icon in the Activity Bar and the Title Bar which open their respective chat modes.") - ], - description: nls.localize('interactiveSession.defaultMode', "Controls the default mode of the chat experience."), - default: 'chatView' } } }); diff --git a/src/vs/workbench/contrib/chat/browser/chat.ts b/src/vs/workbench/contrib/chat/browser/chat.ts index 8e1c6ee182c..f0587cfbc84 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.ts @@ -35,7 +35,8 @@ export interface IChatWidgetService { export interface IQuickChatService { readonly _serviceBrand: undefined; - toggle(providerId: string, query?: string): void; + enabled: boolean; + toggle(providerId?: string, query?: string): void; focus(): void; close(): void; openInChatView(): void; diff --git a/src/vs/workbench/contrib/chat/browser/chatContributionServiceImpl.ts b/src/vs/workbench/contrib/chat/browser/chatContributionServiceImpl.ts index 66759974390..000bc774029 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContributionServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContributionServiceImpl.ts @@ -126,10 +126,7 @@ export class ChatContributionService implements IChatContributionService { canToggleVisibility: false, canMoveView: true, ctorDescriptor: new SyncDescriptor(ChatViewPane, [{ providerId: providerDescriptor.id }]), - when: ContextKeyExpr.and( - ContextKeyExpr.deserialize(providerDescriptor.when), - ContextKeyExpr.notEquals('config.chat.experimental.defaultMode', 'quickQuestion') - ) + when: ContextKeyExpr.deserialize(providerDescriptor.when) }]; Registry.as(ViewExtensions.ViewsRegistry).registerViews(viewDescriptor, viewContainer); diff --git a/src/vs/workbench/contrib/chat/browser/chatQuick.ts b/src/vs/workbench/contrib/chat/browser/chatQuick.ts index ec90707236b..e43c684102d 100644 --- a/src/vs/workbench/contrib/chat/browser/chatQuick.ts +++ b/src/vs/workbench/contrib/chat/browser/chatQuick.ts @@ -29,6 +29,10 @@ export class QuickChatService implements IQuickChatService { @IInstantiationService private readonly instantiationService: IInstantiationService, ) { } + get enabled(): boolean { + return this.chatService.getProviderInfos().length > 0; + } + get focused(): boolean { const widget = this._input?.widget as HTMLElement; if (!widget) { @@ -37,7 +41,7 @@ export class QuickChatService implements IQuickChatService { return dom.isAncestor(document.activeElement, widget); } - toggle(providerId: string, query?: string | undefined): void { + toggle(providerId?: string, query?: string | undefined): void { // If the input is already shown, hide it. This provides a toggle behavior of the quick pick if (this.focused) { this.close(); @@ -46,7 +50,9 @@ export class QuickChatService implements IQuickChatService { // Check if any providers are available. If not, show nothing // This shouldn't be needed because of the precondition, but just in case - const providerInfo = this.chatService.getProviderInfos().find(info => info.id === providerId); + const providerInfo = providerId + ? this.chatService.getProviderInfos().find(info => info.id === providerId) + : this.chatService.getProviderInfos()[0]; if (!providerInfo) { return; } diff --git a/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess.ts b/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess.ts index d6be4855c01..0a16cc4822a 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess.ts @@ -273,7 +273,15 @@ Registry.as(QuickaccessExtensions.Quickaccess).registerQui contextKey: 'inFileSymbolsPicker', placeholder: localize('gotoSymbolQuickAccessPlaceholder', "Type the name of a symbol to go to."), helpEntries: [ - { description: localize('gotoSymbolQuickAccess', "Go to Symbol in Editor"), prefix: AbstractGotoSymbolQuickAccessProvider.PREFIX, commandId: GotoSymbolAction.ID }, - { description: localize('gotoSymbolByCategoryQuickAccess', "Go to Symbol in Editor by Category"), prefix: AbstractGotoSymbolQuickAccessProvider.PREFIX_BY_CATEGORY } + { + description: localize('gotoSymbolQuickAccess', "Go to Symbol in Editor"), + prefix: AbstractGotoSymbolQuickAccessProvider.PREFIX, + commandId: GotoSymbolAction.ID, + commandCenterOrder: 40 + }, + { + description: localize('gotoSymbolByCategoryQuickAccess', "Go to Symbol in Editor by Category"), + prefix: AbstractGotoSymbolQuickAccessProvider.PREFIX_BY_CATEGORY + } ] }); diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index 3b1eebc2929..c0d1213a3c3 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -77,7 +77,11 @@ Registry.as(QuickAccessExtensions.Quickaccess).registerQui prefix: DEBUG_QUICK_ACCESS_PREFIX, contextKey: 'inLaunchConfigurationsPicker', placeholder: nls.localize('startDebugPlaceholder', "Type the name of a launch configuration to run."), - helpEntries: [{ description: nls.localize('startDebuggingHelp', "Start Debugging"), commandId: SELECT_AND_START_ID }] + helpEntries: [{ + description: nls.localize('startDebuggingHelp', "Start Debugging"), + commandId: SELECT_AND_START_ID, + commandCenterOrder: 50 + }] }); // Register quick access for debug console diff --git a/src/vs/workbench/contrib/quickaccess/browser/quickAccess.contribution.ts b/src/vs/workbench/contrib/quickaccess/browser/quickAccess.contribution.ts index 85a5e04afd2..61a4af6eac8 100644 --- a/src/vs/workbench/contrib/quickaccess/browser/quickAccess.contribution.ts +++ b/src/vs/workbench/contrib/quickaccess/browser/quickAccess.contribution.ts @@ -24,7 +24,11 @@ quickAccessRegistry.registerQuickAccessProvider({ ctor: HelpQuickAccessProvider, prefix: HelpQuickAccessProvider.PREFIX, placeholder: localize('helpQuickAccessPlaceholder', "Type '{0}' to get help on the actions you can take from here.", HelpQuickAccessProvider.PREFIX), - helpEntries: [{ description: localize('helpQuickAccess', "Show all Quick Access Providers") }] + helpEntries: [{ + description: localize('helpQuickAccess', "Show all Quick Access Providers"), + commandCenterOrder: 70, + commandCenterLabel: localize('more', 'More') + }] }); quickAccessRegistry.registerQuickAccessProvider({ @@ -40,7 +44,7 @@ quickAccessRegistry.registerQuickAccessProvider({ prefix: CommandsQuickAccessProvider.PREFIX, contextKey: 'inCommandsPicker', placeholder: localize('commandsQuickAccessPlaceholder', "Type the name of a command to run."), - helpEntries: [{ description: localize('commandsQuickAccess', "Show and Run Commands"), commandId: ShowAllCommandsAction.ID }] + helpEntries: [{ description: localize('commandsQuickAccess', "Show and Run Commands"), commandId: ShowAllCommandsAction.ID, commandCenterOrder: 20 }] }); //#endregion diff --git a/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts b/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts index ccd7471df75..94fc88fe959 100644 --- a/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts +++ b/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts @@ -40,7 +40,7 @@ import { Schemas } from 'vs/base/common/network'; import { IFilesConfigurationService, AutoSaveMode } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { ResourceMap } from 'vs/base/common/map'; import { SymbolsQuickAccessProvider } from 'vs/workbench/contrib/search/browser/symbolsQuickAccess'; -import { AnythingQuickAccessProviderRunOptions, DefaultQuickAccessFilterValue } from 'vs/platform/quickinput/common/quickAccess'; +import { AnythingQuickAccessProviderRunOptions, DefaultQuickAccessFilterValue, Extensions, IQuickAccessRegistry } from 'vs/platform/quickinput/common/quickAccess'; import { IWorkbenchQuickAccessConfiguration } from 'vs/workbench/browser/quickaccess'; import { GotoSymbolQuickAccessProvider } from 'vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; @@ -52,11 +52,11 @@ import { Codicon } from 'vs/base/common/codicons'; import { ThemeIcon } from 'vs/base/common/themables'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { stripIcons } from 'vs/base/common/iconLabels'; -import { HelpQuickAccessProvider } from 'vs/platform/quickinput/browser/helpQuickAccess'; -import { CommandsQuickAccessProvider } from 'vs/workbench/contrib/quickaccess/browser/commandsQuickAccess'; -import { DEBUG_QUICK_ACCESS_PREFIX } from 'vs/workbench/contrib/debug/browser/debugCommands'; -import { TasksQuickAccessProvider } from 'vs/workbench/contrib/tasks/browser/tasksQuickAccess'; import { Lazy } from 'vs/base/common/lazy'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { ASK_QUICK_QUESTION_ACTION_ID } from 'vs/workbench/contrib/chat/browser/actions/chatQuickInputActions'; +import { IQuickChatService } from 'vs/workbench/contrib/chat/browser/chat'; interface IAnythingQuickPickItem extends IPickerQuickAccessItem, IQuickPickItemWithResource { } @@ -187,6 +187,8 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider Registry.as(Extensions.Quickaccess)); private getHelpPicks(query: IPreparedQuery, token: CancellationToken, runOptions?: AnythingQuickAccessProviderRunOptions): IAnythingQuickPickItem[] { if (query.normalized) { return []; // If there's a filter, we don't show the help } - type IHelpAnythingQuickPickItem = IAnythingQuickPickItem & { prefix: string }; - const providers: Array = this.helpQuickAccess.getQuickAccessProviders(); - const mapOfProviders = new Map(); - for (const provider of providers) { - mapOfProviders.set(provider.prefix, provider); + type IHelpAnythingQuickPickItem = IAnythingQuickPickItem & { commandCenterOrder: number }; + const providers: IHelpAnythingQuickPickItem[] = this.lazyRegistry.value.getQuickAccessProviders() + .filter(p => p.helpEntries.some(h => h.commandCenterOrder !== undefined)) + .flatMap(provider => provider.helpEntries + .filter(h => h.commandCenterOrder !== undefined) + .map(helpEntry => { + const providerSpecificOptions: AnythingQuickAccessProviderRunOptions | undefined = { + ...runOptions, + includeHelp: provider.prefix === AnythingQuickAccessProvider.PREFIX ? false : runOptions?.includeHelp + }; + + const label = helpEntry.commandCenterLabel ?? helpEntry.description!; + return { + label, + description: helpEntry.prefix ?? provider.prefix, + commandCenterOrder: helpEntry.commandCenterOrder!, + keybinding: helpEntry.commandId ? this.keybindingService.lookupKeybinding(helpEntry.commandId) : undefined, + ariaLabel: localize('helpPickAriaLabel', "{0}, {1}", label, helpEntry.description), + accept: () => { + this.quickInputService.quickAccess.show(provider.prefix, { + preserveValue: true, + providerOptions: providerSpecificOptions + }); + } + }; + })); + + // TODO: There has to be a better place for this, but it's the first time we are adding a non-quick access provider + // to the command center, so for now, let's do this. + if (this.quickChatService.enabled) { + providers.push({ + label: localize('chat', "Open Quick Chat"), + commandCenterOrder: 30, + keybinding: this.keybindingService.lookupKeybinding(ASK_QUICK_QUESTION_ACTION_ID), + accept: () => this.quickChatService.toggle() + }); } - const importantProviders: Array = []; - const AddProvider = (prefix: string, modifications: Partial = {}) => { - if (mapOfProviders.has(prefix)) { - const provider = mapOfProviders.get(prefix)!; - - // We swap the label and description in this to emphasize the ability - // not the prefix. - provider.label = provider.description!; - provider.description = provider.prefix; - - // If the user chooses 'Go to File' the help should go away as if they were - // entering a new mode - const providerSpecificOptions: AnythingQuickAccessProviderRunOptions | undefined = { - ...runOptions, - includeHelp: provider.prefix === AnythingQuickAccessProvider.PREFIX ? false : runOptions?.includeHelp - }; - - importantProviders.push({ - ...mapOfProviders.get(prefix)!, - ...modifications, - accept: () => { - this.quickInputService.quickAccess.show(provider.prefix, { - preserveValue: true, - providerOptions: providerSpecificOptions - }); - } - }); - } - }; - - // TODO@TylerLeonhardt ideally this hardcoded list and hardcoded dependency moves - // into a provider model where when I register a quick access provider I can enlist - // for showing up in command center - - // Acts as the ordering too - AddProvider(AnythingQuickAccessProvider.PREFIX); - AddProvider(CommandsQuickAccessProvider.PREFIX); - AddProvider(GotoSymbolQuickAccessProvider.PREFIX); - AddProvider(DEBUG_QUICK_ACCESS_PREFIX); - AddProvider(TasksQuickAccessProvider.PREFIX); - AddProvider(HelpQuickAccessProvider.PREFIX, { - // More concise - label: localize('more', 'More') - }); - - return importantProviders; + return providers.sort((a, b) => a.commandCenterOrder - b.commandCenterOrder); } //#endregion diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index ec2d3dd0103..d868e5f6575 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -107,7 +107,11 @@ quickAccessRegistry.registerQuickAccessProvider({ prefix: AnythingQuickAccessProvider.PREFIX, placeholder: nls.localize('anythingQuickAccessPlaceholder', "Search files by name (append {0} to go to line or {1} to go to symbol)", AbstractGotoLineQuickAccessProvider.PREFIX, GotoSymbolQuickAccessProvider.PREFIX), contextKey: defaultQuickAccessContextKeyValue, - helpEntries: [{ description: nls.localize('anythingQuickAccess', "Go to File"), commandId: 'workbench.action.quickOpen' }] + helpEntries: [{ + description: nls.localize('anythingQuickAccess', "Go to File"), + commandId: 'workbench.action.quickOpen', + commandCenterOrder: 10 + }] }); quickAccessRegistry.registerQuickAccessProvider({ diff --git a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts index d20bb9d4370..27bdc71350a 100644 --- a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts +++ b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts @@ -408,7 +408,7 @@ quickAccessRegistry.registerQuickAccessProvider({ prefix: TasksQuickAccessProvider.PREFIX, contextKey: tasksPickerContextKey, placeholder: nls.localize('tasksQuickAccessPlaceholder', "Type the name of a task to run."), - helpEntries: [{ description: nls.localize('tasksQuickAccessHelp', "Run Task") }] + helpEntries: [{ description: nls.localize('tasksQuickAccessHelp', "Run Task"), commandCenterOrder: 60 }] }); // tasks.json validation From 9f7783206a19c2b08fbfce27e6d1b17fd1d308ec Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Sat, 19 Aug 2023 13:15:46 +0200 Subject: [PATCH 145/223] Fixes #https://github.com/microsoft/monaco-editor/issues/4129 --- .../diffEditorWidget2/diffEditorViewModel.ts | 53 +++++++++---------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts index 4833d3de651..448ccb5b27e 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts @@ -105,40 +105,36 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo this._register(model.modified.onDidChangeContent((e) => { const diff = this._diff.get(); - if (!diff) { - return; - } - - const textEdits = TextEditInfo.fromModelContentChanges(e.changes); - const result = applyModifiedEdits(this._lastDiff!, textEdits, model.original, model.modified); - if (result) { - this._lastDiff = result; - transaction(tx => { - this._diff.set(DiffState.fromDiffResult(this._lastDiff!), tx); - updateUnchangedRegions(result, tx); - const currentSyncedMovedText = this.syncedMovedTexts.get(); - this.syncedMovedTexts.set(currentSyncedMovedText ? this._lastDiff!.moves.find(m => m.lineRangeMapping.modified.intersect(currentSyncedMovedText.lineRangeMapping.modified)) : undefined, tx); - }); + if (diff) { + const textEdits = TextEditInfo.fromModelContentChanges(e.changes); + const result = applyModifiedEdits(this._lastDiff!, textEdits, model.original, model.modified); + if (result) { + this._lastDiff = result; + transaction(tx => { + this._diff.set(DiffState.fromDiffResult(this._lastDiff!), tx); + updateUnchangedRegions(result, tx); + const currentSyncedMovedText = this.syncedMovedTexts.get(); + this.syncedMovedTexts.set(currentSyncedMovedText ? this._lastDiff!.moves.find(m => m.lineRangeMapping.modified.intersect(currentSyncedMovedText.lineRangeMapping.modified)) : undefined, tx); + }); + } } debouncer.schedule(); })); this._register(model.original.onDidChangeContent((e) => { const diff = this._diff.get(); - if (!diff) { - return; - } - - const textEdits = TextEditInfo.fromModelContentChanges(e.changes); - const result = applyOriginalEdits(this._lastDiff!, textEdits, model.original, model.modified); - if (result) { - this._lastDiff = result; - transaction(tx => { - this._diff.set(DiffState.fromDiffResult(this._lastDiff!), tx); - updateUnchangedRegions(result, tx); - const currentSyncedMovedText = this.syncedMovedTexts.get(); - this.syncedMovedTexts.set(currentSyncedMovedText ? this._lastDiff!.moves.find(m => m.lineRangeMapping.modified.intersect(currentSyncedMovedText.lineRangeMapping.modified)) : undefined, tx); - }); + if (diff) { + const textEdits = TextEditInfo.fromModelContentChanges(e.changes); + const result = applyOriginalEdits(this._lastDiff!, textEdits, model.original, model.modified); + if (result) { + this._lastDiff = result; + transaction(tx => { + this._diff.set(DiffState.fromDiffResult(this._lastDiff!), tx); + updateUnchangedRegions(result, tx); + const currentSyncedMovedText = this.syncedMovedTexts.get(); + this.syncedMovedTexts.set(currentSyncedMovedText ? this._lastDiff!.moves.find(m => m.lineRangeMapping.modified.intersect(currentSyncedMovedText.lineRangeMapping.modified)) : undefined, tx); + }); + } } debouncer.schedule(); @@ -175,7 +171,6 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo result = applyOriginalEdits(result, originalTextEditInfos, model.original, model.modified) ?? result; result = applyModifiedEdits(result, modifiedTextEditInfos, model.original, model.modified) ?? result; - transaction(tx => { updateUnchangedRegions(result, tx); From afa5e38f0ce3fb98c0ffc206468de46c80785aba Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 21 Aug 2023 11:12:51 +0200 Subject: [PATCH 146/223] Fixes #190727 --- src/vs/workbench/api/common/extHostLanguageFeatures.ts | 3 +-- .../vscode.proposed.inlineCompletionsAdditions.d.ts | 5 ----- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 7a32009ac07..cd8b2c1de2c 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -1217,7 +1217,6 @@ class InlineCompletionAdapter extends InlineCompletionAdapterBase { const normalizedResult = Array.isArray(result) ? result : result.items; const commands = this._isAdditionsProposedApiEnabled ? Array.isArray(result) ? [] : result.commands || [] : []; - const suppressSuggestions = this._isAdditionsProposedApiEnabled && !Array.isArray(result) ? result.suppressSuggestions : undefined; const enableForwardStability = this._isAdditionsProposedApiEnabled && !Array.isArray(result) ? result.enableForwardStability : undefined; let disposableStore: DisposableStore | undefined = undefined; @@ -1255,7 +1254,7 @@ class InlineCompletionAdapter extends InlineCompletionAdapterBase { } return this._commands.toInternal(c, disposableStore); }), - suppressSuggestions, + suppressSuggestions: false, enableForwardStability, }; } diff --git a/src/vscode-dts/vscode.proposed.inlineCompletionsAdditions.d.ts b/src/vscode-dts/vscode.proposed.inlineCompletionsAdditions.d.ts index c38c4e23671..88dc3ef60c7 100644 --- a/src/vscode-dts/vscode.proposed.inlineCompletionsAdditions.d.ts +++ b/src/vscode-dts/vscode.proposed.inlineCompletionsAdditions.d.ts @@ -62,11 +62,6 @@ declare module 'vscode' { */ commands?: Command[]; - /** - * When set, overrides the user setting of `editor.inlineSuggest.suppressSuggestions`. - */ - suppressSuggestions?: boolean; - /** * When set and the user types a suggestion without derivating from it, the inline suggestion is not updated. * Defaults to false (might change). From c44c644cf38d9263cf730249766266866af1e5fc Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 21 Aug 2023 12:13:09 +0200 Subject: [PATCH 147/223] [folding] Fold all regions except selected is misnamed (#190732) --- src/vs/editor/contrib/folding/browser/folding.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/vs/editor/contrib/folding/browser/folding.ts b/src/vs/editor/contrib/folding/browser/folding.ts index ee79563f1f5..6134f4fa401 100644 --- a/src/vs/editor/contrib/folding/browser/folding.ts +++ b/src/vs/editor/contrib/folding/browser/folding.ts @@ -908,13 +908,13 @@ class UnfoldAllRegionsAction extends FoldingAction { } } -class FoldAllRegionsExceptAction extends FoldingAction { +class FoldAllExceptAction extends FoldingAction { constructor() { super({ id: 'editor.foldAllExcept', - label: nls.localize('foldAllExcept.label', "Fold All Regions Except Selected"), - alias: 'Fold All Regions Except Selected', + label: nls.localize('foldAllExcept.label', "Fold All Except Selected"), + alias: 'Fold All Except Selected', precondition: CONTEXT_FOLDING_ENABLED, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, @@ -931,13 +931,13 @@ class FoldAllRegionsExceptAction extends FoldingAction { } -class UnfoldAllRegionsExceptAction extends FoldingAction { +class UnfoldAllExceptAction extends FoldingAction { constructor() { super({ id: 'editor.unfoldAllExcept', - label: nls.localize('unfoldAllExcept.label', "Unfold All Regions Except Selected"), - alias: 'Unfold All Regions Except Selected', + label: nls.localize('unfoldAllExcept.label', "Unfold All Except Selected"), + alias: 'Unfold All Except Selected', precondition: CONTEXT_FOLDING_ENABLED, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, @@ -1194,8 +1194,8 @@ registerEditorAction(UnfoldAllAction); registerEditorAction(FoldAllBlockCommentsAction); registerEditorAction(FoldAllRegionsAction); registerEditorAction(UnfoldAllRegionsAction); -registerEditorAction(FoldAllRegionsExceptAction); -registerEditorAction(UnfoldAllRegionsExceptAction); +registerEditorAction(FoldAllExceptAction); +registerEditorAction(UnfoldAllExceptAction); registerEditorAction(ToggleFoldAction); registerEditorAction(GotoParentFoldAction); registerEditorAction(GotoPreviousFoldAction); From 829932cb62b2216d8311c2981918b92b13b8a026 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 21 Aug 2023 12:13:51 +0200 Subject: [PATCH 148/223] improve json schema settings descriptions (#190735) --- extensions/json-language-features/package.nls.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/json-language-features/package.nls.json b/extensions/json-language-features/package.nls.json index 4091dad211a..df68b3f8eac 100644 --- a/extensions/json-language-features/package.nls.json +++ b/extensions/json-language-features/package.nls.json @@ -2,9 +2,9 @@ "displayName": "JSON Language Features", "description": "Provides rich language support for JSON files.", "json.schemas.desc": "Associate schemas to JSON files in the current project.", - "json.schemas.url.desc": "A URL to a schema or a relative path to a schema in the current directory", - "json.schemas.fileMatch.desc": "An array of file patterns to match against when resolving JSON files to schemas. `*` can be used as a wildcard. Exclusion patterns can also be defined and start with '!'. A file matches when there is at least one matching pattern and the last matching pattern is not an exclusion pattern.", - "json.schemas.fileMatch.item.desc": "A file pattern that can contain '*' to match against when resolving JSON files to schemas.", + "json.schemas.url.desc": "A URL or absolute file path to a schema. Can be a relative path in workspace and workspace folder settings.", + "json.schemas.fileMatch.desc": "An array of file patterns to match against when resolving JSON files to schemas. `*` and '**' can be used as a wildcard. Exclusion patterns can also be defined and start with '!'. A file matches when there is at least one matching pattern and the last matching pattern is not an exclusion pattern.", + "json.schemas.fileMatch.item.desc": "A file pattern that can contain '*' and '**' to match against when resolving JSON files to schemas. When beginning with '!', it defines an exclusion pattern.", "json.schemas.schema.desc": "The schema definition for the given URL. The schema only needs to be provided to avoid accesses to the schema URL.", "json.format.enable.desc": "Enable/disable default JSON formatter", "json.format.keepLines.desc" : "Keep all existing new lines when formatting.", From 69b2435e14e5dbd442df58efcc72c28ad81e1ac2 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 21 Aug 2023 12:14:11 +0200 Subject: [PATCH 149/223] set a user agent when attempting to retrieve $schema JSON Schemas (#190726) --- .../client/src/node/jsonClientMain.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/extensions/json-language-features/client/src/node/jsonClientMain.ts b/extensions/json-language-features/client/src/node/jsonClientMain.ts index 10895276e1f..457a40f6a74 100644 --- a/extensions/json-language-features/client/src/node/jsonClientMain.ts +++ b/extensions/json-language-features/client/src/node/jsonClientMain.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ExtensionContext, OutputChannel, window, workspace, l10n } from 'vscode'; +import { ExtensionContext, OutputChannel, window, workspace, l10n, env } from 'vscode'; import { startClient, LanguageClientConstructor, SchemaRequestService, languageServerDescription } from '../jsonClient'; import { ServerOptions, TransportKind, LanguageClientOptions, LanguageClient, BaseLanguageClient } from 'vscode-languageclient/node'; @@ -129,7 +129,10 @@ async function getSchemaRequestService(context: ExtensionContext, log: Log): Pro const isXHRResponse = (error: any): error is XHRResponse => typeof error?.status === 'number'; const request = async (uri: string, etag?: string): Promise => { - const headers: Headers = { 'Accept-Encoding': 'gzip, deflate' }; + const headers: Headers = { + 'Accept-Encoding': 'gzip, deflate', + 'User-Agent': `${env.appName} (${env.appHost})` + }; if (etag) { headers['If-None-Match'] = etag; } From c3a4fbbe8f0c285eb1bb1a3c737b7b368a41e333 Mon Sep 17 00:00:00 2001 From: Ulugbek Abdullaev Date: Mon, 21 Aug 2023 13:00:50 +0200 Subject: [PATCH 150/223] add `vscode.executeMappedEditsProvider` command & use it to have integration tests for mapped-edits service --- build/lib/i18n.resources.json | 4 + .../singlefolder-tests/mappedEdits.test.ts | 102 ++++++++++++++++++ .../vscode-api-tests/testWorkspace/myFile.ts | 3 + .../api/browser/mainThreadMappedEdits.ts | 2 +- .../api/common/extHostApiCommands.ts | 21 +++- .../workbench/api/common/extHostCommands.ts | 10 ++ .../api/common/extHostTypeConverters.ts | 25 +++++ .../common/mappedEdits.contribution.ts | 49 +++++++++ .../mappedEdits/common/mappedEdits.ts | 8 +- src/vs/workbench/workbench.common.main.ts | 3 + 10 files changed, 221 insertions(+), 6 deletions(-) create mode 100644 extensions/vscode-api-tests/src/singlefolder-tests/mappedEdits.test.ts create mode 100644 extensions/vscode-api-tests/testWorkspace/myFile.ts create mode 100644 src/vs/workbench/contrib/mappedEdits/common/mappedEdits.contribution.ts diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json index f83225cc974..f7f31337124 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -58,6 +58,10 @@ "name": "vs/workbench/contrib/commands", "project": "vscode-workbench" }, + { + "name": "vs/workbench/contrib/mappedEdits", + "project": "vscode-workbench" + }, { "name": "vs/workbench/contrib/comments", "project": "vscode-workbench" diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/mappedEdits.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/mappedEdits.test.ts new file mode 100644 index 00000000000..f4fc349f115 --- /dev/null +++ b/extensions/vscode-api-tests/src/singlefolder-tests/mappedEdits.test.ts @@ -0,0 +1,102 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as path from 'path'; +import * as vscode from 'vscode'; +import * as assert from 'assert'; + +suite('mapped edits provider', () => { + + test('mapped edits does not provide edits for unregistered langs', async function () { + + const uri = vscode.Uri.file(path.join(vscode.workspace.rootPath || '', './myFile.ts')); + + const tsDocFilter = [{ language: 'json' }]; + + const r1 = vscode.chat.registerMappedEditsProvider(tsDocFilter, { + provideMappedEdits: (_doc: vscode.TextDocument, codeBlocks: string[], context: vscode.MappedEditsContext, _token: vscode.CancellationToken) => { + + assert(context.selections.length === 1); + assert(context.related.length === 1); + assert('uri' in context.related[0] && 'range' in context.related[0]); + + const edit = new vscode.WorkspaceEdit(); + const text = codeBlocks.join('\n//----\n'); + edit.replace(uri, context.selections[0], text); + return edit; + } + }); + await vscode.workspace.openTextDocument(uri); + const result = await vscode.commands.executeCommand>( + 'vscode.executeMappedEditsProvider', + uri, + [ + '// hello', + `function foo() {\n\treturn 1;\n}`, + ], + { + selections: [new vscode.Selection(0, 0, 1, 0)], + related: [ + { + uri, + range: new vscode.Range(new vscode.Position(0, 0), new vscode.Position(1, 0)) + } + ] + } + ); + r1.dispose(); + + assert(result === null, 'returned null'); + }); + + test('mapped edits provides a single edit replacing the selection', async function () { + + const uri = vscode.Uri.file(path.join(vscode.workspace.rootPath || '', './myFile.ts')); + + const tsDocFilter = [{ language: 'typescript' }]; + + const r1 = vscode.chat.registerMappedEditsProvider(tsDocFilter, { + provideMappedEdits: (_doc: vscode.TextDocument, codeBlocks: string[], context: vscode.MappedEditsContext, _token: vscode.CancellationToken) => { + + assert(context.selections.length === 1); + assert(context.related.length === 1); + assert('uri' in context.related[0] && 'range' in context.related[0]); + + const edit = new vscode.WorkspaceEdit(); + const text = codeBlocks.join('\n//----\n'); + edit.replace(uri, context.selections[0], text); + return edit; + } + }); + + await vscode.workspace.openTextDocument(uri); + const result = await vscode.commands.executeCommand>( + 'vscode.executeMappedEditsProvider', + uri, + [ + '// hello', + `function foo() {\n\treturn 1;\n}`, + ], + { + selections: [new vscode.Selection(0, 0, 1, 0)], + related: [ + { + uri, + range: new vscode.Range(new vscode.Position(0, 0), new vscode.Position(1, 0)) + } + ] + } + ); + r1.dispose(); + + assert(result, 'non null response'); + const edits = result.get(uri); + assert(edits.length === 1); + assert(edits[0].range.start.line === 0); + assert(edits[0].range.start.character === 0); + assert(edits[0].range.end.line === 1); + assert(edits[0].range.end.character === 0); + }); +}); diff --git a/extensions/vscode-api-tests/testWorkspace/myFile.ts b/extensions/vscode-api-tests/testWorkspace/myFile.ts new file mode 100644 index 00000000000..2a2a4927869 --- /dev/null +++ b/extensions/vscode-api-tests/testWorkspace/myFile.ts @@ -0,0 +1,3 @@ +// 1 +// 2 +// 3 diff --git a/src/vs/workbench/api/browser/mainThreadMappedEdits.ts b/src/vs/workbench/api/browser/mainThreadMappedEdits.ts index a2a34f4155d..a268da7bbe1 100644 --- a/src/vs/workbench/api/browser/mainThreadMappedEdits.ts +++ b/src/vs/workbench/api/browser/mainThreadMappedEdits.ts @@ -6,7 +6,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { reviveWorkspaceEditDto } from 'vs/workbench/api/browser/mainThreadBulkEdits'; -import { ExtHostContext, ExtHostMappedEditsShape, IDocumentFilterDto, IMappedEditsContextDto, MainContext, MainThreadMappedEditsShape } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostContext, ExtHostMappedEditsShape, IDocumentFilterDto, MainContext, MainThreadMappedEditsShape } from 'vs/workbench/api/common/extHost.protocol'; import { IMappedEditsProvider, IMappedEditsService } from 'vs/workbench/services/mappedEdits/common/mappedEdits'; import { IExtHostContext, extHostNamedCustomer } from 'vs/workbench/services/extensions/common/extHostCustomers'; diff --git a/src/vs/workbench/api/common/extHostApiCommands.ts b/src/vs/workbench/api/common/extHostApiCommands.ts index 68db008d1b1..fdb8ffa2cd6 100644 --- a/src/vs/workbench/api/common/extHostApiCommands.ts +++ b/src/vs/workbench/api/common/extHostApiCommands.ts @@ -464,7 +464,26 @@ const newCommands: ApiCommand[] = [ new ApiCommandArgument('value', 'The context key value', () => true, v => v), ], ApiCommandResult.Void - ) + ), + // --- mapped edits + new ApiCommand( + 'vscode.executeMappedEditsProvider', '_executeMappedEditsProvider', 'Execute Mapped Edits Provider', + [ + ApiCommandArgument.Uri, + ApiCommandArgument.StringArray, + new ApiCommandArgument( + 'MappedEditsContext', + 'Mapped Edits Context', + (v: unknown) => typeConverters.MappedEditsContext.is(v), + (v: vscode.MappedEditsContext) => typeConverters.MappedEditsContext.from(v) + ) + ], + new ApiCommandResult( + 'A promise that resolves to a workspace edit or null', + (value) => { + return typeConverters.WorkspaceEdit.to(value); + }) + ), ]; //#endregion diff --git a/src/vs/workbench/api/common/extHostCommands.ts b/src/vs/workbench/api/common/extHostCommands.ts index e8fc48765c7..fd3ba6c52b9 100644 --- a/src/vs/workbench/api/common/extHostCommands.ts +++ b/src/vs/workbench/api/common/extHostCommands.ts @@ -444,6 +444,16 @@ export class ApiCommandArgument { static readonly Selection = new ApiCommandArgument('selection', 'A selection in a text document', v => extHostTypes.Selection.isSelection(v), extHostTypeConverter.Selection.from); static readonly Number = new ApiCommandArgument('number', '', v => typeof v === 'number', v => v); static readonly String = new ApiCommandArgument('string', '', v => typeof v === 'string', v => v); + static readonly StringArray = ApiCommandArgument.Arr(ApiCommandArgument.String); + + static Arr(element: ApiCommandArgument) { + return new ApiCommandArgument( + `${element.name}_array`, + `Array of ${element.name}, ${element.description}`, + (v: unknown) => Array.isArray(v) && v.every(e => element.validate(e)), + (v: T[]) => v.map(e => element.convert(e)) + ); + } static readonly CallHierarchyItem = new ApiCommandArgument('item', 'A call hierarchy item', v => v instanceof extHostTypes.CallHierarchyItem, extHostTypeConverter.CallHierarchyItem.from); static readonly TypeHierarchyItem = new ApiCommandArgument('item', 'A type hierarchy item', v => v instanceof extHostTypes.TypeHierarchyItem, extHostTypeConverter.TypeHierarchyItem.from); diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index d0e1bc99d4a..0f7efee2be4 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -32,6 +32,7 @@ import { IMarkerData, IRelatedInformation, MarkerSeverity, MarkerTag } from 'vs/ import { ProgressLocation as MainProgressLocation } from 'vs/platform/progress/common/progress'; import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; import { getPrivateApiFor } from 'vs/workbench/api/common/extHostTestingPrivateApi'; +import type * as mappedEdits from 'vs/workbench/services/mappedEdits/common/mappedEdits'; import { DEFAULT_EDITOR_ASSOCIATION, SaveReason } from 'vs/workbench/common/editor'; import { IViewBadge } from 'vs/workbench/common/views'; import { IChatFollowup, IChatReplyFollowup, IChatResponseCommandFollowup } from 'vs/workbench/contrib/chat/common/chatService'; @@ -1563,6 +1564,30 @@ export namespace LanguageSelector { } } +export namespace MappedEditsContext { + + export function is(v: unknown): v is vscode.MappedEditsContext { + return (!!v && + typeof v === 'object' && + 'selections' in v && + Array.isArray(v.selections) && + v.selections.every(s => s instanceof types.Selection) && + 'related' in v && + Array.isArray(v.related) && + v.related.every(e => e && typeof e === 'object' && URI.isUri(e.uri) && e.range instanceof types.Range)); + } + + export function from(context: vscode.MappedEditsContext): mappedEdits.MappedEditsContext { + return { + selections: context.selections.map(s => Selection.from(s)), + related: context.related.map(r => ({ + uri: URI.from(r.uri), + range: Range.from(r.range) + })) + }; + } +} + export namespace NotebookRange { export function from(range: vscode.NotebookRange): ICellRange { diff --git a/src/vs/workbench/contrib/mappedEdits/common/mappedEdits.contribution.ts b/src/vs/workbench/contrib/mappedEdits/common/mappedEdits.contribution.ts new file mode 100644 index 00000000000..469d369d14a --- /dev/null +++ b/src/vs/workbench/contrib/mappedEdits/common/mappedEdits.contribution.ts @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { URI } from 'vs/base/common/uri'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import * as nls from 'vs/nls'; +import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IMappedEditsService, MappedEditsContext } from 'vs/workbench/services/mappedEdits/common/mappedEdits'; + +class ExecuteMappedEditsProvider extends Action2 { + + static readonly ID = '_executeMappedEditsProvider'; + + constructor() { + super({ + id: ExecuteMappedEditsProvider.ID, + title: { value: nls.localize('executeMappedEditsProvider', "Execute Mapped Edits Provider"), original: '' }, + f1: false, + description: { + description: nls.localize('executeMappedEditsProvider.description', "Executes Mapped Edits Provider and returns the corresponding WorkspaceEdit or null if no edits are provided."), + args: [ + // FIXME@ulugbekna + ] + } + }); + } + + async run(accessor: ServicesAccessor, documentUri: URI, codeBlocks: string[], context: MappedEditsContext) { + + const mappedEditsService = accessor.get(IMappedEditsService); + const modelService = accessor.get(ITextModelService); + + const document = await modelService.createModelReference(documentUri); + + const cancellationTokenSource = new CancellationTokenSource(); + + const res = await mappedEditsService.provideMappedEdits(document.object.textEditorModel, codeBlocks, context, cancellationTokenSource.token); + + document.dispose(); + + return res; + } +} + +registerAction2(ExecuteMappedEditsProvider); diff --git a/src/vs/workbench/services/mappedEdits/common/mappedEdits.ts b/src/vs/workbench/services/mappedEdits/common/mappedEdits.ts index 8e58c5c1031..9a60304881c 100644 --- a/src/vs/workbench/services/mappedEdits/common/mappedEdits.ts +++ b/src/vs/workbench/services/mappedEdits/common/mappedEdits.ts @@ -10,16 +10,16 @@ import { LanguageSelector } from 'vs/editor/common/languageSelector'; import { IDisposable } from 'vs/base/common/lifecycle'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { WorkspaceEdit } from 'vs/editor/common/languages'; -import { Selection } from 'vs/editor/common/core/selection'; -import { Range } from 'vs/editor/common/core/range'; +import { ISelection } from 'vs/editor/common/core/selection'; +import { IRange } from 'vs/editor/common/core/range'; export interface RelatedContextItem { readonly uri: URI; - readonly range: Range; + readonly range: IRange; } export interface MappedEditsContext { - selections: Selection[]; + selections: ISelection[]; /** * If there's no context, the array should be empty. It's also empty until we figure out how to compute this or retrieve from an extension (eg, copilot chat) diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index 81170dfd99e..d30cd73ec49 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -227,6 +227,9 @@ import 'vs/workbench/contrib/markers/browser/markers.contribution'; // Merge Editor import 'vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution'; +// Mapped Edits +import 'vs/workbench/contrib/mappedEdits/common/mappedEdits.contribution'; + // Commands import 'vs/workbench/contrib/commands/common/commands.contribution'; From 9336d2127f8f6b9f15af321676937ff25006add1 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 21 Aug 2023 14:48:52 +0200 Subject: [PATCH 151/223] Fix some lifecycle bugs --- .../browser/stickyScrollModelProvider.ts | 22 +++++++++---------- .../browser/stickyScrollProvider.ts | 17 ++++++-------- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollModelProvider.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollModelProvider.ts index 13051e80058..f1f50f9ced7 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollModelProvider.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollModelProvider.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { DisposableStore } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { OutlineElement, OutlineGroup, OutlineModel } from 'vs/editor/contrib/documentSymbols/browser/outlineModel'; @@ -45,18 +45,20 @@ export interface IStickyModelProvider { update(textModel: ITextModel, textModelVersionId: number, token: CancellationToken): Promise; } -export class StickyModelProvider implements IStickyModelProvider { +export class StickyModelProvider extends Disposable implements IStickyModelProvider { private _modelProviders: IStickyModelCandidateProvider[] = []; private _modelPromise: CancelablePromise | null = null; - private _updateScheduler: Delayer = new Delayer(300); - private readonly _store: DisposableStore; + private _updateScheduler: Delayer = this._register(new Delayer(300)); + private readonly _updateOperation: DisposableStore = this._register(new DisposableStore()); constructor( private readonly _editor: ICodeEditor, @ILanguageConfigurationService readonly _languageConfigurationService: ILanguageConfigurationService, @ILanguageFeaturesService readonly _languageFeaturesService: ILanguageFeaturesService, - defaultModel: string) { + defaultModel: string + ) { + super(); const stickyModelFromCandidateOutlineProvider = new StickyModelFromCandidateOutlineProvider(_languageFeaturesService); const stickyModelFromSyntaxFoldingProvider = new StickyModelFromCandidateSyntaxFoldingProvider(this._editor, _languageFeaturesService); @@ -76,8 +78,6 @@ export class StickyModelProvider implements IStickyModelProvider { this._modelProviders.push(stickyModelFromIndentationFoldingProvider); break; } - - this._store = new DisposableStore(); } private _cancelModelPromise(): void { @@ -89,11 +89,11 @@ export class StickyModelProvider implements IStickyModelProvider { public async update(textModel: ITextModel, textModelVersionId: number, token: CancellationToken): Promise { - this._store.clear(); - this._store.add({ + this._updateOperation.clear(); + this._updateOperation.add({ dispose: () => { this._cancelModelPromise(); - this._updateScheduler?.cancel(); + this._updateScheduler.cancel(); } }); this._cancelModelPromise(); @@ -113,7 +113,7 @@ export class StickyModelProvider implements IStickyModelProvider { } switch (status) { case Status.CANCELED: - this._store.clear(); + this._updateOperation.clear(); return null; case Status.VALID: return modelProvider.stickyModel; diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts index f73fca6f793..5125a517ed7 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts @@ -39,7 +39,7 @@ export class StickyLineCandidateProvider extends Disposable implements IStickyLi static readonly ID = 'store.contrib.stickyScrollController'; - private readonly _onDidChangeStickyScroll = this._store.add(new Emitter()); + private readonly _onDidChangeStickyScroll = this._register(new Emitter()); public readonly onDidChangeStickyScroll = this._onDidChangeStickyScroll.event; private readonly _editor: ICodeEditor; @@ -58,7 +58,7 @@ export class StickyLineCandidateProvider extends Disposable implements IStickyLi ) { super(); this._editor = editor; - this._sessionStore = new DisposableStore(); + this._sessionStore = this._register(new DisposableStore()); this._updateSoon = this._register(new RunOnceScheduler(() => this.update(), 50)); this._register(this._editor.onDidChangeConfiguration(e => { @@ -69,25 +69,22 @@ export class StickyLineCandidateProvider extends Disposable implements IStickyLi this.readConfiguration(); } - override dispose(): void { - super.dispose(); - this._sessionStore.dispose(); - } - private readConfiguration() { + this._stickyModelProvider = null; + this._sessionStore.clear(); + this._options = this._editor.getOption(EditorOption.stickyScroll); if (!this._options.enabled) { - this._sessionStore.clear(); return; } - this._stickyModelProvider = new StickyModelProvider( + this._stickyModelProvider = this._sessionStore.add(new StickyModelProvider( this._editor, this._languageConfigurationService, this._languageFeaturesService, this._options.defaultModel - ); + )); this._sessionStore.add(this._editor.onDidChangeModel(() => this.update())); this._sessionStore.add(this._editor.onDidChangeHiddenAreas(() => this.update())); From 51b63bf8716c254c39685302f1b623b784b0f190 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 21 Aug 2023 15:10:49 +0200 Subject: [PATCH 152/223] Clear out the sticky scroll model immediately when an editor shows a different file, also clear it out when the editor has no model or there is no more sticky model provider --- .../browser/stickyScrollProvider.ts | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts index 5125a517ed7..ee583cd4bfa 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts @@ -11,7 +11,6 @@ import { EditorOption, IEditorStickyScrollOptions } from 'vs/editor/common/confi import { RunOnceScheduler } from 'vs/base/common/async'; import { Range } from 'vs/editor/common/core/range'; import { binarySearch } from 'vs/base/common/arrays'; -import { isEqual } from 'vs/base/common/resources'; import { Event, Emitter } from 'vs/base/common/event'; import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry'; import { StickyModelProvider, IStickyModelProvider } from 'vs/editor/contrib/stickyScroll/browser/stickyScrollModelProvider'; @@ -86,7 +85,14 @@ export class StickyLineCandidateProvider extends Disposable implements IStickyLi this._options.defaultModel )); - this._sessionStore.add(this._editor.onDidChangeModel(() => this.update())); + this._sessionStore.add(this._editor.onDidChangeModel(() => { + // We should not show an old model for a different file, it will always be wrong. + // So we clear the model here immediately and then trigger an update. + this._model = null; + this._onDidChangeStickyScroll.fire(); + + this.update(); + })); this._sessionStore.add(this._editor.onDidChangeHiddenAreas(() => this.update())); this._sessionStore.add(this._editor.onDidChangeModelContent(() => this._updateSoon.schedule())); this._sessionStore.add(this._languageFeaturesService.documentSymbolProvider.onDidChange(() => this.update())); @@ -107,23 +113,14 @@ export class StickyLineCandidateProvider extends Disposable implements IStickyLi private async updateStickyModel(token: CancellationToken): Promise { if (!this._editor.hasModel() || !this._stickyModelProvider) { + this._model = null; return; } const textModel = this._editor.getModel(); const modelVersionId = textModel.getVersionId(); - const isDifferentModel = this._model ? !isEqual(this._model.uri, textModel.uri) : false; - - // Clear sticky scroll to not show stale data for too long - const resetHandle = isDifferentModel ? setTimeout(() => { - if (!token.isCancellationRequested) { - this._model = new StickyModel(textModel.uri, textModel.getVersionId(), undefined, undefined); - this._onDidChangeStickyScroll.fire(); - } - }, 75) : undefined; this._model = (await this._stickyModelProvider.update(textModel, modelVersionId, token)) ?? this._model; - clearTimeout(resetHandle); } private updateIndex(index: number) { From 796e7c3272071ef84c07ca66b0e6ca4b0567a1b3 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 21 Aug 2023 15:11:23 +0200 Subject: [PATCH 153/223] When cancelation is requested, the sticky model provider will return null. Ignore this return value and keep the previous model in this case. --- .../contrib/stickyScroll/browser/stickyScrollProvider.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts index ee583cd4bfa..3269b037cd6 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts @@ -120,7 +120,13 @@ export class StickyLineCandidateProvider extends Disposable implements IStickyLi const textModel = this._editor.getModel(); const modelVersionId = textModel.getVersionId(); - this._model = (await this._stickyModelProvider.update(textModel, modelVersionId, token)) ?? this._model; + const model = await this._stickyModelProvider.update(textModel, modelVersionId, token); + if (token.isCancellationRequested) { + // the computation was canceled, so do not overwrite the model + return; + } + + this._model = model; } private updateIndex(index: number) { From d2624165dfee5116eb08c3fc19c9432345ce9f40 Mon Sep 17 00:00:00 2001 From: Ulugbek Abdullaev Date: Mon, 21 Aug 2023 15:59:00 +0200 Subject: [PATCH 154/223] refactor: `_executeMappedEditsProvider` command doesn't need to be an action --- .../common/mappedEdits.contribution.ts | 42 +++++-------------- 1 file changed, 10 insertions(+), 32 deletions(-) diff --git a/src/vs/workbench/contrib/mappedEdits/common/mappedEdits.contribution.ts b/src/vs/workbench/contrib/mappedEdits/common/mappedEdits.contribution.ts index 469d369d14a..82171c7fbed 100644 --- a/src/vs/workbench/contrib/mappedEdits/common/mappedEdits.contribution.ts +++ b/src/vs/workbench/contrib/mappedEdits/common/mappedEdits.contribution.ts @@ -6,44 +6,22 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { URI } from 'vs/base/common/uri'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import * as nls from 'vs/nls'; -import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IMappedEditsService, MappedEditsContext } from 'vs/workbench/services/mappedEdits/common/mappedEdits'; -class ExecuteMappedEditsProvider extends Action2 { +CommandsRegistry.registerCommand('_executeMappedEditsProvider', async (accessor: ServicesAccessor, documentUri: URI, codeBlocks: string[], context: MappedEditsContext) => { - static readonly ID = '_executeMappedEditsProvider'; + const mappedEditsService = accessor.get(IMappedEditsService); + const modelService = accessor.get(ITextModelService); - constructor() { - super({ - id: ExecuteMappedEditsProvider.ID, - title: { value: nls.localize('executeMappedEditsProvider', "Execute Mapped Edits Provider"), original: '' }, - f1: false, - description: { - description: nls.localize('executeMappedEditsProvider.description', "Executes Mapped Edits Provider and returns the corresponding WorkspaceEdit or null if no edits are provided."), - args: [ - // FIXME@ulugbekna - ] - } - }); - } + const document = await modelService.createModelReference(documentUri); - async run(accessor: ServicesAccessor, documentUri: URI, codeBlocks: string[], context: MappedEditsContext) { + const cancellationTokenSource = new CancellationTokenSource(); - const mappedEditsService = accessor.get(IMappedEditsService); - const modelService = accessor.get(ITextModelService); + const result = await mappedEditsService.provideMappedEdits(document.object.textEditorModel, codeBlocks, context, cancellationTokenSource.token); - const document = await modelService.createModelReference(documentUri); + document.dispose(); - const cancellationTokenSource = new CancellationTokenSource(); - - const res = await mappedEditsService.provideMappedEdits(document.object.textEditorModel, codeBlocks, context, cancellationTokenSource.token); - - document.dispose(); - - return res; - } -} - -registerAction2(ExecuteMappedEditsProvider); + return result; +}); From c90b9775717bcb6497a4b5785b5c2bc832aaad55 Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Mon, 21 Aug 2023 08:06:56 -0700 Subject: [PATCH 155/223] polish matchesAllTags (#190777) * polish matchesAllTags * Even more polish --- .../preferences/browser/settingsTreeModels.ts | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts index c5278298cc5..bcd2604c8d7 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts @@ -382,7 +382,9 @@ export class SettingsTreeSettingElement extends SettingsTreeElement { } matchesAllTags(tagFilters?: Set): boolean { - if (!tagFilters || !tagFilters.size) { + if (!tagFilters?.size) { + // This setting, which may have tags, + // matches against a query with no tags. return true; } @@ -392,15 +394,9 @@ export class SettingsTreeSettingElement extends SettingsTreeElement { this.inspectSelf(); } - if (this.tags) { - let hasFilteredTag = true; - tagFilters.forEach(tag => { - hasFilteredTag = hasFilteredTag && this.tags!.has(tag); - }); - return hasFilteredTag; - } else { - return false; - } + // Check that the filter tags are a subset of this setting's tags + return !!this.tags?.size && + Array.from(tagFilters).every(tag => this.tags!.has(tag)); } matchesScope(scope: SettingsTarget, isRemote: boolean): boolean { From a29a3782af70d01cedacf77e850e451066128f7f Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Mon, 21 Aug 2023 17:08:51 +0200 Subject: [PATCH 156/223] Shift hover on sticky scroll shows the last line of scope (#189939) * adding code for using shift hover and shift click * using the on mouse leave event instead of using the on mouse out * removing redundant variable * chaning to mouse move * appears to work without dom addition * updating the scroll left position also on model change * removing this part which will be added in another pr * review comment --- .../browser/stickyScrollController.ts | 102 +++++++++++++----- .../browser/stickyScrollWidget.ts | 18 ++-- .../test/browser/stickyScroll.test.ts | 32 +++--- 3 files changed, 103 insertions(+), 49 deletions(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts index 265e0c458ff..37647eccdf6 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; -import { IActiveCodeEditor, ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; -import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import { IActiveCodeEditor, ICodeEditor, MouseTargetType } from 'vs/editor/browser/editorBrowser'; +import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { EditorOption, RenderLineNumbersType } from 'vs/editor/common/config/editorOptions'; import { StickyScrollWidget, StickyScrollWidgetState } from './stickyScrollWidget'; @@ -26,7 +26,7 @@ import { ILanguageConfigurationService } from 'vs/editor/common/languages/langua import { ILanguageFeatureDebounceService } from 'vs/editor/common/services/languageFeatureDebounce'; import * as dom from 'vs/base/browser/dom'; import { StickyRange } from 'vs/editor/contrib/stickyScroll/browser/stickyScrollElement'; -import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; +import { IMouseEvent, StandardMouseEvent } from 'vs/base/browser/mouseEvent'; export interface IStickyScrollController { get stickyScrollCandidateProvider(): IStickyLineCandidateProvider; @@ -63,6 +63,8 @@ export class StickyScrollController extends Disposable implements IEditorContrib private _focused = false; private _positionRevealed = false; private _onMouseDown = false; + private _endLineNumbers: number[] = []; + private _showEndForLine: number | null = null; constructor( private readonly _editor: ICodeEditor, @@ -79,7 +81,7 @@ export class StickyScrollController extends Disposable implements IEditorContrib this._register(this._stickyScrollWidget); this._register(this._stickyLineCandidateProvider); - this._widgetState = new StickyScrollWidgetState([], 0); + this._widgetState = new StickyScrollWidgetState([], [], 0); this._readConfiguration(); const stickyScrollDomNode = this._stickyScrollWidget.getDomNode(); this._register(this._editor.onDidChangeConfiguration(e => { @@ -183,8 +185,19 @@ export class StickyScrollController extends Disposable implements IEditorContrib } private _revealPosition(position: IPosition): void { + this._reveaInEditor(position, () => this._editor.revealPosition(position)); + } + + private _revealLineInCenterIfOutsideViewport(position: IPosition): void { + this._reveaInEditor(position, () => this._editor.revealLineInCenterIfOutsideViewport(position.lineNumber, ScrollType.Smooth)); + } + + private _reveaInEditor(position: IPosition, revealFunction: () => void): void { + if (this._focused) { + this._disposeFocusStickyScrollStore(); + } this._positionRevealed = true; - this._editor.revealPosition(position); + revealFunction(); this._editor.setSelection(Range.fromPositions(position)); this._editor.focus(); } @@ -223,35 +236,59 @@ export class StickyScrollController extends Disposable implements IEditorContrib }; }; - this._register(this._editor.onMouseUp((mouseEvent: IEditorMouseEvent) => { - if (mouseEvent.target.type !== MouseTargetType.OVERLAY_WIDGET || mouseEvent.target.detail !== this._stickyScrollWidget.getId()) { - // not hovering over our widget - return; - } - if (mouseEvent.event.ctrlKey || mouseEvent.event.shiftKey || mouseEvent.event.altKey || mouseEvent.event.metaKey) { + const stickyScrollWidgetDomNode = this._stickyScrollWidget.getDomNode(); + this._register(dom.addStandardDisposableListener(stickyScrollWidgetDomNode, dom.EventType.CLICK, (mouseEvent: IMouseEvent) => { + if (mouseEvent.ctrlKey || mouseEvent.altKey || mouseEvent.metaKey) { // modifier pressed return; } - if (!mouseEvent.event.leftButton) { + if (!mouseEvent.leftButton) { // not left click return; } - - let position = this._stickyScrollWidget.getEditorPositionFromNode(mouseEvent.target.element); + if (mouseEvent.shiftKey) { + // shift click + const lineIndex = this._stickyScrollWidget.getStickyLineIndexFromChildDomNode(mouseEvent.target); + if (lineIndex === null) { + return; + } + const position = new Position(this._endLineNumbers[lineIndex], 1); + this._revealLineInCenterIfOutsideViewport(position); + return; + } + // normal click + let position = this._stickyScrollWidget.getEditorPositionFromNode(mouseEvent.target); if (!position) { - const lineNumber = this._stickyScrollWidget.getLineNumberFromChildDomNode(mouseEvent.target.element); + const lineNumber = this._stickyScrollWidget.getLineNumberFromChildDomNode(mouseEvent.target); if (lineNumber === null) { // not hovering a sticky scroll line return; } position = new Position(lineNumber, 1); } - - if (this._focused) { - this._disposeFocusStickyScrollStore(); - } this._revealPosition(position); })); + this._register(dom.addStandardDisposableListener(stickyScrollWidgetDomNode, dom.EventType.MOUSE_MOVE, (mouseEvent: IMouseEvent) => { + if (mouseEvent.shiftKey) { + const currentEndForLineIndex = this._stickyScrollWidget.getStickyLineIndexFromChildDomNode(mouseEvent.target); + if (currentEndForLineIndex === null || this._showEndForLine !== null && this._showEndForLine === currentEndForLineIndex) { + return; + } + this._showEndForLine = currentEndForLineIndex; + this._renderStickyScroll(); + return; + } + if (this._showEndForLine !== null) { + this._showEndForLine = null; + this._renderStickyScroll(); + } + })); + this._register(dom.addDisposableListener(stickyScrollWidgetDomNode, dom.EventType.MOUSE_LEAVE, (e) => { + if (this._showEndForLine !== null) { + this._showEndForLine = null; + this._renderStickyScroll(); + } + })); this._register(gesture.onMouseMoveOrRelevantKeyDown(([mouseEvent, _keyboardEvent]) => { const mouseTarget = getMouseEventTarget(mouseEvent); @@ -344,18 +381,25 @@ export class StickyScrollController extends Disposable implements IEditorContrib this._editor.addOverlayWidget(this._stickyScrollWidget); this._sessionStore.add(this._editor.onDidScrollChange((e) => { if (e.scrollTopChanged) { + this._showEndForLine = null; this._renderStickyScroll(); } })); this._sessionStore.add(this._editor.onDidLayoutChange(() => this._onDidResize())); this._sessionStore.add(this._editor.onDidChangeModelTokens((e) => this._onTokensChange(e))); - this._sessionStore.add(this._stickyLineCandidateProvider.onDidChangeStickyScroll(() => this._renderStickyScroll())); + this._sessionStore.add(this._stickyLineCandidateProvider.onDidChangeStickyScroll(() => { + this._showEndForLine = null; + this._renderStickyScroll(); + })); this._enabled = true; } const lineNumberOption = this._editor.getOption(EditorOption.lineNumbers); if (lineNumberOption.renderType === RenderLineNumbersType.Relative) { - this._sessionStore.add(this._editor.onDidChangeCursorPosition(() => this._renderStickyScroll())); + this._sessionStore.add(this._editor.onDidChangeCursorPosition(() => { + this._showEndForLine = null; + this._renderStickyScroll(); + })); } } @@ -392,7 +436,7 @@ export class StickyScrollController extends Disposable implements IEditorContrib const stickyLineVersion = this._stickyLineCandidateProvider.getVersionId(); if (stickyLineVersion === undefined || stickyLineVersion === model.getVersionId()) { this._widgetState = this.findScrollWidgetState(); - this._stickyScrollVisibleContextKey.set(!(this._widgetState.lineNumbers.length === 0)); + this._stickyScrollVisibleContextKey.set(!(this._widgetState.startLineNumbers.length === 0)); if (!this._focused) { this._stickyScrollWidget.setState(this._widgetState); @@ -430,7 +474,8 @@ export class StickyScrollController extends Disposable implements IEditorContrib const maxNumberStickyLines = Math.min(this._maxStickyLines, this._editor.getOption(EditorOption.stickyScroll).maxLineCount); const scrollTop: number = this._editor.getScrollTop(); let lastLineRelativePosition: number = 0; - const lineNumbers: number[] = []; + const startLineNumbers: number[] = []; + const endLineNumbers: number[] = []; const arrayVisibleRanges = this._editor.getVisibleRanges(); if (arrayVisibleRanges.length !== 0) { const fullVisibleRange = new StickyRange(arrayVisibleRanges[0].startLineNumber, arrayVisibleRanges[arrayVisibleRanges.length - 1].endLineNumber); @@ -448,20 +493,23 @@ export class StickyScrollController extends Disposable implements IEditorContrib const bottomOfEndLine = this._editor.getBottomForLineNumber(end) - scrollTop; if (topOfElementAtDepth > topOfEndLine && topOfElementAtDepth <= bottomOfEndLine) { - lineNumbers.push(start); + startLineNumbers.push(start); + endLineNumbers.push(end + 1); lastLineRelativePosition = bottomOfEndLine - bottomOfElementAtDepth; break; } else if (bottomOfElementAtDepth > bottomOfBeginningLine && bottomOfElementAtDepth <= bottomOfEndLine) { - lineNumbers.push(start); + startLineNumbers.push(start); + endLineNumbers.push(end + 1); } - if (lineNumbers.length === maxNumberStickyLines) { + if (startLineNumbers.length === maxNumberStickyLines) { break; } } } } - return new StickyScrollWidgetState(lineNumbers, lastLineRelativePosition); + this._endLineNumbers = endLineNumbers; + return new StickyScrollWidgetState(startLineNumbers, endLineNumbers, lastLineRelativePosition, this._showEndForLine); } override dispose(): void { diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index 61914f776c7..79e9f479add 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -18,8 +18,10 @@ import { CharacterMapping, RenderLineInput, renderViewLine } from 'vs/editor/com export class StickyScrollWidgetState { constructor( - readonly lineNumbers: number[], - readonly lastLineRelativePosition: number + readonly startLineNumbers: number[], + readonly endLineNumbers: number[], + readonly lastLineRelativePosition: number, + readonly showEndForLine: number | null = null ) { } } @@ -102,11 +104,15 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { dom.clearNode(this._linesDomNode); this._stickyLines = []; const editorLineHeight = this._editor.getOption(EditorOption.lineHeight); - const futureWidgetHeight = state.lineNumbers.length * editorLineHeight + state.lastLineRelativePosition; + const futureWidgetHeight = state.startLineNumbers.length * editorLineHeight + state.lastLineRelativePosition; if (futureWidgetHeight > 0) { this._lastLineRelativePosition = state.lastLineRelativePosition; - this._lineNumbers = state.lineNumbers; + const lineNumbers = [...state.startLineNumbers]; + if (state.showEndForLine !== null) { + lineNumbers[state.showEndForLine] = state.endLineNumbers[state.showEndForLine]; + } + this._lineNumbers = lineNumbers; } else { this._lastLineRelativePosition = 0; this._lineNumbers = []; @@ -299,7 +305,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { } private _getRenderedStickyLineFromChildDomNode(domNode: HTMLElement | null): RenderedStickyLine | null { - const index = this._getStickyLineIndexFromChildDomNode(domNode); + const index = this.getStickyLineIndexFromChildDomNode(domNode); if (index === null || index < 0 || index >= this._stickyLines.length) { return null; } @@ -310,7 +316,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { * Given a child dom node, tries to find the line number attribute * that was stored in the node. Returns null if none is found. */ - private _getStickyLineIndexFromChildDomNode(domNode: HTMLElement | null): number | null { + getStickyLineIndexFromChildDomNode(domNode: HTMLElement | null): number | null { while (domNode && domNode !== this._rootDomNode) { const line = domNode.getAttribute(STICKY_LINE_INDEX_ATTR); if (line) { diff --git a/src/vs/editor/contrib/stickyScroll/test/browser/stickyScroll.test.ts b/src/vs/editor/contrib/stickyScroll/test/browser/stickyScroll.test.ts index 909bddf9801..906d1299bb8 100644 --- a/src/vs/editor/contrib/stickyScroll/test/browser/stickyScroll.test.ts +++ b/src/vs/editor/contrib/stickyScroll/test/browser/stickyScroll.test.ts @@ -161,27 +161,27 @@ suite('Sticky Scroll Tests', () => { editor.setScrollTop(1); state = stickyScrollController.findScrollWidgetState(); - assert.deepStrictEqual(state.lineNumbers, [1]); + assert.deepStrictEqual(state.startLineNumbers, [1]); editor.setScrollTop(lineHeight + 1); state = stickyScrollController.findScrollWidgetState(); - assert.deepStrictEqual(state.lineNumbers, [1]); + assert.deepStrictEqual(state.startLineNumbers, [1]); editor.setScrollTop(4 * lineHeight + 1); state = stickyScrollController.findScrollWidgetState(); - assert.deepStrictEqual(state.lineNumbers, []); + assert.deepStrictEqual(state.startLineNumbers, []); editor.setScrollTop(8 * lineHeight + 1); state = stickyScrollController.findScrollWidgetState(); - assert.deepStrictEqual(state.lineNumbers, [7, 9]); + assert.deepStrictEqual(state.startLineNumbers, [7, 9]); editor.setScrollTop(9 * lineHeight + 1); state = stickyScrollController.findScrollWidgetState(); - assert.deepStrictEqual(state.lineNumbers, [7, 9]); + assert.deepStrictEqual(state.startLineNumbers, [7, 9]); editor.setScrollTop(10 * lineHeight + 1); state = stickyScrollController.findScrollWidgetState(); - assert.deepStrictEqual(state.lineNumbers, [7]); + assert.deepStrictEqual(state.startLineNumbers, [7]); stickyScrollController.dispose(); stickyScrollController.stickyScrollCandidateProvider.dispose(); @@ -212,23 +212,23 @@ suite('Sticky Scroll Tests', () => { editor.setScrollTop(1); state = stickyScrollController.findScrollWidgetState(); - assert.deepStrictEqual(state.lineNumbers, [1]); + assert.deepStrictEqual(state.startLineNumbers, [1]); editor.setScrollTop(lineHeight + 1); state = stickyScrollController.findScrollWidgetState(); - assert.deepStrictEqual(state.lineNumbers, []); + assert.deepStrictEqual(state.startLineNumbers, []); editor.setScrollTop(6 * lineHeight + 1); state = stickyScrollController.findScrollWidgetState(); - assert.deepStrictEqual(state.lineNumbers, [7, 9]); + assert.deepStrictEqual(state.startLineNumbers, [7, 9]); editor.setScrollTop(7 * lineHeight + 1); state = stickyScrollController.findScrollWidgetState(); - assert.deepStrictEqual(state.lineNumbers, [7]); + assert.deepStrictEqual(state.startLineNumbers, [7]); editor.setScrollTop(10 * lineHeight + 1); state = stickyScrollController.findScrollWidgetState(); - assert.deepStrictEqual(state.lineNumbers, []); + assert.deepStrictEqual(state.startLineNumbers, []); stickyScrollController.dispose(); stickyScrollController.stickyScrollCandidateProvider.dispose(); @@ -306,23 +306,23 @@ suite('Sticky Scroll Tests', () => { editor.setScrollTop(1); state = stickyScrollController.findScrollWidgetState(); - assert.deepStrictEqual(state.lineNumbers, [1, 2]); + assert.deepStrictEqual(state.startLineNumbers, [1, 2]); editor.setScrollTop(lineHeight + 1); state = stickyScrollController.findScrollWidgetState(); - assert.deepStrictEqual(state.lineNumbers, [1, 2]); + assert.deepStrictEqual(state.startLineNumbers, [1, 2]); editor.setScrollTop(2 * lineHeight + 1); state = stickyScrollController.findScrollWidgetState(); - assert.deepStrictEqual(state.lineNumbers, [1]); + assert.deepStrictEqual(state.startLineNumbers, [1]); editor.setScrollTop(3 * lineHeight + 1); state = stickyScrollController.findScrollWidgetState(); - assert.deepStrictEqual(state.lineNumbers, [1]); + assert.deepStrictEqual(state.startLineNumbers, [1]); editor.setScrollTop(4 * lineHeight + 1); state = stickyScrollController.findScrollWidgetState(); - assert.deepStrictEqual(state.lineNumbers, []); + assert.deepStrictEqual(state.startLineNumbers, []); stickyScrollController.dispose(); stickyScrollController.stickyScrollCandidateProvider.dispose(); From f8e8da47f9a69c2cca5b48e654d7a9ab5c8fe1e7 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Mon, 21 Aug 2023 09:33:30 -0700 Subject: [PATCH 157/223] Use quickinput colors in quick chat (#190897) should have used this from the beginning. --- src/vs/workbench/contrib/chat/browser/chatQuick.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatQuick.ts b/src/vs/workbench/contrib/chat/browser/chatQuick.ts index e43c684102d..e23c698298e 100644 --- a/src/vs/workbench/contrib/chat/browser/chatQuick.ts +++ b/src/vs/workbench/contrib/chat/browser/chatQuick.ts @@ -10,7 +10,7 @@ import { IContextKeyService, IScopedContextKeyService } from 'vs/platform/contex import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IQuickInputService, IQuickWidget } from 'vs/platform/quickinput/common/quickInput'; -import { editorBackground, editorForeground, inputBackground } from 'vs/platform/theme/common/colorRegistry'; +import { inputBackground, quickInputBackground, quickInputForeground } from 'vs/platform/theme/common/colorRegistry'; import { IChatWidgetService, IQuickChatService } from 'vs/workbench/contrib/chat/browser/chat'; import { IChatViewOptions } from 'vs/workbench/contrib/chat/browser/chatViewPane'; import { ChatWidget } from 'vs/workbench/contrib/chat/browser/chatWidget'; @@ -142,10 +142,10 @@ class QuickChat extends Disposable { ChatWidget, { resource: true, renderInputOnTop: true, renderStyle: 'compact' }, { - listForeground: editorForeground, - listBackground: editorBackground, + listForeground: quickInputForeground, + listBackground: quickInputBackground, inputEditorBackground: inputBackground, - resultEditorBackground: editorBackground + resultEditorBackground: quickInputBackground })); this.widget.render(parent); this.widget.setVisible(true); From 458dc45528b15b56915e49e390dceee81f14b054 Mon Sep 17 00:00:00 2001 From: Jean Pierre Date: Mon, 21 Aug 2023 11:42:51 -0500 Subject: [PATCH 158/223] Fix #190499 --- src/vs/platform/terminal/node/ptyService.ts | 4 ++-- .../quickFix/browser/terminal.quickFix.contribution.ts | 2 +- .../quickFix/browser/terminalQuickFixBuiltinActions.ts | 7 ++----- .../quickFix/test/browser/quickFixAddon.test.ts | 7 +------ 4 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/vs/platform/terminal/node/ptyService.ts b/src/vs/platform/terminal/node/ptyService.ts index 7e017ab5ccd..31977e91c8c 100644 --- a/src/vs/platform/terminal/node/ptyService.ts +++ b/src/vs/platform/terminal/node/ptyService.ts @@ -161,9 +161,9 @@ export class PtyService extends Disposable implements IPtyService { resolve(stdout); }); }); - const processesForPort = stdout.split('\n'); + const processesForPort = stdout.split(/\r?\n/).filter(s => !!s.trim()); if (processesForPort.length >= 1) { - const capturePid = /\s+(\d+)\s+/; + const capturePid = /\s+(\d+)(?:\s+|$)/; const processId = processesForPort[0].match(capturePid)?.[1]; if (processId) { try { diff --git a/src/vs/workbench/contrib/terminalContrib/quickFix/browser/terminal.quickFix.contribution.ts b/src/vs/workbench/contrib/terminalContrib/quickFix/browser/terminal.quickFix.contribution.ts index f910bb1b7c3..96f2fccd74f 100644 --- a/src/vs/workbench/contrib/terminalContrib/quickFix/browser/terminal.quickFix.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/quickFix/browser/terminal.quickFix.contribution.ts @@ -55,7 +55,7 @@ class TerminalQuickFixContribution extends DisposableStore implements ITerminalC // Register quick fixes for (const actionOption of [ gitTwoDashes(), - freePort(this), + freePort((port: string, command: string) => this._instance.freePortKillProcess(port, command)), gitSimilar(), gitPushSetUpstream(), gitCreatePr(), diff --git a/src/vs/workbench/contrib/terminalContrib/quickFix/browser/terminalQuickFixBuiltinActions.ts b/src/vs/workbench/contrib/terminalContrib/quickFix/browser/terminalQuickFixBuiltinActions.ts index 386eb5153fb..89284446f73 100644 --- a/src/vs/workbench/contrib/terminalContrib/quickFix/browser/terminalQuickFixBuiltinActions.ts +++ b/src/vs/workbench/contrib/terminalContrib/quickFix/browser/terminalQuickFixBuiltinActions.ts @@ -5,7 +5,6 @@ import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; -import { ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { ITerminalQuickFixInternalOptions, ITerminalCommandMatchResult, ITerminalQuickFixExecuteTerminalCommandAction, TerminalQuickFixActionInternal, TerminalQuickFixType } from 'vs/workbench/contrib/terminalContrib/quickFix/browser/quickFix'; export const GitCommandLineRegex = /git/; @@ -88,7 +87,7 @@ export function gitTwoDashes(): ITerminalQuickFixInternalOptions { } }; } -export function freePort(terminalInstance?: Partial): ITerminalQuickFixInternalOptions { +export function freePort(runCallback: (port: string, commandLine: string) => Promise): ITerminalQuickFixInternalOptions { return { id: 'Free Port', type: 'internal', @@ -114,9 +113,7 @@ export function freePort(terminalInstance?: Partial): ITermin label, enabled: true, source: QuickFixSource.Builtin, - run: async () => { - await terminalInstance?.freePortKillProcess?.(port, matchResult.commandLine); - } + run: () => runCallback(port, matchResult.commandLine) }; } }; diff --git a/src/vs/workbench/contrib/terminalContrib/quickFix/test/browser/quickFixAddon.test.ts b/src/vs/workbench/contrib/terminalContrib/quickFix/test/browser/quickFixAddon.test.ts index 5d5a0a16a07..74d2ae5cd88 100644 --- a/src/vs/workbench/contrib/terminalContrib/quickFix/test/browser/quickFixAddon.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/quickFix/test/browser/quickFixAddon.test.ts @@ -15,7 +15,6 @@ import { ILogService, NullLogService } from 'vs/platform/log/common/log'; import { ITerminalCommand, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; import { CommandDetectionCapability } from 'vs/platform/terminal/common/capabilities/commandDetectionCapability'; import { TerminalCapabilityStore } from 'vs/platform/terminal/common/capabilities/terminalCapabilityStore'; -import { ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { gitSimilar, freePort, FreePortOutputRegex, gitCreatePr, GitCreatePrOutputRegex, GitPushOutputRegex, gitPushSetUpstream, GitSimilarOutputRegex, gitTwoDashes, GitTwoDashesRegex, pwshUnixCommandNotFoundError, PwshUnixCommandNotFoundErrorOutputRegex, pwshGeneralError, PwshGeneralErrorOutputRegex } from 'vs/workbench/contrib/terminalContrib/quickFix/browser/terminalQuickFixBuiltinActions'; import { TerminalQuickFixAddon, getQuickFixesForCommand } from 'vs/workbench/contrib/terminalContrib/quickFix/browser/quickFixAddon'; import { URI } from 'vs/base/common/uri'; @@ -34,7 +33,6 @@ import { TestCommandService } from 'vs/editor/test/browser/editorTestServices'; suite('QuickFixAddon', () => { let quickFixAddon: TerminalQuickFixAddon; - let terminalInstance: Pick; let commandDetection: CommandDetectionCapability; let commandService: TestCommandService; let openerService: OpenerService; @@ -65,9 +63,6 @@ suite('QuickFixAddon', () => { instantiationService.stub(IContextMenuService, instantiationService.createInstance(ContextMenuService)); instantiationService.stub(IOpenerService, {} as Partial); commandService = new TestCommandService(instantiationService); - terminalInstance = { - async freePortKillProcess(port: string): Promise { } - } as Pick; quickFixAddon = instantiationService.createInstance(TerminalQuickFixAddon, [], capabilities); terminal.loadAddon(quickFixAddon); @@ -212,7 +207,7 @@ suite('QuickFixAddon', () => { enabled: true }]; setup(() => { - const command = freePort(terminalInstance); + const command = freePort(() => Promise.resolve()); expectedMap.set(command.commandLineMatcher.toString(), [command]); quickFixAddon.registerCommandFinishedListener(command); }); From 1cbf3d09bc5f29b642aee86c40e4519d35645ff5 Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Mon, 21 Aug 2023 10:42:04 -0700 Subject: [PATCH 159/223] Chat file tree should not consume mouse wheel (#190908) --- src/vs/workbench/contrib/chat/browser/chatListRenderer.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index 8a367015e9e..a727c0fd887 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -1048,7 +1048,8 @@ class TreePool extends Disposable { accessibilityProvider: { getAriaLabel: (element: IChatResponseProgressFileTreeData) => element.label, getWidgetAriaLabel: () => localize('treeAriaLabel', "File Tree") - } + }, + alwaysConsumeMouseWheel: false }); return tree; From c69eb8f67a88d53b7b241c604d1396c3e2599740 Mon Sep 17 00:00:00 2001 From: David Dossett Date: Mon, 21 Aug 2023 10:42:37 -0700 Subject: [PATCH 160/223] Add status bar item hover foreground color (#190906) * Add status bar item hover foreground color * Use status bar item foreground instead --- build/lib/stylelint/vscode-known-variables.json | 1 + .../browser/parts/statusbar/media/statusbarpart.css | 1 + src/vs/workbench/common/theme.ts | 7 +++++++ 3 files changed, 9 insertions(+) diff --git a/build/lib/stylelint/vscode-known-variables.json b/build/lib/stylelint/vscode-known-variables.json index 2af3addfa9f..5069794fdd2 100644 --- a/build/lib/stylelint/vscode-known-variables.json +++ b/build/lib/stylelint/vscode-known-variables.json @@ -553,6 +553,7 @@ "--vscode-statusBarItem-errorForeground", "--vscode-statusBarItem-focusBorder", "--vscode-statusBarItem-hoverBackground", + "--vscode-statusBarItem-hoverForeground", "--vscode-statusBarItem-prominentBackground", "--vscode-statusBarItem-prominentForeground", "--vscode-statusBarItem-prominentHoverBackground", diff --git a/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css b/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css index 61880da1a6a..52662d0cb2e 100644 --- a/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css +++ b/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css @@ -142,6 +142,7 @@ .monaco-workbench .part.statusbar > .items-container > .statusbar-item > a:hover:not(.disabled) { text-decoration: none; + color: var(--vscode-statusBarItem-hoverForeground); } .monaco-workbench .part.statusbar > .items-container > .statusbar-item > a.disabled { diff --git a/src/vs/workbench/common/theme.ts b/src/vs/workbench/common/theme.ts index 6db644acc7e..1ee3874d722 100644 --- a/src/vs/workbench/common/theme.ts +++ b/src/vs/workbench/common/theme.ts @@ -496,6 +496,13 @@ export const STATUS_BAR_ITEM_HOVER_BACKGROUND = registerColor('statusBarItem.hov hcLight: Color.black.transparent(0.12) }, localize('statusBarItemHoverBackground', "Status bar item background color when hovering. The status bar is shown in the bottom of the window.")); +export const STATUS_BAR_ITEM_HOVER_FOREGROUND = registerColor('statusBarItem.hoverForeground', { + dark: STATUS_BAR_FOREGROUND, + light: STATUS_BAR_FOREGROUND, + hcDark: STATUS_BAR_FOREGROUND, + hcLight: STATUS_BAR_FOREGROUND +}, localize('statusBarItemHoverForeground', "Status bar item foreground color when hovering. The status bar is shown in the bottom of the window.")); + export const STATUS_BAR_ITEM_COMPACT_HOVER_BACKGROUND = registerColor('statusBarItem.compactHoverBackground', { dark: Color.white.transparent(0.20), light: Color.white.transparent(0.20), From f4b78696aff97376be764f67663d4a01a92fa39d Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Mon, 21 Aug 2023 11:15:17 -0700 Subject: [PATCH 161/223] Include followups when transferring Quick Chat --- src/vs/workbench/contrib/chat/browser/chatQuick.ts | 3 ++- src/vs/workbench/contrib/chat/common/chatService.ts | 1 + src/vs/workbench/contrib/chat/common/chatServiceImpl.ts | 4 ++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatQuick.ts b/src/vs/workbench/contrib/chat/browser/chatQuick.ts index e23c698298e..9851fa5a954 100644 --- a/src/vs/workbench/contrib/chat/browser/chatQuick.ts +++ b/src/vs/workbench/contrib/chat/browser/chatQuick.ts @@ -186,7 +186,8 @@ class QuickChat extends Disposable { request.message as string, { message: request.response.response.asString(), - errorDetails: request.response.errorDetails + errorDetails: request.response.errorDetails, + followups: request.response.followups }); } else if (request.message) { diff --git a/src/vs/workbench/contrib/chat/common/chatService.ts b/src/vs/workbench/contrib/chat/common/chatService.ts index 70f6c3f780f..040bee23cae 100644 --- a/src/vs/workbench/contrib/chat/common/chatService.ts +++ b/src/vs/workbench/contrib/chat/common/chatService.ts @@ -178,6 +178,7 @@ export interface IChatDynamicRequest { export interface IChatCompleteResponse { message: string; errorDetails?: IChatResponseErrorDetails; + followups?: IChatFollowup[]; } export interface IChatDetail { diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index a8663c161df..cbc2a2ddff8 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -669,6 +669,10 @@ export class ChatService extends Disposable implements IChatService { session: model.session!, errorDetails: response.errorDetails, }); + if (response.followups !== undefined) { + model.setFollowups(request, response.followups); + } + model.completeResponse(request); } cancelCurrentRequestForSession(sessionId: string): void { From 2e0609686a2791f5f850301dbd504534ae27a508 Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Mon, 21 Aug 2023 11:44:41 -0700 Subject: [PATCH 162/223] Transfer file trees from quick chat to panel chat --- .../workbench/api/browser/mainThreadChat.ts | 7 +++--- .../contrib/chat/browser/chatQuick.ts | 2 +- .../contrib/chat/common/chatModel.ts | 22 ++++++++++++------- .../contrib/chat/common/chatService.ts | 5 +++-- .../contrib/chat/common/chatServiceImpl.ts | 16 ++++++++++---- 5 files changed, 34 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadChat.ts b/src/vs/workbench/api/browser/mainThreadChat.ts index b0621471f66..ddeba6f997b 100644 --- a/src/vs/workbench/api/browser/mainThreadChat.ts +++ b/src/vs/workbench/api/browser/mainThreadChat.ts @@ -11,6 +11,7 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import { ExtHostChatShape, ExtHostContext, IChatRequestDto, IChatResponseProgressDto, MainContext, MainThreadChatShape } from 'vs/workbench/api/common/extHost.protocol'; import { IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat'; import { IChatContributionService } from 'vs/workbench/contrib/chat/common/chatContributionService'; +import { isCompleteInteractiveProgressTreeData } from 'vs/workbench/contrib/chat/common/chatModel'; import { IChat, IChatDynamicRequest, IChatProgress, IChatRequest, IChatResponse, IChatResponseProgressFileTreeData, IChatService } from 'vs/workbench/contrib/chat/common/chatService'; import { IExtHostContext, extHostNamedCustomer } from 'vs/workbench/services/extensions/common/extHostCustomers'; @@ -141,7 +142,7 @@ export class MainThreadChat extends Disposable implements MainThreadChatShape { // Complete an existing deferred promise with resolved content const responsePartId = `${id}_${responsePartHandle}`; const deferredContentPromise = this._activeResponsePartPromises.get(responsePartId); - if (deferredContentPromise && 'treeData' in progress) { + if (deferredContentPromise && isCompleteInteractiveProgressTreeData(progress)) { const withRevivedUris = revive<{ treeData: IChatResponseProgressFileTreeData }>(progress); deferredContentPromise.complete(withRevivedUris); this._activeResponsePartPromises.delete(responsePartId); @@ -152,8 +153,8 @@ export class MainThreadChat extends Disposable implements MainThreadChatShape { return; } - // No need to support standalone tree data that's not attached to a placeholder - if ('treeData' in progress) { + // No need to support standalone tree data that's not attached to a placeholder in API + if (isCompleteInteractiveProgressTreeData(progress)) { return; } diff --git a/src/vs/workbench/contrib/chat/browser/chatQuick.ts b/src/vs/workbench/contrib/chat/browser/chatQuick.ts index 9851fa5a954..6e30b77907b 100644 --- a/src/vs/workbench/contrib/chat/browser/chatQuick.ts +++ b/src/vs/workbench/contrib/chat/browser/chatQuick.ts @@ -185,7 +185,7 @@ class QuickChat extends Disposable { this.chatService.addCompleteRequest(widget.viewModel.sessionId, request.message as string, { - message: request.response.response.asString(), + message: request.response.response.value, errorDetails: request.response.errorDetails, followups: request.response.followups }); diff --git a/src/vs/workbench/contrib/chat/common/chatModel.ts b/src/vs/workbench/contrib/chat/common/chatModel.ts index a242829fd92..87b7ef10cc6 100644 --- a/src/vs/workbench/contrib/chat/common/chatModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatModel.ts @@ -25,7 +25,7 @@ export interface IChatRequestModel { export interface IResponse { readonly value: (IMarkdownString | IChatResponseProgressFileTreeData)[]; onDidChangeValue: Event; - updateContent(responsePart: string | { placeholder: string; resolvedContent?: Promise }, quiet?: boolean): void; + updateContent(responsePart: string | { treeData: IChatResponseProgressFileTreeData } | { placeholder: string; resolvedContent?: Promise }, quiet?: boolean): void; asString(): string; } @@ -111,7 +111,7 @@ export class Response implements IResponse { this._responseData = Array.isArray(value) ? value : [value]; this._responseParts = Array.isArray(value) ? value.map((v) => ('value' in v ? { string: v } : { treeData: v })) : [{ string: value }]; this._responseRepr = this._responseParts.map((part) => { - if ('treeData' in part) { + if (isCompleteInteractiveProgressTreeData(part)) { return ''; } return part.string.value; @@ -122,12 +122,12 @@ export class Response implements IResponse { return this._responseRepr; } - updateContent(responsePart: string | { placeholder: string; resolvedContent?: Promise }, quiet?: boolean): void { + updateContent(responsePart: string | { treeData: IChatResponseProgressFileTreeData } | { placeholder: string; resolvedContent?: Promise }, quiet?: boolean): void { if (typeof responsePart === 'string') { const responsePartLength = this._responseParts.length - 1; const lastResponsePart = this._responseParts[responsePartLength]; - if (lastResponsePart.resolving === true || 'treeData' in lastResponsePart) { + if (lastResponsePart.resolving === true || isCompleteInteractiveProgressTreeData(lastResponsePart)) { // The last part is resolving or a tree data item, start a new part this._responseParts.push({ string: new MarkdownString(responsePart) }); } else { @@ -151,19 +151,21 @@ export class Response implements IResponse { this._updateRepr(quiet); } }); + } else if (isCompleteInteractiveProgressTreeData(responsePart)) { + this._responseParts.push(responsePart); } } private _updateRepr(quiet?: boolean) { this._responseData = this._responseParts.map(part => { - if ('treeData' in part) { + if (isCompleteInteractiveProgressTreeData(part)) { return part.treeData; } return part.string; }); this._responseRepr = this._responseParts.map(part => { - if ('treeData' in part) { + if (isCompleteInteractiveProgressTreeData(part)) { return ''; } return part.string.value; @@ -243,7 +245,7 @@ export class ChatResponseModel extends Disposable implements IChatResponseModel this._id = 'response_' + ChatResponseModel.nextId++; } - updateContent(responsePart: string | { placeholder: string; resolvedContent?: Promise }, quiet?: boolean) { + updateContent(responsePart: string | { treeData: IChatResponseProgressFileTreeData } | { placeholder: string; resolvedContent?: Promise }, quiet?: boolean) { this._response.updateContent(responsePart, quiet); } @@ -547,7 +549,7 @@ export class ChatModel extends Disposable implements IChatModel { if ('content' in progress) { request.response.updateContent(progress.content, quiet); - } else if ('placeholder' in progress) { + } else if ('placeholder' in progress || isCompleteInteractiveProgressTreeData(progress)) { request.response.updateContent(progress, quiet); } else { request.setProviderRequestId(progress.requestId); @@ -692,3 +694,7 @@ export class ChatWelcomeMessageModel implements IChatWelcomeMessageModel { return this.session.responderAvatarIconUri; } } + +export function isCompleteInteractiveProgressTreeData(item: unknown): item is { treeData: IChatResponseProgressFileTreeData } { + return typeof item === 'object' && !!item && 'treeData' in item; +} diff --git a/src/vs/workbench/contrib/chat/common/chatService.ts b/src/vs/workbench/contrib/chat/common/chatService.ts index 040bee23cae..be769ba4f10 100644 --- a/src/vs/workbench/contrib/chat/common/chatService.ts +++ b/src/vs/workbench/contrib/chat/common/chatService.ts @@ -5,6 +5,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { Event } from 'vs/base/common/event'; +import { IMarkdownString } from 'vs/base/common/htmlContent'; import { IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { ProviderResult } from 'vs/editor/common/languages'; @@ -51,7 +52,7 @@ export interface IChatResponseProgressFileTreeData { } export type IChatProgress = - { content: string } | { requestId: string } | { placeholder: string; resolvedContent: Promise }; + { content: string } | { requestId: string } | { treeData: IChatResponseProgressFileTreeData } | { placeholder: string; resolvedContent: Promise }; export interface IPersistedChatState { } export interface IChatProvider { @@ -176,7 +177,7 @@ export interface IChatDynamicRequest { } export interface IChatCompleteResponse { - message: string; + message: string | (IMarkdownString | IChatResponseProgressFileTreeData)[]; errorDetails?: IChatResponseErrorDetails; followups?: IChatFollowup[]; } diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index cbc2a2ddff8..ae621f87f69 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -22,7 +22,7 @@ import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storag import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { CONTEXT_PROVIDER_EXISTS } from 'vs/workbench/contrib/chat/common/chatContextKeys'; -import { ChatModel, ChatWelcomeMessageModel, IChatModel, ISerializableChatData, ISerializableChatsData } from 'vs/workbench/contrib/chat/common/chatModel'; +import { ChatModel, ChatWelcomeMessageModel, IChatModel, ISerializableChatData, ISerializableChatsData, isCompleteInteractiveProgressTreeData } from 'vs/workbench/contrib/chat/common/chatModel'; import { ChatMessageRole, IChatMessage } from 'vs/workbench/contrib/chat/common/chatProvider'; import { IChat, IChatCompleteResponse, IChatDetail, IChatDynamicRequest, IChatProgress, IChatProvider, IChatProviderInfo, IChatReplyFollowup, IChatRequest, IChatResponse, IChatService, IChatTransferredSessionData, IChatUserActionEvent, ISlashCommand, InteractiveSessionCopyKind, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatSlashCommandService, IChatSlashFragment } from 'vs/workbench/contrib/chat/common/chatSlashCommands'; @@ -453,6 +453,9 @@ export class ChatService extends Disposable implements IChatService { this.trace('sendRequest', `Provider returned progress for session ${model.sessionId}, ${progress.content.length} chars`); } else if ('placeholder' in progress) { this.trace('sendRequest', `Provider returned placeholder for session ${model.sessionId}, ${progress.placeholder}`); + } else if (isCompleteInteractiveProgressTreeData(progress)) { + // This isn't exposed in API + this.trace('sendRequest', `Provider returned tree data for session ${model.sessionId}, ${progress.treeData.label}`); } else { this.trace('sendRequest', `Provider returned id for session ${model.sessionId}, ${progress.requestId}`); } @@ -662,9 +665,14 @@ export class ChatService extends Disposable implements IChatService { await model.waitForInitialization(); const request = model.addRequest(message); - model.acceptResponseProgress(request, { - content: response.message, - }, true); + if (typeof response.message === 'string') { + model.acceptResponseProgress(request, { content: response.message }); + } else { + for (const part of response.message) { + const progress = isMarkdownString(part) ? { content: part.value } : { treeData: part }; + model.acceptResponseProgress(request, progress, true); + } + } model.setResponse(request, { session: model.session!, errorDetails: response.errorDetails, From 548c32e80ca9744544a6d245e679cf14f3e3a025 Mon Sep 17 00:00:00 2001 From: Andrea Mah <31675041+andreamah@users.noreply.github.com> Date: Mon, 21 Aug 2023 12:02:42 -0700 Subject: [PATCH 163/223] Initial implementation for Quick Text Search (#190711) * initial text result quick access --- .../quickTextSearch/textSearchQuickAccess.ts | 202 ++++++++++++++++++ .../search/browser/search.contribution.ts | 10 + .../browser/searchActionsTextQuickAccess.ts | 39 ++++ .../contrib/search/browser/searchIcons.ts | 1 + .../contrib/search/browser/searchModel.ts | 95 ++++---- .../contrib/search/browser/searchView.ts | 82 ++++--- .../contrib/search/common/constants.ts | 1 + .../search/test/browser/searchActions.test.ts | 2 +- .../browser/searchNotebookHelpers.test.ts | 2 +- .../search/test/browser/searchViewlet.test.ts | 2 +- 10 files changed, 368 insertions(+), 68 deletions(-) create mode 100644 src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts create mode 100644 src/vs/workbench/contrib/search/browser/searchActionsTextQuickAccess.ts diff --git a/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts b/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts new file mode 100644 index 00000000000..ad3d5da68ff --- /dev/null +++ b/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts @@ -0,0 +1,202 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { CancellationToken } from 'vs/base/common/cancellation'; +import { IMatch } from 'vs/base/common/filters'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { basenameOrAuthority, dirname } from 'vs/base/common/resources'; +import { ThemeIcon } from 'vs/base/common/themables'; +import { localize } from 'vs/nls'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { WorkbenchCompressibleObjectTree, getSelectionKeyboardEvent } from 'vs/platform/list/browser/listService'; +import { IPickerQuickAccessItem, PickerQuickAccessProvider } from 'vs/platform/quickinput/browser/pickerQuickAccess'; +import { IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; +import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { IViewsService } from 'vs/workbench/common/views'; +import { searchDetailsIcon, searchOpenInFileIcon } from 'vs/workbench/contrib/search/browser/searchIcons'; +import { Match, MatchInNotebook, RenderableMatch, SearchModel, SearchResult } from 'vs/workbench/contrib/search/browser/searchModel'; +import { SearchView, getEditorSelectionFromMatch } from 'vs/workbench/contrib/search/browser/searchView'; +import { getOutOfWorkspaceEditorResources } from 'vs/workbench/contrib/search/common/search'; +import { ACTIVE_GROUP, IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { ITextQueryBuilderOptions, QueryBuilder } from 'vs/workbench/services/search/common/queryBuilder'; +import { IPatternInfo, ISearchConfigurationProperties, ITextQuery, VIEW_ID } from 'vs/workbench/services/search/common/search'; + +export const TEXT_SEARCH_QUICK_ACCESS_PREFIX = '% '; + +const DEFAULT_TEXT_QUERY_BUILDER_OPTIONS: ITextQueryBuilderOptions = { + _reason: 'quickAccessSearch', + disregardIgnoreFiles: false, + disregardExcludeSettings: false, + onlyOpenEditors: false, + expandPatterns: true +}; + +const MAX_FILES_SHOWN = 30; +const MAX_RESULTS_PER_FILE = 10; + +export class TextSearchQuickAccess extends PickerQuickAccessProvider { + private queryBuilder: QueryBuilder; + private searchModel: SearchModel; + + private _getTextQueryBuilderOptions(charsPerLine: number): ITextQueryBuilderOptions { + return { + ...DEFAULT_TEXT_QUERY_BUILDER_OPTIONS, + ... { + extraFileResources: this._instantiationService.invokeFunction(getOutOfWorkspaceEditorResources), + maxResults: this.searchConfig.maxResults ?? undefined, + isSmartCase: this.searchConfig.smartCase, + }, + + previewOptions: { + matchLines: 1, + charsPerLine + } + }; + } + + constructor( + @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, + @IEditorService private readonly _editorService: IEditorService, + @ILabelService private readonly _labelService: ILabelService, + @IViewsService private readonly _viewsService: IViewsService, + @IConfigurationService private readonly _configurationService: IConfigurationService, + ) { + super(TEXT_SEARCH_QUICK_ACCESS_PREFIX, { canAcceptInBackground: true }); + + this.queryBuilder = this._instantiationService.createInstance(QueryBuilder); + this.searchModel = this._instantiationService.createInstance(SearchModel); + } + + private get searchConfig(): ISearchConfigurationProperties { + return this._configurationService.getValue('search'); + } + + private async doSearch(contentPattern: string): Promise { + + if (contentPattern === '') { + return undefined; + } + + const folderResources: IWorkspaceFolder[] = this._contextService.getWorkspace().folders; + const content: IPatternInfo = { + pattern: contentPattern, + }; + const charsPerLine = content.isRegExp ? 10000 : 1000; // from https://github.com/microsoft/vscode/blob/e7ad5651ac26fa00a40aa1e4010e81b92f655569/src/vs/workbench/contrib/search/browser/searchView.ts#L1508 + + const query: ITextQuery = this.queryBuilder.text(content, folderResources.map(folder => folder.uri), this._getTextQueryBuilderOptions(charsPerLine)); + + await this.searchModel.search(query, undefined); + return this.searchModel.searchResult; + } + + private moveToSearchViewlet(model: SearchModel, currentElem: RenderableMatch) { + // this function takes this._searchModel.searchResult and moves it to the search viewlet's search model. + // then, this._searchModel will construct a new (empty) SearchResult, and the search viewlet's search result will be disposed. + this._viewsService.openView(VIEW_ID, false); + const viewlet: SearchView | undefined = this._viewsService.getActiveViewWithId(VIEW_ID) as SearchView; + viewlet.importSearchResult(model); + + const viewer: WorkbenchCompressibleObjectTree | undefined = viewlet?.getControl(); + + viewer.setFocus([currentElem], getSelectionKeyboardEvent()); + viewer.setSelection([currentElem], getSelectionKeyboardEvent()); + viewer.reveal(currentElem); + } + + protected async _getPicks(contentPattern: string, disposables: DisposableStore, token: CancellationToken): Promise<(IQuickPickSeparator | IPickerQuickAccessItem)[]> { + + const searchResult = await this.doSearch(contentPattern); + + if (!searchResult) { + return []; + } + + const picks: Array = []; + + const matches = searchResult.matches(); + const files = matches.length > MAX_FILES_SHOWN ? matches.slice(0, MAX_FILES_SHOWN) : matches; + + for (let fileIndex = 0; fileIndex < matches.length; fileIndex++) { + if (fileIndex === MAX_FILES_SHOWN) { + + picks.push({ + type: 'separator', + }); + + picks.push({ + label: 'See More Files', + iconClass: ThemeIcon.asClassName(searchDetailsIcon), + accept: async () => { + this.moveToSearchViewlet(this.searchModel, matches[MAX_FILES_SHOWN]); + } + }); + break; + } + + const fileMatch = files[fileIndex]; + + const label = basenameOrAuthority(fileMatch.resource); + const description = this._labelService.getUriLabel(dirname(fileMatch.resource), { relative: true }); + + + picks.push({ + label, + type: 'separator', + tooltip: description, + buttons: [{ + iconClass: ThemeIcon.asClassName(searchOpenInFileIcon), + tooltip: localize('QuickSearchOpenInFile', "Open File") + }], + }); + + const results: Match[] = fileMatch.matches() ?? []; + for (let matchIndex = 0; matchIndex < results.length; matchIndex++) { + const element = results[matchIndex]; + + if (matchIndex === MAX_RESULTS_PER_FILE) { + picks.push({ + label: 'More', + iconClass: ThemeIcon.asClassName(searchDetailsIcon), + accept: async () => { + this.moveToSearchViewlet(this.searchModel, element); + } + }); + break; + } + const options = { + selection: getEditorSelectionFromMatch(element, this.searchModel), + revealIfVisible: true, + indexedCellOptions: element instanceof MatchInNotebook ? { cellIndex: element.cellIndex, selection: element.range } : undefined, + }; + const preview = element.preview(); + const previewText = ' ' + (preview.before + preview.inside + preview.after).trim().substring(0, 999); + const match: IMatch[] = [{ + start: preview.before.length + 2, + end: preview.before.length + preview.inside.length + 2 + }]; + picks.push({ + label: `${previewText}`, + highlights: { + label: match + }, + description: `${element.range().startLineNumber}:${element.range().startColumn}`, + ariaLabel: `Match at location ${element.range().startLineNumber}:${element.range().startColumn} - ${previewText}`, + accept: async () => { + await this._editorService.openEditor({ + resource: fileMatch.resource, + options + }, ACTIVE_GROUP); + }, + }); + } + + } + + return picks; + + } +} diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index d868e5f6575..0aa08eff74d 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -42,6 +42,8 @@ import 'vs/workbench/contrib/search/browser/searchActionsNav'; import 'vs/workbench/contrib/search/browser/searchActionsRemoveReplace'; import 'vs/workbench/contrib/search/browser/searchActionsSymbol'; import 'vs/workbench/contrib/search/browser/searchActionsTopBar'; +import 'vs/workbench/contrib/search/browser/searchActionsTextQuickAccess'; +import { TEXT_SEARCH_QUICK_ACCESS_PREFIX, TextSearchQuickAccess } from 'vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess'; registerSingleton(ISearchWorkbenchService, SearchWorkbenchService, InstantiationType.Delayed); registerSingleton(ISearchHistoryService, SearchHistoryService, InstantiationType.Delayed); @@ -122,6 +124,14 @@ quickAccessRegistry.registerQuickAccessProvider({ helpEntries: [{ description: nls.localize('symbolsQuickAccess', "Go to Symbol in Workspace"), commandId: Constants.ShowAllSymbolsActionId }] }); +quickAccessRegistry.registerQuickAccessProvider({ + ctor: TextSearchQuickAccess, + prefix: TEXT_SEARCH_QUICK_ACCESS_PREFIX, + contextKey: 'inTextSearchPicker', + placeholder: nls.localize('textSearchPickerPlaceholder', "Search for text in your workspace files."), + helpEntries: [{ description: nls.localize('textSearchPickerHelp', "Show All Text Results (experimental)"), commandId: Constants.QuickTextSearchActionId }] +}); + // Configuration const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); configurationRegistry.registerConfiguration({ diff --git a/src/vs/workbench/contrib/search/browser/searchActionsTextQuickAccess.ts b/src/vs/workbench/contrib/search/browser/searchActionsTextQuickAccess.ts new file mode 100644 index 00000000000..ebf0558987d --- /dev/null +++ b/src/vs/workbench/contrib/search/browser/searchActionsTextQuickAccess.ts @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import * as nls from 'vs/nls'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import * as Constants from 'vs/workbench/contrib/search/common/constants'; +import { RenderableMatch } from 'vs/workbench/contrib/search/browser/searchModel'; +import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; +import { category } from 'vs/workbench/contrib/search/browser/searchActionsBase'; +import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { TEXT_SEARCH_QUICK_ACCESS_PREFIX } from 'vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess'; + +registerAction2(class TextSearchQuickAccessAction extends Action2 { + + constructor( + ) { + super({ + id: Constants.QuickTextSearchActionId, + title: { + value: nls.localize('quickTextSearch', "Quick Text Search"), + original: 'Quick Text Search' + }, + category, + menu: [{ + id: MenuId.SearchContext, + group: 'search_2', + order: 1 + }], + f1: true + }); + + } + + override async run(accessor: ServicesAccessor, match: RenderableMatch | undefined): Promise { + const quickInputService = accessor.get(IQuickInputService); + quickInputService.quickAccess.show(TEXT_SEARCH_QUICK_ACCESS_PREFIX); + } +}); diff --git a/src/vs/workbench/contrib/search/browser/searchIcons.ts b/src/vs/workbench/contrib/search/browser/searchIcons.ts index 7d64b527301..34ab891e7ad 100644 --- a/src/vs/workbench/contrib/search/browser/searchIcons.ts +++ b/src/vs/workbench/contrib/search/browser/searchIcons.ts @@ -27,3 +27,4 @@ export const searchStopIcon = registerIcon('search-stop', Codicon.searchStop, lo export const searchViewIcon = registerIcon('search-view-icon', Codicon.search, localize('searchViewIcon', 'View icon of the search view.')); export const searchNewEditorIcon = registerIcon('search-new-editor', Codicon.newFile, localize('searchNewEditorIcon', 'Icon for the action to open a new search editor.')); +export const searchOpenInFileIcon = registerIcon('search-open-in-file', Codicon.goToFile, localize('searchOpenInFile', 'Icon for the action to go to the file of the current search result.')); diff --git a/src/vs/workbench/contrib/search/browser/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchModel.ts index 8f9dd5cdcb6..82bf57c7636 100644 --- a/src/vs/workbench/contrib/search/browser/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchModel.ts @@ -940,7 +940,7 @@ export class FolderMatch extends Disposable { protected _index: number, protected _query: ITextQuery, private _parent: SearchResult | FolderMatch, - private _searchModel: SearchModel, + private _searchResult: SearchResult, private _closestRoot: FolderMatchWorkspaceRoot | null, @IReplaceService private readonly replaceService: IReplaceService, @IInstantiationService protected readonly instantiationService: IInstantiationService, @@ -957,7 +957,7 @@ export class FolderMatch extends Disposable { } get searchModel(): SearchModel { - return this._searchModel; + return this._searchResult.searchModel; } get showHighlights(): boolean { @@ -1033,7 +1033,7 @@ export class FolderMatch extends Disposable { } public createIntermediateFolderMatch(resource: URI, id: string, index: number, query: ITextQuery, baseWorkspaceFolder: FolderMatchWorkspaceRoot): FolderMatchWithResource { - const folderMatch = this.instantiationService.createInstance(FolderMatchWithResource, resource, id, index, query, this, this._searchModel, baseWorkspaceFolder); + const folderMatch = this.instantiationService.createInstance(FolderMatchWithResource, resource, id, index, query, this, this._searchResult, baseWorkspaceFolder); this.configureIntermediateMatch(folderMatch); this.doAddFolder(folderMatch); return folderMatch; @@ -1313,13 +1313,13 @@ export class FolderMatchWithResource extends FolderMatch { protected _normalizedResource: Lazy; - constructor(_resource: URI, _id: string, _index: number, _query: ITextQuery, _parent: SearchResult | FolderMatch, _searchModel: SearchModel, _closestRoot: FolderMatchWorkspaceRoot | null, + constructor(_resource: URI, _id: string, _index: number, _query: ITextQuery, _parent: SearchResult | FolderMatch, _searchResult: SearchResult, _closestRoot: FolderMatchWorkspaceRoot | null, @IReplaceService replaceService: IReplaceService, @IInstantiationService instantiationService: IInstantiationService, @ILabelService labelService: ILabelService, @IUriIdentityService uriIdentityService: IUriIdentityService ) { - super(_resource, _id, _index, _query, _parent, _searchModel, _closestRoot, replaceService, instantiationService, labelService, uriIdentityService); + super(_resource, _id, _index, _query, _parent, _searchResult, _closestRoot, replaceService, instantiationService, labelService, uriIdentityService); this._normalizedResource = new Lazy(() => this.uriIdentityService.extUri.removeTrailingPathSeparator(this.uriIdentityService.extUri.normalizePath( this.resource))); } @@ -1337,13 +1337,13 @@ export class FolderMatchWithResource extends FolderMatch { * FolderMatchWorkspaceRoot => folder for workspace root */ export class FolderMatchWorkspaceRoot extends FolderMatchWithResource { - constructor(_resource: URI, _id: string, _index: number, _query: ITextQuery, _parent: SearchResult, _searchModel: SearchModel, + constructor(_resource: URI, _id: string, _index: number, _query: ITextQuery, _parent: SearchResult, @IReplaceService replaceService: IReplaceService, @IInstantiationService instantiationService: IInstantiationService, @ILabelService labelService: ILabelService, @IUriIdentityService uriIdentityService: IUriIdentityService ) { - super(_resource, _id, _index, _query, _parent, _searchModel, null, replaceService, instantiationService, labelService, uriIdentityService); + super(_resource, _id, _index, _query, _parent, _parent, null, replaceService, instantiationService, labelService, uriIdentityService); } private normalizedUriParent(uri: URI): URI { @@ -1409,14 +1409,14 @@ export class FolderMatchWorkspaceRoot extends FolderMatchWithResource { * FolderMatch => required resource (normal folder node) */ export class FolderMatchNoRoot extends FolderMatch { - constructor(_id: string, _index: number, _query: ITextQuery, _parent: SearchResult | FolderMatch, _searchModel: SearchModel, + constructor(_id: string, _index: number, _query: ITextQuery, _parent: SearchResult, @IReplaceService replaceService: IReplaceService, @IInstantiationService instantiationService: IInstantiationService, @ILabelService labelService: ILabelService, @IUriIdentityService uriIdentityService: IUriIdentityService, ) { - super(null, _id, _index, _query, _parent, _searchModel, null, replaceService, instantiationService, labelService, uriIdentityService); + super(null, _id, _index, _query, _parent, _parent, null, replaceService, instantiationService, labelService, uriIdentityService); } createAndConfigureFileMatch(rawFileMatch: IFileMatch, searchInstanceID: string): FileMatch { @@ -1570,7 +1570,7 @@ function createParentList(element: RenderableMatch): RenderableMatch[] { export class SearchResult extends Disposable { private _onChange = this._register(new PauseableEmitter({ - merge: this.mergeEvents + merge: mergeSearchResultEvents })); readonly onChange: Event = this._onChange.event; private _folderMatches: FolderMatchWorkspaceRoot[] = []; @@ -1585,7 +1585,7 @@ export class SearchResult extends Disposable { private _onDidChangeModelListener: IDisposable | undefined; constructor( - private _searchModel: SearchModel, + public searchModel: SearchModel, @IReplaceService private readonly replaceService: IReplaceService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IModelService private readonly modelService: IModelService, @@ -1685,27 +1685,6 @@ export class SearchResult extends Disposable { this._query = query; } - private mergeEvents(events: IChangeEvent[]): IChangeEvent { - const retEvent: IChangeEvent = { - elements: [], - added: false, - removed: false, - }; - events.forEach((e) => { - if (e.added) { - retEvent.added = true; - } - - if (e.removed) { - retEvent.removed = true; - } - - retEvent.elements = retEvent.elements.concat(e.elements); - }); - - return retEvent; - } - private onDidAddNotebookEditorWidget(widget: NotebookEditorWidget): void { this._onWillChangeModelListener?.dispose(); @@ -1744,20 +1723,17 @@ export class SearchResult extends Disposable { } private _createBaseFolderMatch(resource: URI | null, id: string, index: number, query: ITextQuery): FolderMatch { - let folderMatch; + let folderMatch: FolderMatch; if (resource) { - folderMatch = this.instantiationService.createInstance(FolderMatchWorkspaceRoot, resource, id, index, query, this, this._searchModel); + folderMatch = this.instantiationService.createInstance(FolderMatchWorkspaceRoot, resource, id, index, query, this); } else { - folderMatch = this.instantiationService.createInstance(FolderMatchNoRoot, id, index, query, this, this._searchModel); + folderMatch = this.instantiationService.createInstance(FolderMatchNoRoot, id, index, query, this); } const disposable = folderMatch.onChange((event) => this._onChange.fire(event)); folderMatch.onDispose(() => disposable.dispose()); return folderMatch; } - get searchModel(): SearchModel { - return this._searchModel; - } add(allRaw: IFileMatch[], searchInstanceID: string, silent: boolean = false): void { // Split up raw into a list per folder so we can do a batch add per folder. @@ -1959,8 +1935,14 @@ export class SearchModel extends Disposable { private readonly _onReplaceTermChanged: Emitter = this._register(new Emitter()); readonly onReplaceTermChanged: Event = this._onReplaceTermChanged.event; + private readonly _onSearchResultChanged = this._register(new PauseableEmitter({ + merge: mergeSearchResultEvents + })); + readonly onSearchResultChanged: Event = this._onSearchResultChanged.event; + private currentCancelTokenSource: CancellationTokenSource | null = null; private searchCancelledForNewSearch: boolean = false; + private _searchResultChangedListener: IDisposable; constructor( @ISearchService private readonly searchService: ISearchService, @@ -1972,6 +1954,7 @@ export class SearchModel extends Disposable { ) { super(); this._searchResult = this.instantiationService.createInstance(SearchResult, this); + this._searchResultChangedListener = this._register(this._searchResult.onChange((e) => this._onSearchResultChanged.fire(e))); } isReplaceActive(): boolean { @@ -2010,6 +1993,15 @@ export class SearchModel extends Disposable { return this._searchResult; } + set searchResult(searchResult: SearchResult) { + this._searchResult.dispose(); + this._searchResultChangedListener.dispose(); + + this._searchResult = searchResult; + this._searchResult.searchModel = this; + this._searchResultChangedListener = this._register(this._searchResult.onChange((e) => this._onSearchResultChanged.fire(e))); + } + private async doSearch(query: ITextQuery, progressEmitter: Emitter, searchQuery: ITextQuery, searchInstanceID: string, onProgress?: (result: ISearchProgressItem) => void): Promise { const searchStart = Date.now(); const tokenSource = this.currentCancelTokenSource = new CancellationTokenSource(); @@ -2168,6 +2160,11 @@ export class SearchModel extends Disposable { this.searchResult.dispose(); super.dispose(); } + + transferSearchResult(other: SearchModel): void { + other.searchResult = this._searchResult; + this._searchResult = this.instantiationService.createInstance(SearchResult, this); + } } export type FileMatchOrMatch = FileMatch | Match; @@ -2343,3 +2340,25 @@ function getFileMatches(matches: (FileMatch | FolderMatchWithResource)[]): FileM return fileMatches.concat(folderMatches.map(e => e.allDownstreamFileMatches()).flat()); } + + +function mergeSearchResultEvents(events: IChangeEvent[]): IChangeEvent { + const retEvent: IChangeEvent = { + elements: [], + added: false, + removed: false, + }; + events.forEach((e) => { + if (e.added) { + retEvent.added = true; + } + + if (e.removed) { + retEvent.removed = true; + } + + retEvent.elements = retEvent.elements.concat(e.elements); + }); + + return retEvent; +} diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 2d4afe7a428..cf7506c874c 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -319,6 +319,34 @@ export class SearchView extends ViewPane { } } + private refreshInputs() { + this.pauseSearching = true; + this.searchWidget.setValue(this.viewModel.searchResult.query?.contentPattern.pattern ?? ''); + this.searchWidget.setReplaceAllActionState(false); + this.searchWidget.toggleReplace(true); + this.inputPatternIncludes.setOnlySearchInOpenEditors(this.viewModel.searchResult.query?.onlyOpenEditors || false); + this.inputPatternExcludes.setUseExcludesAndIgnoreFiles(!this.viewModel.searchResult.query?.userDisabledExcludesAndIgnoreFiles || true); + this.searchIncludePattern.setValue(''); + this.searchExcludePattern.setValue(''); + this.pauseSearching = false; + } + + public async importSearchResult(searchModel: SearchModel): Promise { + // experimental: used by the quick access search to overwrite a search result + searchModel.transferSearchResult(this.viewModel); + + this.onSearchResultsChanged(); + this.refreshInputs(); + + const collapseResults = this.searchConfig.collapseResults; + if (collapseResults !== 'alwaysCollapse' && this.viewModel.searchResult.matches().length === 1) { + const onlyMatch = this.viewModel.searchResult.matches()[0]; + if (onlyMatch.count() < 50) { + this.tree.expand(onlyMatch); + } + } + } + protected override renderBody(parent: HTMLElement): void { super.renderBody(parent); this.container = dom.append(parent, dom.$('.search-view')); @@ -431,7 +459,7 @@ export class SearchView extends ViewPane { this.toggleQueryDetails(true, true, true); } - this._register(this.viewModel.searchResult.onChange((event) => this.onSearchResultsChanged(event))); + this._register(this.viewModel.onSearchResultChanged((event) => this.onSearchResultsChanged(event))); this._register(this.onDidChangeBodyVisibility(visible => this.onVisibilityChanged(visible))); @@ -837,7 +865,7 @@ export class SearchView extends ViewPane { this._register(this.tree.onContextMenu(e => this.onContextMenu(e))); const updateHasSomeCollapsible = () => this.toggleCollapseStateDelayer.trigger(() => this.hasSomeCollapsibleResultKey.set(this.hasSomeCollapsible())); updateHasSomeCollapsible(); - this._register(this.viewModel.searchResult.onChange(() => updateHasSomeCollapsible())); + this._register(this.viewModel.onSearchResultChanged(() => updateHasSomeCollapsible())); this._register(this.tree.onDidChangeCollapseState(() => updateHasSomeCollapsible())); this._register(Event.debounce(this.tree.onDidOpen, (last, event) => event, DEBOUNCE_DELAY, true)(options => { @@ -1854,7 +1882,7 @@ export class SearchView extends ViewPane { } async open(element: FileMatchOrMatch, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean, resourceInput?: URI): Promise { - const selection = this.getSelectionFrom(element); + const selection = getEditorSelectionFromMatch(element, this.viewModel); const oldParentMatches = element instanceof Match ? element.parent().matches() : []; const resource = resourceInput ?? (element instanceof Match ? element.parent().resource : (element).resource); let editor: IEditorPane | undefined; @@ -1949,30 +1977,6 @@ export class SearchView extends ViewPane { }, errors.onUnexpectedError); } - private getSelectionFrom(element: FileMatchOrMatch): any { - let match: Match | null = null; - if (element instanceof Match) { - match = element; - } - if (element instanceof FileMatch && element.count() > 0) { - match = element.matches()[element.matches().length - 1]; - } - if (match) { - const range = match.range(); - if (this.viewModel.isReplaceActive() && !!this.viewModel.replaceString) { - const replaceString = match.replaceString; - return { - startLineNumber: range.startLineNumber, - startColumn: range.startColumn, - endLineNumber: range.startLineNumber, - endColumn: range.startColumn + replaceString.length - }; - } - return range; - } - return undefined; - } - private onUntitledDidDispose(resource: URI): void { if (!this.viewModel) { return; @@ -2146,3 +2150,27 @@ class SearchLinkButton extends Disposable { })); } } + +export function getEditorSelectionFromMatch(element: FileMatchOrMatch, viewModel: SearchModel): any { + let match: Match | null = null; + if (element instanceof Match) { + match = element; + } + if (element instanceof FileMatch && element.count() > 0) { + match = element.matches()[element.matches().length - 1]; + } + if (match) { + const range = match.range(); + if (viewModel.isReplaceActive() && !!viewModel.replaceString) { + const replaceString = match.replaceString; + return { + startLineNumber: range.startLineNumber, + startColumn: range.startColumn, + endLineNumber: range.startLineNumber, + endColumn: range.startColumn + replaceString.length + }; + } + return range; + } + return undefined; +} diff --git a/src/vs/workbench/contrib/search/common/constants.ts b/src/vs/workbench/contrib/search/common/constants.ts index 2b81f549838..d6e4fd9d7a9 100644 --- a/src/vs/workbench/contrib/search/common/constants.ts +++ b/src/vs/workbench/contrib/search/common/constants.ts @@ -30,6 +30,7 @@ export const AddCursorsAtSearchResults = 'addCursorsAtSearchResults'; export const RevealInSideBarForSearchResults = 'search.action.revealInSideBar'; export const ReplaceInFilesActionId = 'workbench.action.replaceInFiles'; export const ShowAllSymbolsActionId = 'workbench.action.showAllSymbols'; +export const QuickTextSearchActionId = 'workbench.action.quickTextSearch'; export const CancelSearchActionId = 'search.action.cancel'; export const RefreshSearchResultsActionId = 'search.action.refreshSearchResults'; export const FocusNextSearchResultActionId = 'search.action.focusNextSearchResult'; diff --git a/src/vs/workbench/contrib/search/test/browser/searchActions.test.ts b/src/vs/workbench/contrib/search/test/browser/searchActions.test.ts index 7d47ec0a8bf..ddaf7dcb2d5 100644 --- a/src/vs/workbench/contrib/search/test/browser/searchActions.test.ts +++ b/src/vs/workbench/contrib/search/test/browser/searchActions.test.ts @@ -117,7 +117,7 @@ suite('Search Actions', () => { type: QueryType.Text, folderQueries: [{ folder: createFileUriFromPathFromRoot() }], contentPattern: { pattern: '' } - }, searchModel.searchResult, searchModel, null); + }, searchModel.searchResult, searchModel.searchResult, null); return instantiationService.createInstance(FileMatch, { pattern: '' }, undefined, undefined, folderMatch, rawMatch, null, ''); diff --git a/src/vs/workbench/contrib/search/test/browser/searchNotebookHelpers.test.ts b/src/vs/workbench/contrib/search/test/browser/searchNotebookHelpers.test.ts index 8f807a2a739..4eae2669c1c 100644 --- a/src/vs/workbench/contrib/search/test/browser/searchNotebookHelpers.test.ts +++ b/src/vs/workbench/contrib/search/test/browser/searchNotebookHelpers.test.ts @@ -205,7 +205,7 @@ suite('searchNotebookHelpers', () => { type: QueryType.Text, folderQueries: [{ folder: createFileUriFromPathFromRoot() }], contentPattern: { pattern: '' } - }, searchModel.searchResult, searchModel, null); + }, searchModel.searchResult, searchModel.searchResult, null); return instantiationService.createInstance(FileMatch, { pattern: '' }, undefined, undefined, folderMatch, rawMatch, null, ''); diff --git a/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts b/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts index 014e7c143cc..279ee49e361 100644 --- a/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts +++ b/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts @@ -190,7 +190,7 @@ suite('Search - Viewlet', () => { type: QueryType.Text, folderQueries: [{ folder: createFileUriFromPathFromRoot() }], contentPattern: { pattern: '' } - }, parent ?? aSearchResult().folderMatches()[0], searchModel, null); + }, parent ?? aSearchResult().folderMatches()[0], searchModel.searchResult, null); } function aSearchResult(): SearchResult { From d0b14e8569282b8cfba402b3896619fc50927db4 Mon Sep 17 00:00:00 2001 From: floge07 <53813973+floge07@users.noreply.github.com> Date: Mon, 21 Aug 2023 21:18:44 +0200 Subject: [PATCH 164/223] fix: allow underscore as a valid char in tunnel remote address (#190904) adjust the regexes used to validate tunnel remote addresses, add underscore as a valid char. --- .../configuration-editing/schemas/attachContainer.schema.json | 2 +- src/vs/workbench/services/remote/common/tunnelModel.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/configuration-editing/schemas/attachContainer.schema.json b/extensions/configuration-editing/schemas/attachContainer.schema.json index 6d9d794265b..5e5bd571d69 100644 --- a/extensions/configuration-editing/schemas/attachContainer.schema.json +++ b/extensions/configuration-editing/schemas/attachContainer.schema.json @@ -24,7 +24,7 @@ }, { "type": "string", - "pattern": "^([a-z0-9-]+):(\\d{1,5})$" + "pattern": "^([a-z0-9_-]+):(\\d{1,5})$" } ] } diff --git a/src/vs/workbench/services/remote/common/tunnelModel.ts b/src/vs/workbench/services/remote/common/tunnelModel.ts index a5dc6002794..31ce3fca59f 100644 --- a/src/vs/workbench/services/remote/common/tunnelModel.ts +++ b/src/vs/workbench/services/remote/common/tunnelModel.ts @@ -50,7 +50,7 @@ export interface Tunnel { } export function parseAddress(address: string): { host: string; port: number } | undefined { - const matches = address.match(/^([a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*:)?([0-9]+)$/); + const matches = address.match(/^([a-zA-Z0-9_-]+(?:\.[a-zA-Z0-9_-]+)*:)?([0-9]+)$/); if (!matches) { return undefined; } From a08322ae0a1d7526cd4fe5c12742ebb14dc8d2d7 Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Mon, 21 Aug 2023 12:54:48 -0700 Subject: [PATCH 165/223] Add delay to Settings editor search for smoke tests (#190903) Ref #190896 --- test/automation/src/settings.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/automation/src/settings.ts b/test/automation/src/settings.ts index 43b6a6c6918..68401eb0eda 100644 --- a/test/automation/src/settings.ts +++ b/test/automation/src/settings.ts @@ -8,8 +8,9 @@ import { Editors } from './editors'; import { Code } from './code'; import { QuickAccess } from './quickaccess'; -export class SettingsEditor { +const SEARCH_BOX = '.settings-editor .suggest-input-container .monaco-editor textarea'; +export class SettingsEditor { constructor(private code: Code, private editors: Editors, private editor: Editor, private quickaccess: QuickAccess) { } /** @@ -56,12 +57,13 @@ export class SettingsEditor { async openUserSettingsUI(): Promise { await this.quickaccess.runCommand('workbench.action.openSettings2'); - await this.code.waitForElement('.settings-editor'); + await this.code.waitForActiveElement(SEARCH_BOX); } async searchSettingsUI(query: string): Promise { await this.openUserSettingsUI(); - await this.code.waitAndClick('.settings-editor .suggest-input-container .monaco-editor textarea'); + + await this.code.waitAndClick(SEARCH_BOX); if (process.platform === 'darwin') { await this.code.dispatchKeybinding('cmd+a'); } else { From df7dbc417442acb831aad245f1d6966990252d8f Mon Sep 17 00:00:00 2001 From: Bhavya U Date: Mon, 21 Aug 2023 15:30:42 -0700 Subject: [PATCH 166/223] Add support for controlling followup command visibility with `when` clauses (#190615) Add support for controlling followup command visibility with when clauses --- src/vs/workbench/api/common/extHostTypeConverters.ts | 1 + src/vs/workbench/contrib/chat/browser/chatFollowups.ts | 7 +++++++ src/vs/workbench/contrib/chat/browser/chatInputPart.ts | 2 +- src/vs/workbench/contrib/chat/browser/chatListRenderer.ts | 6 ++++-- src/vs/workbench/contrib/chat/common/chatService.ts | 1 + src/vscode-dts/vscode.proposed.interactive.d.ts | 1 + 6 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index d0e1bc99d4a..9ffb8aa451b 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -2152,6 +2152,7 @@ export namespace ChatFollowup { kind: 'command', title: followup.title ?? '', commandId: followup.commandId ?? '', + when: followup.when ?? '', args: followup.args }; } else { diff --git a/src/vs/workbench/contrib/chat/browser/chatFollowups.ts b/src/vs/workbench/contrib/chat/browser/chatFollowups.ts index fe77551dea0..409e4452098 100644 --- a/src/vs/workbench/contrib/chat/browser/chatFollowups.ts +++ b/src/vs/workbench/contrib/chat/browser/chatFollowups.ts @@ -7,6 +7,7 @@ import * as dom from 'vs/base/browser/dom'; import { Button, IButtonStyles } from 'vs/base/browser/ui/button/button'; import { MarkdownString } from 'vs/base/common/htmlContent'; import { Disposable } from 'vs/base/common/lifecycle'; +import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IChatFollowup } from 'vs/workbench/contrib/chat/common/chatService'; const $ = dom.$; @@ -17,6 +18,7 @@ export class ChatFollowups extends Disposable { followups: T[], private readonly options: IButtonStyles | undefined, private readonly clickHandler: (followup: T) => void, + private readonly contextService: IContextKeyService, ) { super(); @@ -25,6 +27,11 @@ export class ChatFollowups extends Disposable { } private renderFollowup(container: HTMLElement, followup: T): void { + + if (followup.kind === 'command' && followup.when && !this.contextService.contextMatchesRules(ContextKeyExpr.deserialize(followup.when))) { + return; + } + const tooltip = 'tooltip' in followup ? followup.tooltip : undefined; const button = this._register(new Button(container, { ...this.options, supportIcons: true, title: tooltip })); if (followup.kind === 'reply') { diff --git a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts index 6dce4e1d7d1..7066089148a 100644 --- a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts @@ -269,7 +269,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge dom.clearNode(this.followupsContainer); if (items && items.length > 0) { - this.followupsDisposables.add(new ChatFollowups(this.followupsContainer, items, undefined, followup => this._onDidAcceptFollowup.fire(followup))); + this.followupsDisposables.add(new ChatFollowups(this.followupsContainer, items, undefined, followup => this._onDidAcceptFollowup.fire(followup), this.contextKeyService)); } } diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index a727c0fd887..52f971ad1b9 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -349,7 +349,8 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer this._onDidClickFollowup.fire(followup))); + followup => this._onDidClickFollowup.fire(followup), + templateData.contextKeyService)); } else { const result = this.renderMarkdown(item as IMarkdownString, element, templateData.elementDisposables, templateData); for (const codeElement of result.element.querySelectorAll('code')) { diff --git a/src/vs/workbench/contrib/chat/common/chatService.ts b/src/vs/workbench/contrib/chat/common/chatService.ts index 70f6c3f780f..4c265f87383 100644 --- a/src/vs/workbench/contrib/chat/common/chatService.ts +++ b/src/vs/workbench/contrib/chat/common/chatService.ts @@ -104,6 +104,7 @@ export interface IChatResponseCommandFollowup { commandId: string; args?: any[]; title: string; // supports codicon strings + when?: string; } export type IChatFollowup = IChatReplyFollowup | IChatResponseCommandFollowup; diff --git a/src/vscode-dts/vscode.proposed.interactive.d.ts b/src/vscode-dts/vscode.proposed.interactive.d.ts index a294597ec6a..382afeb8277 100644 --- a/src/vscode-dts/vscode.proposed.interactive.d.ts +++ b/src/vscode-dts/vscode.proposed.interactive.d.ts @@ -149,6 +149,7 @@ declare module 'vscode' { commandId: string; args?: any[]; title: string; // supports codicon strings + when?: string; } export interface InteractiveSessionSlashCommand { From 6f8b058b103b646695a9b7c8275d0a815820f0df Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 21 Aug 2023 15:47:03 -0700 Subject: [PATCH 167/223] Pick up TS 5.2 RC (#190922) --- extensions/package.json | 2 +- extensions/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/package.json b/extensions/package.json index b0bac44993d..02a1f822e7d 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -4,7 +4,7 @@ "license": "MIT", "description": "Dependencies shared by all extensions", "dependencies": { - "typescript": "^5.2.0-dev.20230807" + "typescript": "^5.2.1-rc" }, "scripts": { "postinstall": "node ./postinstall.mjs" diff --git a/extensions/yarn.lock b/extensions/yarn.lock index 815d839a79a..08ae5fb06c3 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -228,10 +228,10 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -typescript@^5.2.0-dev.20230807: - version "5.2.0-dev.20230807" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.0-dev.20230807.tgz#652fa8a3fcc46958035d9b911c815299deb9f58e" - integrity sha512-sF8sZl3r/mpAdKAxASaWaoU+mNPF3g8OrZ601HraU2l4WEwAf6nO7sq0Bzl+Y3FV7sWjy+gb6kdM1CtLjVne/g== +typescript@^5.2.1-rc: + version "5.2.1-rc" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.1-rc.tgz#9cf33ff6bc39ba9e1fa59761124f596ecf5e0c07" + integrity sha512-gsOdmedQZEWLrYhNqHuzPmcV+4wX7UujzYqszDC5mVMjcN6Nm7lN2eAtndmjWl24aGdAwJqL2ooywkxpaTx8QQ== vscode-grammar-updater@^1.1.0: version "1.1.0" From cf4f089eefc65230c32373e99152162b91710e47 Mon Sep 17 00:00:00 2001 From: David Dossett Date: Mon, 21 Aug 2023 16:35:12 -0700 Subject: [PATCH 168/223] Add hover foreground for prominent status bar items (#190925) --- build/lib/stylelint/vscode-known-variables.json | 3 ++- src/vs/workbench/common/theme.ts | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/build/lib/stylelint/vscode-known-variables.json b/build/lib/stylelint/vscode-known-variables.json index 5069794fdd2..6133fe142b4 100644 --- a/build/lib/stylelint/vscode-known-variables.json +++ b/build/lib/stylelint/vscode-known-variables.json @@ -557,6 +557,7 @@ "--vscode-statusBarItem-prominentBackground", "--vscode-statusBarItem-prominentForeground", "--vscode-statusBarItem-prominentHoverBackground", + "--vscode-statusBarItem-prominentHoverForeground", "--vscode-statusBarItem-remoteBackground", "--vscode-statusBarItem-remoteForeground", "--vscode-statusBarItem-warningBackground", @@ -769,4 +770,4 @@ "--z-index-notebook-sticky-scroll", "--zoom-factor" ] -} +} \ No newline at end of file diff --git a/src/vs/workbench/common/theme.ts b/src/vs/workbench/common/theme.ts index 1ee3874d722..bc3da309318 100644 --- a/src/vs/workbench/common/theme.ts +++ b/src/vs/workbench/common/theme.ts @@ -531,6 +531,13 @@ export const STATUS_BAR_PROMINENT_ITEM_HOVER_BACKGROUND = registerColor('statusB hcLight: null }, localize('statusBarProminentItemHoverBackground', "Status bar prominent items background color when hovering. Prominent items stand out from other status bar entries to indicate importance. Change mode `Toggle Tab Key Moves Focus` from command palette to see an example. The status bar is shown in the bottom of the window.")); +export const STATUS_BAR_PROMINENT_ITEM_HOVER_FOREGROUND = registerColor('statusBarItem.prominentHoverForeground', { + dark: STATUS_BAR_FOREGROUND, + light: STATUS_BAR_FOREGROUND, + hcDark: STATUS_BAR_FOREGROUND, + hcLight: STATUS_BAR_FOREGROUND +}, localize('statusBarProminentItemHoverForeground', "Status bar prominent items foreground color when hovering. Prominent items stand out from other status bar entries to indicate importance. Change mode `Toggle Tab Key Moves Focus` from command palette to see an example. The status bar is shown in the bottom of the window.")); + export const STATUS_BAR_ERROR_ITEM_BACKGROUND = registerColor('statusBarItem.errorBackground', { dark: darken(errorForeground, .4), light: darken(errorForeground, .4), From 620eb95fd95a3c68f6c24c5db89ad8f62bb34c51 Mon Sep 17 00:00:00 2001 From: Justin Chen <54879025+justschen@users.noreply.github.com> Date: Mon, 21 Aug 2023 17:43:46 -0700 Subject: [PATCH 169/223] Action Widget - removed custom keyboard controller, added support in list widget (#190765) * added fix to custom keyboard controller * removed custom keyboard controller, using list widget keyboard label provider * code cleanup --- src/vs/base/browser/ui/list/listWidget.ts | 29 +++++++-- .../actionWidget/browser/actionList.ts | 60 ++++--------------- 2 files changed, 35 insertions(+), 54 deletions(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 36c5a74a948..ee900068b4b 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -16,7 +16,7 @@ import { timeout } from 'vs/base/common/async'; import { Color } from 'vs/base/common/color'; import { memoize } from 'vs/base/common/decorators'; import { Emitter, Event, EventBufferer } from 'vs/base/common/event'; -import { matchesPrefix } from 'vs/base/common/filters'; +import { matchesFuzzy2, matchesPrefix } from 'vs/base/common/filters'; import { KeyCode } from 'vs/base/common/keyCodes'; import { DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle'; import { clamp } from 'vs/base/common/numbers'; @@ -527,11 +527,27 @@ class TypeNavigationController implements IDisposable { const label = this.keyboardNavigationLabelProvider.getKeyboardNavigationLabel(this.view.element(index)); const labelStr = label && label.toString(); - if (typeof labelStr === 'undefined' || matchesPrefix(word, labelStr)) { - this.previouslyFocused = start; - this.list.setFocus([index]); - this.list.reveal(index); - return; + if (this.list.options.typeNavigationEnabled) { + if (typeof labelStr !== 'undefined') { + const prefix = matchesPrefix(word, labelStr); + const fuzzy = matchesFuzzy2(word, labelStr); + + // ensures that when fuzzy matching, it doesn't clash with prefix matching (1 input vs 1+ should be prefix and fuzzy respecitvely) + const fuzzyScore = fuzzy ? fuzzy[0].end - fuzzy[0].start : 0; + if (prefix || fuzzyScore > 1) { + this.previouslyFocused = start; + this.list.setFocus([index]); + this.list.reveal(index); + return; + } + } + } else { + if (typeof labelStr === 'undefined' || matchesPrefix(word, labelStr)) { + this.previouslyFocused = start; + this.list.setFocus([index]); + this.list.reveal(index); + return; + } } } } @@ -978,6 +994,7 @@ export interface IListOptions extends IListOptionsUpdate { readonly keyboardNavigationLabelProvider?: IKeyboardNavigationLabelProvider; readonly keyboardNavigationDelegate?: IKeyboardNavigationDelegate; readonly keyboardSupport?: boolean; + readonly keyboardNavigationEnabled?: boolean; readonly multipleSelectionController?: IMultipleSelectionController; readonly styleController?: (suffix: string) => IStyleController; readonly accessibilityProvider?: IListAccessibilityProvider; diff --git a/src/vs/platform/actionWidget/browser/actionList.ts b/src/vs/platform/actionWidget/browser/actionList.ts index 1cea5ac88c0..e4422c393d6 100644 --- a/src/vs/platform/actionWidget/browser/actionList.ts +++ b/src/vs/platform/actionWidget/browser/actionList.ts @@ -6,7 +6,6 @@ import * as dom from 'vs/base/browser/dom'; import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel'; import { IListEvent, IListMouseEvent, IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { List } from 'vs/base/browser/ui/list/listWidget'; -import { matchesFuzzy2 } from 'vs/base/common/filters'; import { Codicon } from 'vs/base/common/codicons'; import { ResolvedKeybinding } from 'vs/base/common/keybindings'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -44,11 +43,6 @@ interface IActionMenuTemplateData { readonly keybinding: KeybindingLabel; } -interface IActionFilteredItems { - readonly label: string | undefined; - readonly index: number; -} - export const enum ActionListItemKind { Action = 'action', Header = 'header' @@ -154,6 +148,15 @@ class AcceptSelectedEvent extends UIEvent { class PreviewSelectedEvent extends UIEvent { constructor() { super('previewSelectedAction'); } } + +function getKeyboardNavigationLabel(item: IActionListItem): string | undefined { + // Filter out header vs. action + if (item.kind === 'action') { + return item.label; + } + return undefined; +} + export class ActionList extends Disposable { public readonly domNode: HTMLElement; @@ -164,10 +167,6 @@ export class ActionList extends Disposable { private readonly _headerLineHeight = 26; private readonly _allMenuItems: readonly IActionListItem[]; - private readonly _allMenuItemsFiltered: IActionFilteredItems[] = []; - - private keypresses: string[] = []; - private timeout: number | null = null; constructor( user: string, @@ -191,6 +190,8 @@ export class ActionList extends Disposable { new HeaderRenderer(), ], { keyboardSupport: false, + typeNavigationEnabled: true, + keyboardNavigationLabelProvider: { getKeyboardNavigationLabel }, accessibilityProvider: { getAriaLabel: element => { if (element.kind === ActionListItemKind.Action) { @@ -207,18 +208,15 @@ export class ActionList extends Disposable { getWidgetRole: () => 'listbox', }, })); + this._list.style(defaultListStyles); this._register(this._list.onMouseClick(e => this.onListClick(e))); this._register(this._list.onMouseOver(e => this.onListHover(e))); this._register(this._list.onDidChangeFocus(() => this._list.domFocus())); this._register(this._list.onDidChangeSelection(e => this.onListSelection(e))); - this._register(this._list.onKeyPress(e => this.onKeyPress(e))); this._allMenuItems = items; - const menuItems = this._allMenuItems; - this._allMenuItemsFiltered = menuItems.flatMap((obj, index) => (obj.kind === `action` && !obj.disabled) ? [{ label: obj.label, index }] : []); - this._list.splice(0, this._list.length, this._allMenuItems); if (this._list.length) { @@ -313,40 +311,6 @@ export class ActionList extends Disposable { this._list.setFocus([]); } } - - private onKeyPress(e: KeyboardEvent) { - this.keypresses.push(e.key); - this.listFuzzyMatch(this.keypresses.join('')); - - // Clear the previous timeout (if any) - if (this.timeout !== null) { - window.clearTimeout(this.timeout); - } - - // Set a new timeout - this.timeout = window.setTimeout(() => { this.processKeyPresses(); }, 1000); - - } - - private processKeyPresses() { - this.keypresses = []; - } - - private listFuzzyMatch(str: string) { - for (const menuItem of this._allMenuItemsFiltered) { - if (menuItem.label) { - const result = matchesFuzzy2(str, menuItem.label); - // Result is either null or an array of obj {start, end} for matches between str and menuItem.label - if (result) { - this._list.setFocus([menuItem.index]); - if (result[0].start === 0) { - // Breaks when finds first found instance of match [0,1+] to [0,1+] - break; - } - } - } - } - } } function stripNewlines(str: string): string { From 1945d43a2ba3e1fd9fd0e45e0739e160884cf448 Mon Sep 17 00:00:00 2001 From: aamunger Date: Mon, 21 Aug 2023 19:54:27 -0700 Subject: [PATCH 170/223] untitled files are not about to save with auto-save --- .../workbench/contrib/notebook/common/notebookEditorInput.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts index 4bdbae63271..0524f62bde4 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts @@ -158,8 +158,8 @@ export class NotebookEditorInput extends AbstractResourceEditorInput { override isSaving(): boolean { const model = this._editorModelReference?.object; - if (!model || !model.isDirty() || model.hasErrorState) { - return false; // require the model to be dirty and not in an error state + if (!model || !model.isDirty() || model.hasErrorState || this.hasCapability(EditorInputCapabilities.Untitled)) { + return false; // require the model to be dirty, file-backed and not in an error state } // if a short auto save is configured, treat this as being saved From 763fdc016e7da77393dbfd7f1c473adb9d055b5a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 22 Aug 2023 08:36:55 +0200 Subject: [PATCH 171/223] Confusing tooltip and action on default notification API (fix #190190) (#190943) --- .../browser/parts/notifications/notificationsActions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/notifications/notificationsActions.ts b/src/vs/workbench/browser/parts/notifications/notificationsActions.ts index b69808128ae..39387591537 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsActions.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsActions.ts @@ -133,7 +133,7 @@ export class CollapseNotificationAction extends Action { export class ConfigureNotificationAction extends Action { static readonly ID = 'workbench.action.configureNotification'; - static readonly LABEL = localize('configureNotification', "Configure Notification"); + static readonly LABEL = localize('configureNotification', "More Actions..."); constructor( id: string, From c3e92639d49c2ed3fa97430f7a35bb5018075a07 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 22 Aug 2023 08:37:14 +0200 Subject: [PATCH 172/223] [Bug][Comparison View] Saving the file in the active editor will cause both files to be saved. (fix #190210) (#190944) --- src/vs/workbench/contrib/files/browser/fileCommands.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/files/browser/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts index 82c91b59496..30e70d2f05a 100644 --- a/src/vs/workbench/contrib/files/browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts @@ -377,11 +377,16 @@ async function saveSelectedEditors(accessor: ServicesAccessor, options?: ISaveEd // has 2 sides, we consider both, to support saving both sides. // We only allow this when saving, not for "Save As" and not if any // editor is untitled which would bring up a "Save As" dialog too. + // In addition, we require the secondary side to be modified to not + // trigger a touch operation unexpectedly. + // // See also https://github.com/microsoft/vscode/issues/4180 // See also https://github.com/microsoft/vscode/issues/106330 + // See also https://github.com/microsoft/vscode/issues/190210 if ( activeGroup.activeEditor instanceof SideBySideEditorInput && - !options?.saveAs && !(activeGroup.activeEditor.primary.hasCapability(EditorInputCapabilities.Untitled) || activeGroup.activeEditor.secondary.hasCapability(EditorInputCapabilities.Untitled)) + !options?.saveAs && !(activeGroup.activeEditor.primary.hasCapability(EditorInputCapabilities.Untitled) || activeGroup.activeEditor.secondary.hasCapability(EditorInputCapabilities.Untitled)) && + activeGroup.activeEditor.secondary.isModified() ) { editors.push({ groupId: activeGroup.id, editor: activeGroup.activeEditor.primary }); editors.push({ groupId: activeGroup.id, editor: activeGroup.activeEditor.secondary }); From 8caa1c80f5ff68a6a6b78d36057d2a6ec21362b1 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 22 Aug 2023 08:56:34 +0200 Subject: [PATCH 173/223] update `confirmPlease` flow (#190942) --- .github/commands.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/commands.json b/.github/commands.json index 15c6cd953c5..9d2ac7d7090 100644 --- a/.github/commands.json +++ b/.github/commands.json @@ -486,7 +486,7 @@ ], "action": "comment", "addLabel": "info-needed", - "comment": "Please perform the following **three tasks** to diagnose the root cause of the issue:\n\n* [ ] **1.) Disable Extensions**\n * Select `View` and pick `Command Palette...`\n * Run `Developer: Reload With Extensions Disabled`\n * 👉 See if the issue reproduces\n\n* [ ] **2.) Disable Configuration**\n * Select `View` and pick `Command Palette...`\n * Run `Profiles: Create a Temporary Profile`\n * 👉 See if the issue reproduces\n\n* [ ] **3.) Try VS Code Insiders**\n * Download [VS Code Insiders](https://code.visualstudio.com/insiders/)\n * Install and Run it\n * 👉 See if the issue reproduces\n \nThen pick one of the three resolutions depending on which step has helped:\n\n
\n Disabling my Extensions helped\n\nPlease run the command `Start Extension Bisect` and follow the instructions to find the extension that is causing this issue.\n\nimage\n\nPlease report the issue to the extension causing this.\n
\n\n
\n Disabling my configuration helped\nPlease report back more details about your configuration, including settings.\n
\n\n
\n Using VS Code Insiders has helped\n✅ This likely means that the issue has been addressed already and will be available in an upcoming release. You can safely use VS Code Insiders until the new stable version is available.\n
" + "comment": "Please diagnose the root cause of the issue by running the command `F1 > Help: Troubleshoot Issue` and following the instructions. Once you have done that, please update the issue with the results.\n\nHappy Coding!" }, { "__comment__": "Allows folks on the team to label issues by commenting: `\\label My-Label` ", From 652b702314f8669213c5cadd4b0419123256e913 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 22 Aug 2023 11:43:26 +0200 Subject: [PATCH 174/223] files - document `files.associations` config (#185191) (#190951) --- src/vs/workbench/contrib/files/browser/files.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/files/browser/files.contribution.ts b/src/vs/workbench/contrib/files/browser/files.contribution.ts index 4f3d6065173..f6de1a29bc4 100644 --- a/src/vs/workbench/contrib/files/browser/files.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/files.contribution.ts @@ -181,7 +181,7 @@ configurationRegistry.registerConfiguration({ }, [FILES_ASSOCIATIONS_CONFIG]: { 'type': 'object', - 'markdownDescription': nls.localize('associations', "Configure file associations to languages (for example `\"*.extension\": \"html\"`). These have precedence over the default associations of the languages installed."), + 'markdownDescription': nls.localize('associations', "Configure [glob patterns](https://aka.ms/vscode-glob-patterns) of file associations to languages (for example `\"*.extension\": \"html\"`). Patterns will match on the absolute path of a file if they contain a path separator and will match on the name of the file otherwise. These have precedence over the default associations of the languages installed."), 'additionalProperties': { 'type': 'string' } From d8affc039ab669c3009aad5bab4e8e2f9de299a9 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 22 Aug 2023 12:34:24 +0200 Subject: [PATCH 175/223] fix #188582 (#190957) --- .../common/extensionsProfileScannerService.ts | 19 +- .../extensionsProfileScannerService.test.ts | 252 +++++++++++++++++- 2 files changed, 259 insertions(+), 12 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/extensionsProfileScannerService.ts b/src/vs/platform/extensionManagement/common/extensionsProfileScannerService.ts index 30e749ac312..40eb2584305 100644 --- a/src/vs/platform/extensionManagement/common/extensionsProfileScannerService.ts +++ b/src/vs/platform/extensionManagement/common/extensionsProfileScannerService.ts @@ -124,22 +124,24 @@ export abstract class AbstractExtensionsProfileScannerService extends Disposable const extensionsToRemove: IScannedProfileExtension[] = []; const extensionsToAdd: IScannedProfileExtension[] = []; try { - await this.withProfileExtensions(profileLocation, profileExtensions => { + await this.withProfileExtensions(profileLocation, existingExtensions => { const result: IScannedProfileExtension[] = []; - for (const extension of profileExtensions) { - if (extensions.some(([e]) => areSameExtensions(e.identifier, extension.identifier) && e.manifest.version !== extension.version)) { + for (const existing of existingExtensions) { + if (extensions.some(([e]) => areSameExtensions(e.identifier, existing.identifier) && e.manifest.version !== existing.version)) { // Remove the existing extension with different version - extensionsToRemove.push(extension); + extensionsToRemove.push(existing); } else { - result.push(extension); + result.push(existing); } } for (const [extension, metadata] of extensions) { - if (!result.some(e => areSameExtensions(e.identifier, extension.identifier) && e.version === extension.manifest.version)) { - // Add only if the same version of the extension is not already added - const extensionToAdd = { identifier: extension.identifier, version: extension.manifest.version, location: extension.location, metadata }; + const index = result.findIndex(e => areSameExtensions(e.identifier, extension.identifier) && e.version === extension.manifest.version); + const extensionToAdd = { identifier: extension.identifier, version: extension.manifest.version, location: extension.location, metadata }; + if (index === -1) { extensionsToAdd.push(extensionToAdd); result.push(extensionToAdd); + } else { + result.splice(index, 1, extensionToAdd); } } if (extensionsToAdd.length) { @@ -189,7 +191,6 @@ export abstract class AbstractExtensionsProfileScannerService extends Disposable async removeExtensionFromProfile(extension: IExtension, profileLocation: URI): Promise { const extensionsToRemove: IScannedProfileExtension[] = []; - this._onRemoveExtensions.fire({ extensions: extensionsToRemove, profileLocation }); try { await this.withProfileExtensions(profileLocation, profileExtensions => { const result: IScannedProfileExtension[] = []; diff --git a/src/vs/platform/extensionManagement/test/common/extensionsProfileScannerService.test.ts b/src/vs/platform/extensionManagement/test/common/extensionsProfileScannerService.test.ts index 2dcd920d3e2..b2fa0668e72 100644 --- a/src/vs/platform/extensionManagement/test/common/extensionsProfileScannerService.test.ts +++ b/src/vs/platform/extensionManagement/test/common/extensionsProfileScannerService.test.ts @@ -4,13 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import * as sinon from 'sinon'; import { VSBuffer } from 'vs/base/common/buffer'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { AbstractExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService'; -import { ExtensionType, IExtension, TargetPlatform } from 'vs/platform/extensions/common/extensions'; +import { AbstractExtensionsProfileScannerService, ProfileExtensionsEvent } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService'; +import { ExtensionType, IExtension, IExtensionManifest, TargetPlatform } from 'vs/platform/extensions/common/extensions'; import { FileService } from 'vs/platform/files/common/fileService'; import { IFileService } from 'vs/platform/files/common/files'; import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; @@ -47,6 +48,10 @@ suite('ExtensionsProfileScannerService', () => { instantiationService.stub(IUserDataProfilesService, userDataProfilesService); }); + teardown(() => disposables.clear()); + + suiteTeardown(() => sinon.restore()); + test('write extensions located in the same extensions folder', async () => { const testObject = instantiationService.createInstance(TestObject, extensionsLocation); @@ -442,7 +447,247 @@ suite('ExtensionsProfileScannerService', () => { assert.deepStrictEqual(manifestContent, [{ identifier: extension.identifier, location: extension.location.toJSON(), relativeLocation: 'pub.a-1.0.0', version: extension.manifest.version }]); }); - function aExtension(id: string, location: URI, e?: Partial): IExtension { + test('add extension trigger events', async () => { + const testObject = instantiationService.createInstance(TestObject, extensionsLocation); + const target1 = sinon.stub(); + const target2 = sinon.stub(); + testObject.onAddExtensions(target1); + testObject.onDidAddExtensions(target2); + + const extensionsManifest = joinPath(extensionsLocation, 'extensions.json'); + const extension = aExtension('pub.a', joinPath(ROOT, 'foo', 'pub.a-1.0.0')); + await testObject.addExtensionsToProfile([[extension, undefined]], extensionsManifest); + + const actual = await testObject.scanProfileExtensions(extensionsManifest); + assert.deepStrictEqual(actual.map(a => ({ ...a, location: a.location.toJSON() })), [{ identifier: extension.identifier, location: extension.location.toJSON(), version: extension.manifest.version, metadata: undefined }]); + + assert.ok(target1.calledOnce); + assert.deepStrictEqual(((target1.args[0][0])).profileLocation.toString(), extensionsManifest.toString()); + assert.deepStrictEqual(((target1.args[0][0])).extensions.length, 1); + assert.deepStrictEqual(((target1.args[0][0])).extensions[0].identifier, extension.identifier); + assert.deepStrictEqual(((target1.args[0][0])).extensions[0].version, extension.manifest.version); + assert.deepStrictEqual(((target1.args[0][0])).extensions[0].location.toString(), extension.location.toString()); + + assert.ok(target2.calledOnce); + assert.deepStrictEqual(((target2.args[0][0])).profileLocation.toString(), extensionsManifest.toString()); + assert.deepStrictEqual(((target2.args[0][0])).extensions.length, 1); + assert.deepStrictEqual(((target2.args[0][0])).extensions[0].identifier, extension.identifier); + assert.deepStrictEqual(((target2.args[0][0])).extensions[0].version, extension.manifest.version); + assert.deepStrictEqual(((target2.args[0][0])).extensions[0].location.toString(), extension.location.toString()); + }); + + test('remove extension trigger events', async () => { + const testObject = instantiationService.createInstance(TestObject, extensionsLocation); + const target1 = sinon.stub(); + const target2 = sinon.stub(); + testObject.onRemoveExtensions(target1); + testObject.onDidRemoveExtensions(target2); + + const extensionsManifest = joinPath(extensionsLocation, 'extensions.json'); + const extension = aExtension('pub.a', joinPath(ROOT, 'foo', 'pub.a-1.0.0')); + await testObject.addExtensionsToProfile([[extension, undefined]], extensionsManifest); + await testObject.removeExtensionFromProfile(extension, extensionsManifest); + + const actual = await testObject.scanProfileExtensions(extensionsManifest); + assert.deepStrictEqual(actual.length, 0); + + assert.ok(target1.calledOnce); + assert.deepStrictEqual(((target1.args[0][0])).profileLocation.toString(), extensionsManifest.toString()); + assert.deepStrictEqual(((target1.args[0][0])).extensions.length, 1); + assert.deepStrictEqual(((target1.args[0][0])).extensions[0].identifier, extension.identifier); + assert.deepStrictEqual(((target1.args[0][0])).extensions[0].version, extension.manifest.version); + assert.deepStrictEqual(((target1.args[0][0])).extensions[0].location.toString(), extension.location.toString()); + + assert.ok(target2.calledOnce); + assert.deepStrictEqual(((target2.args[0][0])).profileLocation.toString(), extensionsManifest.toString()); + assert.deepStrictEqual(((target2.args[0][0])).extensions.length, 1); + assert.deepStrictEqual(((target2.args[0][0])).extensions[0].identifier, extension.identifier); + assert.deepStrictEqual(((target2.args[0][0])).extensions[0].version, extension.manifest.version); + assert.deepStrictEqual(((target2.args[0][0])).extensions[0].location.toString(), extension.location.toString()); + }); + + test('add extension with same id but different version', async () => { + const testObject = instantiationService.createInstance(TestObject, extensionsLocation); + + const extensionsManifest = joinPath(extensionsLocation, 'extensions.json'); + + const extension1 = aExtension('pub.a', joinPath(ROOT, 'pub.a-1.0.0')); + await testObject.addExtensionsToProfile([[extension1, undefined]], extensionsManifest); + + const target1 = sinon.stub(); + const target2 = sinon.stub(); + const target3 = sinon.stub(); + const target4 = sinon.stub(); + testObject.onAddExtensions(target1); + testObject.onRemoveExtensions(target2); + testObject.onDidAddExtensions(target3); + testObject.onDidRemoveExtensions(target4); + const extension2 = aExtension('pub.a', joinPath(ROOT, 'pub.a-2.0.0'), undefined, { version: '2.0.0' }); + await testObject.addExtensionsToProfile([[extension2, undefined]], extensionsManifest); + + const actual = await testObject.scanProfileExtensions(extensionsManifest); + assert.deepStrictEqual(actual.map(a => ({ ...a, location: a.location.toJSON() })), [{ identifier: extension2.identifier, location: extension2.location.toJSON(), version: extension2.manifest.version, metadata: undefined }]); + + assert.ok(target1.calledOnce); + assert.deepStrictEqual(((target1.args[0][0])).profileLocation.toString(), extensionsManifest.toString()); + assert.deepStrictEqual(((target1.args[0][0])).extensions.length, 1); + assert.deepStrictEqual(((target1.args[0][0])).extensions[0].identifier, extension2.identifier); + assert.deepStrictEqual(((target1.args[0][0])).extensions[0].version, extension2.manifest.version); + assert.deepStrictEqual(((target1.args[0][0])).extensions[0].location.toString(), extension2.location.toString()); + + assert.ok(target2.calledOnce); + assert.deepStrictEqual(((target2.args[0][0])).profileLocation.toString(), extensionsManifest.toString()); + assert.deepStrictEqual(((target2.args[0][0])).extensions.length, 1); + assert.deepStrictEqual(((target2.args[0][0])).extensions[0].identifier, extension1.identifier); + assert.deepStrictEqual(((target2.args[0][0])).extensions[0].version, extension1.manifest.version); + assert.deepStrictEqual(((target2.args[0][0])).extensions[0].location.toString(), extension1.location.toString()); + + assert.ok(target3.calledOnce); + assert.deepStrictEqual(((target1.args[0][0])).profileLocation.toString(), extensionsManifest.toString()); + assert.deepStrictEqual(((target1.args[0][0])).extensions.length, 1); + assert.deepStrictEqual(((target1.args[0][0])).extensions[0].identifier, extension2.identifier); + assert.deepStrictEqual(((target1.args[0][0])).extensions[0].version, extension2.manifest.version); + assert.deepStrictEqual(((target1.args[0][0])).extensions[0].location.toString(), extension2.location.toString()); + + assert.ok(target4.calledOnce); + assert.deepStrictEqual(((target2.args[0][0])).profileLocation.toString(), extensionsManifest.toString()); + assert.deepStrictEqual(((target2.args[0][0])).extensions.length, 1); + assert.deepStrictEqual(((target2.args[0][0])).extensions[0].identifier, extension1.identifier); + assert.deepStrictEqual(((target2.args[0][0])).extensions[0].version, extension1.manifest.version); + assert.deepStrictEqual(((target2.args[0][0])).extensions[0].location.toString(), extension1.location.toString()); + }); + + test('add same extension', async () => { + const testObject = instantiationService.createInstance(TestObject, extensionsLocation); + + const extensionsManifest = joinPath(extensionsLocation, 'extensions.json'); + + const extension = aExtension('pub.a', joinPath(ROOT, 'pub.a-1.0.0')); + await testObject.addExtensionsToProfile([[extension, undefined]], extensionsManifest); + + const target1 = sinon.stub(); + const target2 = sinon.stub(); + const target3 = sinon.stub(); + const target4 = sinon.stub(); + testObject.onAddExtensions(target1); + testObject.onRemoveExtensions(target2); + testObject.onDidAddExtensions(target3); + testObject.onDidRemoveExtensions(target4); + await testObject.addExtensionsToProfile([[extension, undefined]], extensionsManifest); + + const actual = await testObject.scanProfileExtensions(extensionsManifest); + assert.deepStrictEqual(actual.map(a => ({ ...a, location: a.location.toJSON() })), [{ identifier: extension.identifier, location: extension.location.toJSON(), version: extension.manifest.version, metadata: undefined }]); + assert.ok(target1.notCalled); + assert.ok(target2.notCalled); + assert.ok(target3.notCalled); + assert.ok(target4.notCalled); + }); + + test('add same extension with different metadata', async () => { + const testObject = instantiationService.createInstance(TestObject, extensionsLocation); + + const extensionsManifest = joinPath(extensionsLocation, 'extensions.json'); + + const extension = aExtension('pub.a', joinPath(ROOT, 'pub.a-1.0.0')); + await testObject.addExtensionsToProfile([[extension, undefined]], extensionsManifest); + + const target1 = sinon.stub(); + const target2 = sinon.stub(); + const target3 = sinon.stub(); + const target4 = sinon.stub(); + testObject.onAddExtensions(target1); + testObject.onRemoveExtensions(target2); + testObject.onDidAddExtensions(target3); + testObject.onDidRemoveExtensions(target4); + await testObject.addExtensionsToProfile([[extension, { isApplicationScoped: true }]], extensionsManifest); + + const actual = await testObject.scanProfileExtensions(extensionsManifest); + assert.deepStrictEqual(actual.map(a => ({ ...a, location: a.location.toJSON(), metadata: a.metadata })), [{ identifier: extension.identifier, location: extension.location.toJSON(), version: extension.manifest.version, metadata: { isApplicationScoped: true } }]); + assert.ok(target1.notCalled); + assert.ok(target2.notCalled); + assert.ok(target3.notCalled); + assert.ok(target4.notCalled); + }); + + test('add extension with different version and metadata', async () => { + const testObject = instantiationService.createInstance(TestObject, extensionsLocation); + + const extensionsManifest = joinPath(extensionsLocation, 'extensions.json'); + + const extension1 = aExtension('pub.a', joinPath(ROOT, 'pub.a-1.0.0')); + await testObject.addExtensionsToProfile([[extension1, undefined]], extensionsManifest); + const extension2 = aExtension('pub.a', joinPath(ROOT, 'pub.a-2.0.0'), undefined, { version: '2.0.0' }); + + const target1 = sinon.stub(); + const target2 = sinon.stub(); + const target3 = sinon.stub(); + const target4 = sinon.stub(); + testObject.onAddExtensions(target1); + testObject.onRemoveExtensions(target2); + testObject.onDidAddExtensions(target3); + testObject.onDidRemoveExtensions(target4); + await testObject.addExtensionsToProfile([[extension2, { isApplicationScoped: true }]], extensionsManifest); + + const actual = await testObject.scanProfileExtensions(extensionsManifest); + assert.deepStrictEqual(actual.map(a => ({ ...a, location: a.location.toJSON(), metadata: a.metadata })), [{ identifier: extension2.identifier, location: extension2.location.toJSON(), version: extension2.manifest.version, metadata: { isApplicationScoped: true } }]); + + assert.ok(target1.calledOnce); + assert.deepStrictEqual(((target1.args[0][0])).profileLocation.toString(), extensionsManifest.toString()); + assert.deepStrictEqual(((target1.args[0][0])).extensions.length, 1); + assert.deepStrictEqual(((target1.args[0][0])).extensions[0].identifier, extension2.identifier); + assert.deepStrictEqual(((target1.args[0][0])).extensions[0].version, extension2.manifest.version); + assert.deepStrictEqual(((target1.args[0][0])).extensions[0].location.toString(), extension2.location.toString()); + + assert.ok(target2.calledOnce); + assert.deepStrictEqual(((target2.args[0][0])).profileLocation.toString(), extensionsManifest.toString()); + assert.deepStrictEqual(((target2.args[0][0])).extensions.length, 1); + assert.deepStrictEqual(((target2.args[0][0])).extensions[0].identifier, extension1.identifier); + assert.deepStrictEqual(((target2.args[0][0])).extensions[0].version, extension1.manifest.version); + assert.deepStrictEqual(((target2.args[0][0])).extensions[0].location.toString(), extension1.location.toString()); + + assert.ok(target3.calledOnce); + assert.deepStrictEqual(((target1.args[0][0])).profileLocation.toString(), extensionsManifest.toString()); + assert.deepStrictEqual(((target1.args[0][0])).extensions.length, 1); + assert.deepStrictEqual(((target1.args[0][0])).extensions[0].identifier, extension2.identifier); + assert.deepStrictEqual(((target1.args[0][0])).extensions[0].version, extension2.manifest.version); + assert.deepStrictEqual(((target1.args[0][0])).extensions[0].location.toString(), extension2.location.toString()); + + assert.ok(target4.calledOnce); + assert.deepStrictEqual(((target2.args[0][0])).profileLocation.toString(), extensionsManifest.toString()); + assert.deepStrictEqual(((target2.args[0][0])).extensions.length, 1); + assert.deepStrictEqual(((target2.args[0][0])).extensions[0].identifier, extension1.identifier); + assert.deepStrictEqual(((target2.args[0][0])).extensions[0].version, extension1.manifest.version); + assert.deepStrictEqual(((target2.args[0][0])).extensions[0].location.toString(), extension1.location.toString()); + }); + + test('add extension with same id and version located in the different folder', async () => { + const testObject = instantiationService.createInstance(TestObject, extensionsLocation); + + const extensionsManifest = joinPath(extensionsLocation, 'extensions.json'); + + let extension = aExtension('pub.a', joinPath(ROOT, 'foo', 'pub.a-1.0.0')); + await testObject.addExtensionsToProfile([[extension, undefined]], extensionsManifest); + + const target1 = sinon.stub(); + const target2 = sinon.stub(); + const target3 = sinon.stub(); + const target4 = sinon.stub(); + testObject.onAddExtensions(target1); + testObject.onRemoveExtensions(target2); + testObject.onDidAddExtensions(target3); + testObject.onDidRemoveExtensions(target4); + extension = aExtension('pub.a', joinPath(ROOT, 'pub.a-1.0.0')); + await testObject.addExtensionsToProfile([[extension, undefined]], extensionsManifest); + + const actual = await testObject.scanProfileExtensions(extensionsManifest); + assert.deepStrictEqual(actual.map(a => ({ ...a, location: a.location.toJSON() })), [{ identifier: extension.identifier, location: extension.location.toJSON(), version: extension.manifest.version, metadata: undefined }]); + assert.ok(target1.notCalled); + assert.ok(target2.notCalled); + assert.ok(target3.notCalled); + assert.ok(target4.notCalled); + }); + + function aExtension(id: string, location: URI, e?: Partial, manifest?: Partial): IExtension { return { identifier: { id }, location, @@ -454,6 +699,7 @@ suite('ExtensionsProfileScannerService', () => { publisher: 'publisher', version: '1.0.0', engines: { vscode: '1.0.0' }, + ...manifest, }, isValid: true, validations: [], From f19d12309511a2053a0a928dbbd0e05365b52b6a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 22 Aug 2023 12:37:40 +0200 Subject: [PATCH 176/223] status bar - introduce `kind` and adopt (fix #184967) (#190947) * status bar - introduce `kind` and adopt (fix #184967) * adopt for extensions * deprecate colors --- .../lib/stylelint/vscode-known-variables.json | 14 ++- .../api/browser/mainThreadStatusBar.ts | 2 +- .../api/browser/statusBarExtensionPoint.ts | 18 +++- .../parts/editor/accessibilityStatus.ts | 5 +- .../browser/parts/editor/editorStatus.ts | 8 +- .../parts/statusbar/media/statusbarpart.css | 61 ++++++++++-- .../browser/parts/statusbar/statusbarItem.ts | 19 +++- .../browser/parts/statusbar/statusbarPart.ts | 4 +- src/vs/workbench/common/theme.ts | 94 ++++++++++++++++--- .../browser/languageStatus.contribution.ts | 18 ++-- .../editorStatusBar/editorStatusBar.ts | 2 +- .../contrib/remote/browser/remoteIndicator.ts | 20 +--- .../contrib/remote/browser/tunnelView.ts | 10 +- .../terminal/browser/baseTerminalBackend.ts | 5 +- .../browser/workspace.contribution.ts | 7 +- .../services/statusbar/browser/statusbar.ts | 16 +++- 16 files changed, 211 insertions(+), 92 deletions(-) diff --git a/build/lib/stylelint/vscode-known-variables.json b/build/lib/stylelint/vscode-known-variables.json index 6133fe142b4..73faa4aaa12 100644 --- a/build/lib/stylelint/vscode-known-variables.json +++ b/build/lib/stylelint/vscode-known-variables.json @@ -545,12 +545,16 @@ "--vscode-statusBar-noFolderBackground", "--vscode-statusBar-noFolderBorder", "--vscode-statusBar-noFolderForeground", - "--vscode-statusBar-offlineBackground", - "--vscode-statusBar-offlineForeground", + "--vscode-statusBarItem-offlineBackground", + "--vscode-statusBarItem-offlineForeground", + "--vscode-statusBarItem-offlineHoverBackground", + "--vscode-statusBarItem-offlineHoverForeground", "--vscode-statusBarItem-activeBackground", "--vscode-statusBarItem-compactHoverBackground", "--vscode-statusBarItem-errorBackground", "--vscode-statusBarItem-errorForeground", + "--vscode-statusBarItem-errorHoverBackground", + "--vscode-statusBarItem-errorHoverForeground", "--vscode-statusBarItem-focusBorder", "--vscode-statusBarItem-hoverBackground", "--vscode-statusBarItem-hoverForeground", @@ -560,8 +564,12 @@ "--vscode-statusBarItem-prominentHoverForeground", "--vscode-statusBarItem-remoteBackground", "--vscode-statusBarItem-remoteForeground", + "--vscode-statusBarItem-remoteHoverBackground", + "--vscode-statusBarItem-remoteHoverForeground", "--vscode-statusBarItem-warningBackground", "--vscode-statusBarItem-warningForeground", + "--vscode-statusBarItem-warningHoverBackground", + "--vscode-statusBarItem-warningHoverForeground", "--vscode-symbolIcon-arrayForeground", "--vscode-symbolIcon-booleanForeground", "--vscode-symbolIcon-classForeground", @@ -770,4 +778,4 @@ "--z-index-notebook-sticky-scroll", "--zoom-factor" ] -} \ No newline at end of file +} diff --git a/src/vs/workbench/api/browser/mainThreadStatusBar.ts b/src/vs/workbench/api/browser/mainThreadStatusBar.ts index 4e561ae88bb..00eb17f4982 100644 --- a/src/vs/workbench/api/browser/mainThreadStatusBar.ts +++ b/src/vs/workbench/api/browser/mainThreadStatusBar.ts @@ -56,7 +56,7 @@ export class MainThreadStatusBar implements MainThreadStatusBarShape { this._store.dispose(); } - $setEntry(entryId: string, id: string, extensionId: string | undefined, name: string, text: string, tooltip: IMarkdownString | string | undefined, command: Command | undefined, color: string | ThemeColor | undefined, backgroundColor: string | ThemeColor | undefined, alignLeft: boolean, priority: number | undefined, accessibilityInformation: IAccessibilityInformation | undefined): void { + $setEntry(entryId: string, id: string, extensionId: string | undefined, name: string, text: string, tooltip: IMarkdownString | string | undefined, command: Command | undefined, color: string | ThemeColor | undefined, backgroundColor: ThemeColor | undefined, alignLeft: boolean, priority: number | undefined, accessibilityInformation: IAccessibilityInformation | undefined): void { const kind = this.statusbarService.setOrUpdateEntry(entryId, id, extensionId, name, text, tooltip, command, color, backgroundColor, alignLeft, priority, accessibilityInformation); if (kind === StatusBarUpdateKind.DidDefine) { this._store.add(toDisposable(() => this.statusbarService.unsetEntry(entryId))); diff --git a/src/vs/workbench/api/browser/statusBarExtensionPoint.ts b/src/vs/workbench/api/browser/statusBarExtensionPoint.ts index cd2e439f8f6..99dd3279fe8 100644 --- a/src/vs/workbench/api/browser/statusBarExtensionPoint.ts +++ b/src/vs/workbench/api/browser/statusBarExtensionPoint.ts @@ -9,7 +9,7 @@ import { localize } from 'vs/nls'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { IStatusbarService, StatusbarAlignment as MainThreadStatusBarAlignment, IStatusbarEntryAccessor, IStatusbarEntry, StatusbarAlignment, IStatusbarEntryPriority } from 'vs/workbench/services/statusbar/browser/statusbar'; +import { IStatusbarService, StatusbarAlignment as MainThreadStatusBarAlignment, IStatusbarEntryAccessor, IStatusbarEntry, StatusbarAlignment, IStatusbarEntryPriority, StatusbarEntryKind } from 'vs/workbench/services/statusbar/browser/statusbar'; import { ThemeColor } from 'vs/base/common/themables'; import { Command } from 'vs/editor/common/languages'; import { IAccessibilityInformation, isAccessibilityInformation } from 'vs/platform/accessibility/common/accessibility'; @@ -21,6 +21,7 @@ import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/ import { Iterable } from 'vs/base/common/iterator'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { asStatusBarItemIdentifier } from 'vs/workbench/api/common/extHostTypes'; +import { STATUS_BAR_ERROR_ITEM_BACKGROUND, STATUS_BAR_WARNING_ITEM_BACKGROUND } from 'vs/workbench/common/theme'; // --- service @@ -48,7 +49,7 @@ export interface IExtensionStatusBarItemService { onDidChange: Event; - setOrUpdateEntry(id: string, statusId: string, extensionId: string | undefined, name: string, text: string, tooltip: IMarkdownString | string | undefined, command: Command | undefined, color: string | ThemeColor | undefined, backgroundColor: string | ThemeColor | undefined, alignLeft: boolean, priority: number | undefined, accessibilityInformation: IAccessibilityInformation | undefined): StatusBarUpdateKind; + setOrUpdateEntry(id: string, statusId: string, extensionId: string | undefined, name: string, text: string, tooltip: IMarkdownString | string | undefined, command: Command | undefined, color: string | ThemeColor | undefined, backgroundColor: ThemeColor | undefined, alignLeft: boolean, priority: number | undefined, accessibilityInformation: IAccessibilityInformation | undefined): StatusBarUpdateKind; unsetEntry(id: string): void; @@ -75,7 +76,7 @@ class ExtensionStatusBarItemService implements IExtensionStatusBarItemService { setOrUpdateEntry(entryId: string, id: string, extensionId: string | undefined, name: string, text: string, tooltip: IMarkdownString | string | undefined, - command: Command | undefined, color: string | ThemeColor | undefined, backgroundColor: string | ThemeColor | undefined, + command: Command | undefined, color: string | ThemeColor | undefined, backgroundColor: ThemeColor | undefined, alignLeft: boolean, priority: number | undefined, accessibilityInformation: IAccessibilityInformation | undefined ): StatusBarUpdateKind { // if there are icons in the text use the tooltip for the aria label @@ -91,7 +92,16 @@ class ExtensionStatusBarItemService implements IExtensionStatusBarItemService { ariaLabel += `, ${tooltipString}`; } } - const entry: IStatusbarEntry = { name, text, tooltip, command, color, backgroundColor, ariaLabel, role }; + let kind: StatusbarEntryKind | undefined = undefined; + switch (backgroundColor?.id) { + case STATUS_BAR_ERROR_ITEM_BACKGROUND: + case STATUS_BAR_WARNING_ITEM_BACKGROUND: + // override well known colors that map to status entry kinds to support associated themable hover colors + kind = backgroundColor.id === STATUS_BAR_ERROR_ITEM_BACKGROUND ? 'error' : 'warning'; + color = undefined; + backgroundColor = undefined; + } + const entry: IStatusbarEntry = { name, text, tooltip, command, color, backgroundColor, ariaLabel, role, kind }; if (typeof priority === 'undefined') { priority = 0; diff --git a/src/vs/workbench/browser/parts/editor/accessibilityStatus.ts b/src/vs/workbench/browser/parts/editor/accessibilityStatus.ts index de65eca21f0..5160d1e2791 100644 --- a/src/vs/workbench/browser/parts/editor/accessibilityStatus.ts +++ b/src/vs/workbench/browser/parts/editor/accessibilityStatus.ts @@ -13,8 +13,6 @@ import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configur import { INotificationHandle, INotificationService, NotificationPriority } from 'vs/platform/notification/common/notification'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment } from 'vs/workbench/services/statusbar/browser/statusbar'; -import { themeColorFromId } from 'vs/platform/theme/common/themeService'; -import { STATUS_BAR_PROMINENT_ITEM_BACKGROUND, STATUS_BAR_PROMINENT_ITEM_FOREGROUND } from 'vs/workbench/common/theme'; export class AccessibilityStatus extends Disposable implements IWorkbenchContribution { private screenReaderNotification: INotificationHandle | null = null; @@ -71,8 +69,7 @@ export class AccessibilityStatus extends Disposable implements IWorkbenchContrib text, ariaLabel: text, command: 'showEditorScreenReaderNotification', - backgroundColor: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_BACKGROUND), - color: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_FOREGROUND) + kind: 'prominent' }, 'status.editor.screenReaderMode', StatusbarAlignment.RIGHT, 100.6); } } else { diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index d18e4fc12f7..dcb7c46f57a 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -47,8 +47,6 @@ import { Event } from 'vs/base/common/event'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment, IStatusbarEntry } from 'vs/workbench/services/statusbar/browser/statusbar'; import { IMarker, IMarkerService, MarkerSeverity, IMarkerData } from 'vs/platform/markers/common/markers'; -import { STATUS_BAR_PROMINENT_ITEM_BACKGROUND, STATUS_BAR_PROMINENT_ITEM_FOREGROUND } from 'vs/workbench/common/theme'; -import { themeColorFromId } from 'vs/platform/theme/common/themeService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { SideBySideEditorInput } from 'vs/workbench/common/editor/sideBySideEditorInput'; import { AutomaticLanguageDetectionLikelyWrongClassification, AutomaticLanguageDetectionLikelyWrongId, IAutomaticLanguageDetectionLikelyWrongData, ILanguageDetectionService } from 'vs/workbench/services/languageDetection/common/languageDetectionWorkerService'; @@ -381,8 +379,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { ariaLabel: text, tooltip: localize('disableTabMode', "Disable Accessibility Mode"), command: 'editor.action.toggleTabFocusMode', - backgroundColor: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_BACKGROUND), - color: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_FOREGROUND) + kind: 'prominent' }, 'status.editor.tabFocusMode', StatusbarAlignment.RIGHT, 100.7); } } else { @@ -400,8 +397,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { ariaLabel: text, tooltip: localize('disableColumnSelectionMode', "Disable Column Selection Mode"), command: 'editor.action.toggleColumnSelection', - backgroundColor: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_BACKGROUND), - color: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_FOREGROUND) + kind: 'prominent' }, 'status.editor.columnSelectionMode', StatusbarAlignment.RIGHT, 100.8); } } else { diff --git a/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css b/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css index 52662d0cb2e..d6897bf02d7 100644 --- a/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css +++ b/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css @@ -155,15 +155,6 @@ color: inherit; } -.monaco-workbench .part.statusbar > .items-container > .statusbar-item .status-bar-info { - color: var(--vscode-statusBarItem-prominentForeground); - background-color: var(--vscode-statusBarItem-prominentBackground); -} - -.monaco-workbench .part.statusbar > .items-container > .statusbar-item a.status-bar-info:hover:not(.disabled) { - background-color: var(--vscode-statusBarItem-prominentHoverBackground); -} - .monaco-workbench .part.statusbar > .items-container > .statusbar-item a:active:not(.disabled) { outline: 1px solid var(--vscode-contrastActiveBorder) !important; outline-offset: -1px; @@ -181,3 +172,55 @@ .monaco-workbench:not(.hc-light):not(.hc-black) .part.statusbar > .items-container > .statusbar-item a:hover:not(.disabled) { background-color: var(--vscode-statusBarItem-hoverBackground); } + +/** Status bar entry item kinds */ + +.monaco-workbench .part.statusbar > .items-container > .statusbar-item.warning-kind { + color: var(--vscode-statusBarItem-warningForeground); + background-color: var(--vscode-statusBarItem-warningBackground); +} + +.monaco-workbench .part.statusbar > .items-container > .statusbar-item.warning-kind a:hover:not(.disabled) { + color: var(--vscode-statusBarItem-warningHoverForeground); + background-color: var(--vscode-statusBarItem-warningHoverBackground) !important; +} + +.monaco-workbench .part.statusbar > .items-container > .statusbar-item.error-kind { + color: var(--vscode-statusBarItem-errorForeground); + background-color: var(--vscode-statusBarItem-errorBackground); +} + +.monaco-workbench .part.statusbar > .items-container > .statusbar-item.error-kind a:hover:not(.disabled) { + color: var(--vscode-statusBarItem-errorHoverForeground); + background-color: var(--vscode-statusBarItem-errorHoverBackground) !important; +} + +.monaco-workbench .part.statusbar > .items-container > .statusbar-item.prominent-kind { + color: var(--vscode-statusBarItem-prominentForeground); + background-color: var(--vscode-statusBarItem-prominentBackground); +} + +.monaco-workbench .part.statusbar > .items-container > .statusbar-item.prominent-kind a:hover:not(.disabled) { + color: var(--vscode-statusBarItem-prominentHoverForeground); + background-color: var(--vscode-statusBarItem-prominentHoverBackground) !important; +} + +.monaco-workbench .part.statusbar > .items-container > .statusbar-item.remote-kind { + color: var(--vscode-statusBarItem-remoteForeground); + background-color: var(--vscode-statusBarItem-remoteBackground); +} + +.monaco-workbench .part.statusbar > .items-container > .statusbar-item.remote-kind a:hover:not(.disabled) { + color: var(--vscode-statusBarItem-remoteHoverForeground); + background-color: var(--vscode-statusBarItem-remoteHoverBackground) !important; +} + +.monaco-workbench .part.statusbar > .items-container > .statusbar-item.offline-kind { + color: var(--vscode-statusBarItem-offlineForeground); + background-color: var(--vscode-statusBarItem-offlineBackground); +} + +.monaco-workbench .part.statusbar > .items-container > .statusbar-item.offline-kind a:hover:not(.disabled) { + color: var(--vscode-statusBarItem-offlineHoverForeground); + background-color: var(--vscode-statusBarItem-offlineHoverBackground) !important; +} diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarItem.ts b/src/vs/workbench/browser/parts/statusbar/statusbarItem.ts index b1988505d95..39fd753e3d6 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarItem.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarItem.ts @@ -8,7 +8,7 @@ import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { SimpleIconLabel } from 'vs/base/browser/ui/iconLabel/simpleIconLabel'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IStatusbarEntry, ShowTooltipCommand } from 'vs/workbench/services/statusbar/browser/statusbar'; +import { IStatusbarEntry, ShowTooltipCommand, StatusbarEntryKinds } from 'vs/workbench/services/statusbar/browser/statusbar'; import { WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ThemeColor } from 'vs/base/common/themables'; @@ -156,6 +156,21 @@ export class StatusbarEntryItem extends Disposable { } } + const hasBackgroundColor = !!entry.backgroundColor || (entry.kind && entry.kind !== 'standard'); + + // Update: Kind + if (!this.entry || entry.kind !== this.entry.kind) { + for (const kind of StatusbarEntryKinds) { + this.container.classList.remove(`${kind}-kind`); + } + + if (entry.kind && entry.kind !== 'standard') { + this.container.classList.add(`${entry.kind}-kind`); + } + + this.container.classList.toggle('has-background-color', hasBackgroundColor); + } + // Update: Foreground if (!this.entry || entry.color !== this.entry.color) { this.applyColor(this.labelContainer, entry.color); @@ -163,7 +178,7 @@ export class StatusbarEntryItem extends Disposable { // Update: Background if (!this.entry || entry.backgroundColor !== this.entry.backgroundColor) { - this.container.classList.toggle('has-background-color', !!entry.backgroundColor); + this.container.classList.toggle('has-background-color', hasBackgroundColor); this.applyColor(this.container, entry.backgroundColor, true); } diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts index 1c949d44a00..a2938f04e10 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts @@ -20,7 +20,7 @@ import { EventHelper, createStyleSheet, addDisposableListener, EventType, clearN import { IStorageService } from 'vs/platform/storage/common/storage'; import { Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { coalesce, equals } from 'vs/base/common/arrays'; +import { equals } from 'vs/base/common/arrays'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { ToggleStatusbarVisibilityAction } from 'vs/workbench/browser/actions/layoutActions'; import { assertIsDefined } from 'vs/base/common/types'; @@ -176,7 +176,7 @@ export class StatusbarPart extends Part implements IStatusbarService { private doAddEntry(entry: IStatusbarEntry, id: string, alignment: StatusbarAlignment, priority: IStatusbarEntryPriority): IStatusbarEntryAccessor { // View model item - const itemContainer = this.doCreateStatusItem(id, alignment, ...coalesce([entry.showBeak ? 'has-beak' : undefined])); + const itemContainer = this.doCreateStatusItem(id, alignment); const item = this.instantiationService.createInstance(StatusbarEntryItem, itemContainer, entry, this.hoverDelegate); // View model entry diff --git a/src/vs/workbench/common/theme.ts b/src/vs/workbench/common/theme.ts index bc3da309318..a4a46546750 100644 --- a/src/vs/workbench/common/theme.ts +++ b/src/vs/workbench/common/theme.ts @@ -515,28 +515,28 @@ export const STATUS_BAR_PROMINENT_ITEM_FOREGROUND = registerColor('statusBarItem light: STATUS_BAR_FOREGROUND, hcDark: STATUS_BAR_FOREGROUND, hcLight: STATUS_BAR_FOREGROUND -}, localize('statusBarProminentItemForeground', "Status bar prominent items foreground color. Prominent items stand out from other status bar entries to indicate importance. Change mode `Toggle Tab Key Moves Focus` from command palette to see an example. The status bar is shown in the bottom of the window.")); +}, localize('statusBarProminentItemForeground', "Status bar prominent items foreground color. Prominent items stand out from other status bar entries to indicate importance. The status bar is shown in the bottom of the window.")); export const STATUS_BAR_PROMINENT_ITEM_BACKGROUND = registerColor('statusBarItem.prominentBackground', { dark: Color.black.transparent(0.5), light: Color.black.transparent(0.5), hcDark: Color.black.transparent(0.5), hcLight: Color.black.transparent(0.5), -}, localize('statusBarProminentItemBackground', "Status bar prominent items background color. Prominent items stand out from other status bar entries to indicate importance. Change mode `Toggle Tab Key Moves Focus` from command palette to see an example. The status bar is shown in the bottom of the window.")); +}, localize('statusBarProminentItemBackground', "Status bar prominent items background color. Prominent items stand out from other status bar entries to indicate importance. The status bar is shown in the bottom of the window.")); + +export const STATUS_BAR_PROMINENT_ITEM_HOVER_FOREGROUND = registerColor('statusBarItem.prominentHoverForeground', { + dark: STATUS_BAR_ITEM_HOVER_FOREGROUND, + light: STATUS_BAR_ITEM_HOVER_FOREGROUND, + hcDark: STATUS_BAR_ITEM_HOVER_FOREGROUND, + hcLight: STATUS_BAR_ITEM_HOVER_FOREGROUND +}, localize('statusBarProminentItemHoverForeground', "Status bar prominent items foreground color when hovering. Prominent items stand out from other status bar entries to indicate importance. The status bar is shown in the bottom of the window.")); export const STATUS_BAR_PROMINENT_ITEM_HOVER_BACKGROUND = registerColor('statusBarItem.prominentHoverBackground', { dark: Color.black.transparent(0.3), light: Color.black.transparent(0.3), hcDark: Color.black.transparent(0.3), hcLight: null -}, localize('statusBarProminentItemHoverBackground', "Status bar prominent items background color when hovering. Prominent items stand out from other status bar entries to indicate importance. Change mode `Toggle Tab Key Moves Focus` from command palette to see an example. The status bar is shown in the bottom of the window.")); - -export const STATUS_BAR_PROMINENT_ITEM_HOVER_FOREGROUND = registerColor('statusBarItem.prominentHoverForeground', { - dark: STATUS_BAR_FOREGROUND, - light: STATUS_BAR_FOREGROUND, - hcDark: STATUS_BAR_FOREGROUND, - hcLight: STATUS_BAR_FOREGROUND -}, localize('statusBarProminentItemHoverForeground', "Status bar prominent items foreground color when hovering. Prominent items stand out from other status bar entries to indicate importance. Change mode `Toggle Tab Key Moves Focus` from command palette to see an example. The status bar is shown in the bottom of the window.")); +}, localize('statusBarProminentItemHoverBackground', "Status bar prominent items background color when hovering. Prominent items stand out from other status bar entries to indicate importance. The status bar is shown in the bottom of the window.")); export const STATUS_BAR_ERROR_ITEM_BACKGROUND = registerColor('statusBarItem.errorBackground', { dark: darken(errorForeground, .4), @@ -552,6 +552,20 @@ export const STATUS_BAR_ERROR_ITEM_FOREGROUND = registerColor('statusBarItem.err hcLight: Color.white }, localize('statusBarErrorItemForeground', "Status bar error items foreground color. Error items stand out from other status bar entries to indicate error conditions. The status bar is shown in the bottom of the window.")); +export const STATUS_BAR_ERROR_ITEM_HOVER_FOREGROUND = registerColor('statusBarItem.errorHoverForeground', { + dark: STATUS_BAR_ITEM_HOVER_FOREGROUND, + light: STATUS_BAR_ITEM_HOVER_FOREGROUND, + hcDark: STATUS_BAR_ITEM_HOVER_FOREGROUND, + hcLight: STATUS_BAR_ITEM_HOVER_FOREGROUND +}, localize('statusBarErrorItemHoverForeground', "Status bar error items foreground color when hovering. Error items stand out from other status bar entries to indicate error conditions. The status bar is shown in the bottom of the window.")); + +export const STATUS_BAR_ERROR_ITEM_HOVER_BACKGROUND = registerColor('statusBarItem.errorHoverBackground', { + dark: STATUS_BAR_ITEM_HOVER_BACKGROUND, + light: STATUS_BAR_ITEM_HOVER_BACKGROUND, + hcDark: STATUS_BAR_ITEM_HOVER_BACKGROUND, + hcLight: STATUS_BAR_ITEM_HOVER_BACKGROUND +}, localize('statusBarErrorItemHoverBackground', "Status bar error items background color when hovering. Error items stand out from other status bar entries to indicate error conditions. The status bar is shown in the bottom of the window.")); + export const STATUS_BAR_WARNING_ITEM_BACKGROUND = registerColor('statusBarItem.warningBackground', { dark: darken(editorWarningForeground, .4), light: darken(editorWarningForeground, .4), @@ -566,6 +580,20 @@ export const STATUS_BAR_WARNING_ITEM_FOREGROUND = registerColor('statusBarItem.w hcLight: Color.white }, localize('statusBarWarningItemForeground', "Status bar warning items foreground color. Warning items stand out from other status bar entries to indicate warning conditions. The status bar is shown in the bottom of the window.")); +export const STATUS_BAR_WARNING_ITEM_HOVER_FOREGROUND = registerColor('statusBarItem.warningHoverForeground', { + dark: STATUS_BAR_ITEM_HOVER_FOREGROUND, + light: STATUS_BAR_ITEM_HOVER_FOREGROUND, + hcDark: STATUS_BAR_ITEM_HOVER_FOREGROUND, + hcLight: STATUS_BAR_ITEM_HOVER_FOREGROUND +}, localize('statusBarWarningItemHoverForeground', "Status bar warning items foreground color when hovering. Warning items stand out from other status bar entries to indicate warning conditions. The status bar is shown in the bottom of the window.")); + +export const STATUS_BAR_WARNING_ITEM_HOVER_BACKGROUND = registerColor('statusBarItem.warningHoverBackground', { + dark: STATUS_BAR_ITEM_HOVER_BACKGROUND, + light: STATUS_BAR_ITEM_HOVER_BACKGROUND, + hcDark: STATUS_BAR_ITEM_HOVER_BACKGROUND, + hcLight: STATUS_BAR_ITEM_HOVER_BACKGROUND +}, localize('statusBarWarningItemHoverBackground', "Status bar warning items background color when hovering. Warning items stand out from other status bar entries to indicate warning conditions. The status bar is shown in the bottom of the window.")); + // < --- Activity Bar --- > @@ -657,20 +685,62 @@ export const PROFILE_BADGE_FOREGROUND = registerColor('profileBadge.foreground', // < --- Remote --- > -export const STATUS_BAR_HOST_NAME_BACKGROUND = registerColor('statusBarItem.remoteBackground', { +export const STATUS_BAR_REMOTE_ITEM_BACKGROUND = registerColor('statusBarItem.remoteBackground', { dark: ACTIVITY_BAR_BADGE_BACKGROUND, light: ACTIVITY_BAR_BADGE_BACKGROUND, hcDark: ACTIVITY_BAR_BADGE_BACKGROUND, hcLight: ACTIVITY_BAR_BADGE_BACKGROUND }, localize('statusBarItemHostBackground', "Background color for the remote indicator on the status bar.")); -export const STATUS_BAR_HOST_NAME_FOREGROUND = registerColor('statusBarItem.remoteForeground', { +export const STATUS_BAR_REMOTE_ITEM_FOREGROUND = registerColor('statusBarItem.remoteForeground', { dark: ACTIVITY_BAR_BADGE_FOREGROUND, light: ACTIVITY_BAR_BADGE_FOREGROUND, hcDark: ACTIVITY_BAR_BADGE_FOREGROUND, hcLight: ACTIVITY_BAR_BADGE_FOREGROUND }, localize('statusBarItemHostForeground', "Foreground color for the remote indicator on the status bar.")); +export const STATUS_BAR_REMOTE_ITEM_HOVER_FOREGROUND = registerColor('statusBarItem.remoteHoverForeground', { + dark: STATUS_BAR_ITEM_HOVER_FOREGROUND, + light: STATUS_BAR_ITEM_HOVER_FOREGROUND, + hcDark: STATUS_BAR_ITEM_HOVER_FOREGROUND, + hcLight: STATUS_BAR_ITEM_HOVER_FOREGROUND +}, localize('statusBarRemoteItemHoverForeground', "Foreground color for the remote indicator on the status bar when hovering.")); + +export const STATUS_BAR_REMOTE_ITEM_HOVER_BACKGROUND = registerColor('statusBarItem.remoteHoverBackground', { + dark: STATUS_BAR_ITEM_HOVER_BACKGROUND, + light: STATUS_BAR_ITEM_HOVER_BACKGROUND, + hcDark: STATUS_BAR_ITEM_HOVER_BACKGROUND, + hcLight: null +}, localize('statusBarRemoteItemHoverBackground', "Background color for the remote indicator on the status bar when hovering.")); + +export const STATUS_BAR_OFFLINE_ITEM_BACKGROUND = registerColor('statusBarItem.offlineBackground', { + dark: '#6c1717', + light: '#6c1717', + hcDark: '#6c1717', + hcLight: '#6c1717' +}, localize('statusBarItemOfflineBackground', "Status bar item background color when the workbench is offline.")); + +export const STATUS_BAR_OFFLINE_ITEM_FOREGROUND = registerColor('statusBarItem.offlineForeground', { + dark: STATUS_BAR_REMOTE_ITEM_FOREGROUND, + light: STATUS_BAR_REMOTE_ITEM_FOREGROUND, + hcDark: STATUS_BAR_REMOTE_ITEM_FOREGROUND, + hcLight: STATUS_BAR_REMOTE_ITEM_FOREGROUND +}, localize('statusBarItemOfflineForeground', "Status bar item foreground color when the workbench is offline.")); + +export const STATUS_BAR_OFFLINE_ITEM_HOVER_FOREGROUND = registerColor('statusBarItem.offlineHoverForeground', { + dark: STATUS_BAR_ITEM_HOVER_FOREGROUND, + light: STATUS_BAR_ITEM_HOVER_FOREGROUND, + hcDark: STATUS_BAR_ITEM_HOVER_FOREGROUND, + hcLight: STATUS_BAR_ITEM_HOVER_FOREGROUND +}, localize('statusBarOfflineItemHoverForeground', "Status bar item foreground hover color when the workbench is offline.")); + +export const STATUS_BAR_OFFLINE_ITEM_HOVER_BACKGROUND = registerColor('statusBarItem.offlineHoverBackground', { + dark: STATUS_BAR_ITEM_HOVER_BACKGROUND, + light: STATUS_BAR_ITEM_HOVER_BACKGROUND, + hcDark: STATUS_BAR_ITEM_HOVER_BACKGROUND, + hcLight: null +}, localize('statusBarOfflineItemHoverBackground', "Status bar item background hover color when the workbench is offline.")); + export const EXTENSION_BADGE_REMOTE_BACKGROUND = registerColor('extensionBadge.remoteBackground', { dark: ACTIVITY_BAR_BADGE_BACKGROUND, light: ACTIVITY_BAR_BADGE_BACKGROUND, diff --git a/src/vs/workbench/contrib/languageStatus/browser/languageStatus.contribution.ts b/src/vs/workbench/contrib/languageStatus/browser/languageStatus.contribution.ts index af697829ec1..3dd004897d2 100644 --- a/src/vs/workbench/contrib/languageStatus/browser/languageStatus.contribution.ts +++ b/src/vs/workbench/contrib/languageStatus/browser/languageStatus.contribution.ts @@ -11,14 +11,12 @@ import Severity from 'vs/base/common/severity'; import { getCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { localize } from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; -import { themeColorFromId } from 'vs/platform/theme/common/themeService'; -import { ThemeColor, ThemeIcon } from 'vs/base/common/themables'; +import { ThemeIcon } from 'vs/base/common/themables'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { STATUS_BAR_ERROR_ITEM_BACKGROUND, STATUS_BAR_ERROR_ITEM_FOREGROUND, STATUS_BAR_WARNING_ITEM_BACKGROUND, STATUS_BAR_WARNING_ITEM_FOREGROUND } from 'vs/workbench/common/theme'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ILanguageStatus, ILanguageStatusService } from 'vs/workbench/services/languageStatus/common/languageStatusService'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import { IStatusbarEntry, IStatusbarEntryAccessor, IStatusbarService, ShowTooltipCommand, StatusbarAlignment } from 'vs/workbench/services/statusbar/browser/statusbar'; +import { IStatusbarEntry, IStatusbarEntryAccessor, IStatusbarService, ShowTooltipCommand, StatusbarAlignment, StatusbarEntryKind } from 'vs/workbench/services/statusbar/browser/statusbar'; import { parseLinkedText } from 'vs/base/common/linkedText'; import { Link } from 'vs/platform/opener/browser/link'; import { IOpenerService } from 'vs/platform/opener/common/opener'; @@ -367,14 +365,11 @@ class EditorStatusContribution implements IWorkbenchContribution { private static _asStatusbarEntry(item: ILanguageStatus): IStatusbarEntry { - let color: ThemeColor | undefined; - let backgroundColor: ThemeColor | undefined; + let kind: StatusbarEntryKind | undefined; if (item.severity === Severity.Warning) { - color = themeColorFromId(STATUS_BAR_WARNING_ITEM_FOREGROUND); - backgroundColor = themeColorFromId(STATUS_BAR_WARNING_ITEM_BACKGROUND); + kind = 'warning'; } else if (item.severity === Severity.Error) { - color = themeColorFromId(STATUS_BAR_ERROR_ITEM_FOREGROUND); - backgroundColor = themeColorFromId(STATUS_BAR_ERROR_ITEM_BACKGROUND); + kind = 'error'; } return { @@ -383,8 +378,7 @@ class EditorStatusContribution implements IWorkbenchContribution { ariaLabel: item.accessibilityInfo?.label ?? item.label, role: item.accessibilityInfo?.role, tooltip: item.command?.tooltip || new MarkdownString(item.detail, { isTrusted: true, supportThemeIcons: true }), - color, - backgroundColor, + kind, command: item.command }; } diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts b/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts index e7c3a8d12c2..633dedb4a4b 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts @@ -167,7 +167,7 @@ export class KernelStatus extends Disposable implements IWorkbenchContribution { text: nls.localize('kernel.select.label', "Select Kernel"), ariaLabel: nls.localize('kernel.select.label', "Select Kernel"), command: SELECT_KERNEL_ID, - backgroundColor: { id: 'statusBarItem.prominentBackground' } + kind: 'prominent' }, SELECT_KERNEL_ID, StatusbarAlignment.RIGHT, diff --git a/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts b/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts index 3130f1911ca..cab8b0d506e 100644 --- a/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts +++ b/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts @@ -4,8 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { STATUS_BAR_HOST_NAME_BACKGROUND, STATUS_BAR_HOST_NAME_FOREGROUND } from 'vs/workbench/common/theme'; -import { themeColorFromId } from 'vs/platform/theme/common/themeService'; import { IRemoteAgentService, remoteConnectionLatencyMeasurer } from 'vs/workbench/services/remote/common/remoteAgentService'; import { RunOnceScheduler, retry } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; @@ -45,7 +43,6 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { IProductService } from 'vs/platform/product/common/productService'; import { DomEmitter } from 'vs/base/browser/event'; -import { registerColor } from 'vs/platform/theme/common/colorRegistry'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ThemeIcon } from 'vs/base/common/themables'; @@ -53,20 +50,6 @@ import { infoIcon } from 'vs/workbench/contrib/extensions/browser/extensionsIcon import { IOpenerService } from 'vs/platform/opener/common/opener'; import { URI } from 'vs/base/common/uri'; -export const STATUS_BAR_OFFLINE_BACKGROUND = registerColor('statusBar.offlineBackground', { - dark: '#6c1717', - light: '#6c1717', - hcDark: '#6c1717', - hcLight: '#6c1717' -}, nls.localize('statusBarOfflineBackground', "Status bar background color when the workbench is offline. The status bar is shown in the bottom of the window")); - -export const STATUS_BAR_OFFLINE_FOREGROUND = registerColor('statusBar.offlineForeground', { - dark: STATUS_BAR_HOST_NAME_FOREGROUND, - light: STATUS_BAR_HOST_NAME_FOREGROUND, - hcDark: STATUS_BAR_HOST_NAME_FOREGROUND, - hcLight: STATUS_BAR_HOST_NAME_FOREGROUND -}, nls.localize('statusBarOfflineForeground', "Status bar foreground color when the workbench is offline. The status bar is shown in the bottom of the window")); - type ActionGroup = [string, Array]; interface RemoteExtensionMetadata { @@ -558,8 +541,7 @@ export class RemoteStatusIndicator extends Disposable implements IWorkbenchContr const properties: IStatusbarEntry = { name: nls.localize('remoteHost', "Remote Host"), - backgroundColor: themeColorFromId(this.networkState === 'offline' ? STATUS_BAR_OFFLINE_BACKGROUND : STATUS_BAR_HOST_NAME_BACKGROUND), - color: themeColorFromId(this.networkState === 'offline' ? STATUS_BAR_OFFLINE_FOREGROUND : STATUS_BAR_HOST_NAME_FOREGROUND), + kind: this.networkState === 'offline' ? 'offline' : 'remote', ariaLabel, text, showProgress, diff --git a/src/vs/workbench/contrib/remote/browser/tunnelView.ts b/src/vs/workbench/contrib/remote/browser/tunnelView.ts index 8f94225be44..ac01011049a 100644 --- a/src/vs/workbench/contrib/remote/browser/tunnelView.ts +++ b/src/vs/workbench/contrib/remote/browser/tunnelView.ts @@ -52,7 +52,7 @@ import { registerColor } from 'vs/platform/theme/common/colorRegistry'; import { IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent'; import { IHoverDelegateOptions } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; import { IHoverService } from 'vs/workbench/services/hover/browser/hover'; -import { STATUS_BAR_HOST_NAME_BACKGROUND } from 'vs/workbench/common/theme'; +import { STATUS_BAR_REMOTE_ITEM_BACKGROUND } from 'vs/workbench/common/theme'; import { Codicon } from 'vs/base/common/codicons'; import { defaultButtonStyles, defaultInputBoxStyles } from 'vs/platform/theme/browser/defaultStyles'; import { Attributes, CandidatePort, Tunnel, TunnelCloseReason, TunnelModel, TunnelSource, forwardedPortsViewEnabled, makeAddress, mapHasAddressLocalhostOrAllInterfaces, parseAddress } from 'vs/workbench/services/remote/common/tunnelModel'; @@ -1775,9 +1775,9 @@ MenuRegistry.appendMenuItem(MenuId.TunnelLocalAddressInline, ({ })); registerColor('ports.iconRunningProcessForeground', { - light: STATUS_BAR_HOST_NAME_BACKGROUND, - dark: STATUS_BAR_HOST_NAME_BACKGROUND, - hcDark: STATUS_BAR_HOST_NAME_BACKGROUND, - hcLight: STATUS_BAR_HOST_NAME_BACKGROUND + light: STATUS_BAR_REMOTE_ITEM_BACKGROUND, + dark: STATUS_BAR_REMOTE_ITEM_BACKGROUND, + hcDark: STATUS_BAR_REMOTE_ITEM_BACKGROUND, + hcLight: STATUS_BAR_REMOTE_ITEM_BACKGROUND }, nls.localize('portWithRunningProcess.foreground', "The color of the icon for a port that has an associated running process.")); diff --git a/src/vs/workbench/contrib/terminal/browser/baseTerminalBackend.ts b/src/vs/workbench/contrib/terminal/browser/baseTerminalBackend.ts index fcb01ae3e84..7710088cf64 100644 --- a/src/vs/workbench/contrib/terminal/browser/baseTerminalBackend.ts +++ b/src/vs/workbench/contrib/terminal/browser/baseTerminalBackend.ts @@ -8,9 +8,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { localize } from 'vs/nls'; import { ICrossVersionSerializedTerminalState, IPtyHostController, ISerializedTerminalState, ITerminalLogService } from 'vs/platform/terminal/common/terminal'; -import { themeColorFromId } from 'vs/platform/theme/common/themeService'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { STATUS_BAR_WARNING_ITEM_BACKGROUND, STATUS_BAR_WARNING_ITEM_FOREGROUND } from 'vs/workbench/common/theme'; import { TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; @@ -68,8 +66,7 @@ export abstract class BaseTerminalBackend extends Disposable { tooltip: localize('nonResponsivePtyHost', "The connection to the terminal's pty host process is unresponsive, terminals may stop working. Click to manually restart the pty host."), ariaLabel: localize('ptyHostStatus.ariaLabel', 'Pty Host is unresponsive'), command: TerminalCommandId.RestartPtyHost, - backgroundColor: themeColorFromId(STATUS_BAR_WARNING_ITEM_BACKGROUND), - color: themeColorFromId(STATUS_BAR_WARNING_ITEM_FOREGROUND), + kind: 'warning' }; } statusBarAccessor = statusBarService.addEntry(unresponsiveStatusBarEntry, 'ptyHostStatus', StatusbarAlignment.LEFT); diff --git a/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts b/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts index 6d705492fe9..8bc95f4f591 100644 --- a/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts +++ b/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts @@ -32,7 +32,6 @@ import { isEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleF import { dirname, resolve } from 'vs/base/common/path'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent'; -import { STATUS_BAR_PROMINENT_ITEM_BACKGROUND, STATUS_BAR_PROMINENT_ITEM_FOREGROUND } from 'vs/workbench/common/theme'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IBannerItem, IBannerService } from 'vs/workbench/services/banner/browser/bannerService'; @@ -527,9 +526,6 @@ export class WorkspaceTrustUXHandler extends Disposable implements IWorkbenchCon private getStatusbarEntry(trusted: boolean): IStatusbarEntry { const text = workspaceTrustToString(trusted); - const backgroundColor = { id: STATUS_BAR_PROMINENT_ITEM_BACKGROUND }; - const color = { id: STATUS_BAR_PROMINENT_ITEM_FOREGROUND }; - let ariaLabel = ''; let toolTip: IMarkdownString | string | undefined; switch (this.workspaceContextService.getWorkbenchState()) { @@ -586,8 +582,7 @@ export class WorkspaceTrustUXHandler extends Disposable implements IWorkbenchCon ariaLabel: ariaLabel, tooltip: toolTip, command: MANAGE_TRUST_COMMAND_ID, - backgroundColor, - color + kind: 'prominent' }; } diff --git a/src/vs/workbench/services/statusbar/browser/statusbar.ts b/src/vs/workbench/services/statusbar/browser/statusbar.ts index e38c45d1cf3..2e5abde6fba 100644 --- a/src/vs/workbench/services/statusbar/browser/statusbar.ts +++ b/src/vs/workbench/services/statusbar/browser/statusbar.ts @@ -153,6 +153,9 @@ export interface IStatusbarStyleOverride { readonly border?: ColorIdentifier; } +export type StatusbarEntryKind = 'standard' | 'warning' | 'error' | 'prominent' | 'remote' | 'offline'; +export const StatusbarEntryKinds: StatusbarEntryKind[] = ['standard', 'warning', 'error', 'prominent', 'remote', 'offline']; + /** * A declarative way of describing a status bar entry */ @@ -188,12 +191,16 @@ export interface IStatusbarEntry { readonly tooltip?: string | IMarkdownString | HTMLElement; /** - * An optional color to use for the entry + * An optional color to use for the entry. + * + * @deprecated Use `kind` instead to support themable hover styles. */ readonly color?: string | ThemeColor; /** - * An optional background color to use for the entry + * An optional background color to use for the entry. + * + * @deprecated Use `kind` instead to support themable hover styles. */ readonly backgroundColor?: string | ThemeColor; @@ -215,6 +222,11 @@ export interface IStatusbarEntry { * specified, `syncing` will be used. */ readonly showProgress?: boolean | 'syncing' | 'loading'; + + /** + * The kind of status bar entry. This applies different colors to the entry. + */ + readonly kind?: StatusbarEntryKind; } export interface IStatusbarEntryAccessor extends IDisposable { From 02c34a9476ed77ebb4198ea00464286f79a39f47 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 22 Aug 2023 03:43:50 -0700 Subject: [PATCH 177/223] Add explain quick fix kind This hasn't gone through the API process yet. We need some way of indicating the quick fix kind such that they can be presented in a different way. The main use case here is a sparkle icon for AI suggests but it could be expanded in the future Part of #162950 --- src/vs/platform/terminal/common/terminal.ts | 1 + .../terminal/browser/media/terminal.css | 5 ++ .../quickFix/browser/quickFix.ts | 1 + .../quickFix/browser/quickFixAddon.ts | 53 +++++++++++++------ .../browser/terminalQuickFixService.ts | 8 +++ 5 files changed, 51 insertions(+), 17 deletions(-) diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index 9dee3d5b41a..235d8efdbe8 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -966,6 +966,7 @@ export interface ITerminalCommandSelector { outputMatcher?: ITerminalOutputMatcher; exitStatus: boolean; commandExitResult: 'success' | 'error'; + kind?: 'fix' | 'explain'; } export interface ITerminalBackend { diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index 1b240eafdab..96e52e4816c 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -486,6 +486,11 @@ color: var(--vscode-editorLightBulb-foreground) !important; background-color: var(--vscode-terminal-background, --vscode-panel-background); } +.monaco-workbench .terminal .terminal-command-decoration.quick-fix.explainOnly { + /* Use success background to blend in with the terminal better as it's lower priority. We will + * probably want to add an explicit color for this eventually. */ + color: var(--vscode-terminalCommandDecoration-successBackground) !important; +} .terminal-scroll-highlight { left: 0; diff --git a/src/vs/workbench/contrib/terminalContrib/quickFix/browser/quickFix.ts b/src/vs/workbench/contrib/terminalContrib/quickFix/browser/quickFix.ts index 8a13380b971..f576102fe85 100644 --- a/src/vs/workbench/contrib/terminalContrib/quickFix/browser/quickFix.ts +++ b/src/vs/workbench/contrib/terminalContrib/quickFix/browser/quickFix.ts @@ -56,6 +56,7 @@ export interface ITerminalQuickFixOptions { commandLineMatcher: string | RegExp; outputMatcher?: ITerminalOutputMatcher; commandExitResult: 'success' | 'error'; + kind?: 'fix' | 'explain'; } export interface ITerminalQuickFix { diff --git a/src/vs/workbench/contrib/terminalContrib/quickFix/browser/quickFixAddon.ts b/src/vs/workbench/contrib/terminalContrib/quickFix/browser/quickFixAddon.ts index 0631a1e4e04..9f1ccc65e8c 100644 --- a/src/vs/workbench/contrib/terminalContrib/quickFix/browser/quickFixAddon.ts +++ b/src/vs/workbench/contrib/terminalContrib/quickFix/browser/quickFixAddon.ts @@ -36,9 +36,8 @@ import { Codicon } from 'vs/base/common/codicons'; import { ThemeIcon } from 'vs/base/common/themables'; import { ICommandService } from 'vs/platform/commands/common/commands'; -const quickFixSelectors = [ +const quickFixClasses = [ DecorationSelector.QuickFix, - DecorationSelector.LightBulb, DecorationSelector.Codicon, DecorationSelector.CommandDecoration, DecorationSelector.XtermDecoration @@ -116,7 +115,7 @@ export class TerminalQuickFixAddon extends Disposable implements ITerminalAddon, } // TODO: What's documentation do? Need a vscode command? - const actions = this._currentRenderContext.quickFixes.map(f => new TerminalQuickFixItem(f, f.type, f.source, f.label)); + const actions = this._currentRenderContext.quickFixes.map(f => new TerminalQuickFixItem(f, f.type, f.source, f.label, f.kind)); const documentation = this._currentRenderContext.quickFixes.map(f => { return { id: f.source, title: f.label, tooltip: f.source }; }); const actionSet = { // TODO: Documentation and actions are separate? @@ -150,7 +149,8 @@ export class TerminalQuickFixAddon extends Disposable implements ITerminalAddon, type: 'unresolved', commandLineMatcher: selector.commandLineMatcher, outputMatcher: selector.outputMatcher, - commandExitResult: selector.commandExitResult + commandExitResult: selector.commandExitResult, + kind: selector.kind }); this._registeredSelectors.add(selector.id); this._commandListeners.set(matcherKey, currentOptions); @@ -192,7 +192,14 @@ export class TerminalQuickFixAddon extends Disposable implements ITerminalAddon, } const id = selector.id; await this._extensionService.activateByEvent(`onTerminalQuickFixRequest:${id}`); - return this._quickFixService.providers.get(id)?.provideTerminalQuickFixes(command, lines, { type: 'resolved', commandLineMatcher: selector.commandLineMatcher, outputMatcher: selector.outputMatcher, commandExitResult: selector.commandExitResult, id: selector.id }, new CancellationTokenSource().token); + return this._quickFixService.providers.get(id)?.provideTerminalQuickFixes(command, lines, { + type: 'resolved', + commandLineMatcher: selector.commandLineMatcher, + outputMatcher: selector.outputMatcher, + commandExitResult: selector.commandExitResult, + kind: selector.kind, + id: selector.id + }, new CancellationTokenSource().token); }; const result = await getQuickFixesForCommand(aliases, terminal, command, this._commandListeners, this._commandService, this._openerService, this._labelService, this._onDidRequestRerunCommand, resolver); if (!result) { @@ -266,7 +273,13 @@ export class TerminalQuickFixAddon extends Disposable implements ITerminalAddon, return; } - e.classList.add(...quickFixSelectors); + e.classList.add(...quickFixClasses); + const isExplainOnly = fixes.every(e => e.kind === 'explain'); + if (isExplainOnly) { + e.classList.add('explainOnly'); + } + e.classList.add(...ThemeIcon.asClassNameArray(isExplainOnly ? Codicon.sparkle : Codicon.lightBulb)); + updateLayout(this._configurationService, e); this._audioCueService.playAudioCue(AudioCue.terminalQuickFix); @@ -285,6 +298,7 @@ export class TerminalQuickFixAddon extends Disposable implements ITerminalAddon, export interface ITerminalAction extends IAction { type: TerminalQuickFixType; + kind?: 'fix' | 'explain'; source: string; uri?: URI; command?: string; @@ -351,6 +365,7 @@ export async function getQuickFixesForCommand( const label = localize('quickFix.command', 'Run: {0}', fix.terminalCommand); action = { type: TerminalQuickFixType.TerminalCommand, + kind: option.kind, class: undefined, source: quickFix.source, id: quickFix.id, @@ -384,6 +399,7 @@ export async function getQuickFixesForCommand( id: quickFix.id, label, type: TerminalQuickFixType.Opener, + kind: option.kind, class: undefined, enabled: true, run: () => openerService.open(fix.uri), @@ -397,6 +413,7 @@ export async function getQuickFixesForCommand( action = { source: 'builtin', type: fix.type, + kind: option.kind, id: fix.id, label: fix.label, class: fix.class, @@ -413,6 +430,7 @@ export async function getQuickFixesForCommand( action = { source: quickFix.source, type: fix.type, + kind: option.kind, id: fix.id, label: fix.title, class: undefined, @@ -441,22 +459,20 @@ function convertToQuickFixOptions(selectorProvider: ITerminalQuickFixProviderSel commandLineMatcher: selectorProvider.selector.commandLineMatcher, outputMatcher: selectorProvider.selector.outputMatcher, commandExitResult: selectorProvider.selector.commandExitResult, + kind: selectorProvider.selector.kind, getQuickFixes: selectorProvider.provider.provideTerminalQuickFixes }; } class TerminalQuickFixItem { - action: ITerminalAction; - type: TerminalQuickFixType; - disabled?: boolean; - title?: string; - source: string; - constructor(action: ITerminalAction, type: TerminalQuickFixType, source: string, title?: string, disabled?: boolean) { - this.action = action; - this.disabled = disabled; - this.title = title; - this.source = source; - this.type = type; + readonly disabled = false; + constructor( + readonly action: ITerminalAction, + readonly type: TerminalQuickFixType, + readonly source: string, + readonly title: string | undefined, + readonly kind: 'fix' | 'explain' = 'fix' + ) { } } @@ -488,6 +504,9 @@ function toActionWidgetItems(inputQuickFixes: readonly TerminalQuickFixItem[], s } function getQuickFixIcon(quickFix: TerminalQuickFixItem): ThemeIcon { + if (quickFix.kind === 'explain') { + return Codicon.sparkle; + } switch (quickFix.type) { case TerminalQuickFixType.Opener: if ('uri' in quickFix.action && quickFix.action.uri) { diff --git a/src/vs/workbench/contrib/terminalContrib/quickFix/browser/terminalQuickFixService.ts b/src/vs/workbench/contrib/terminalContrib/quickFix/browser/terminalQuickFixService.ts index 4119c99c047..61b17085bb6 100644 --- a/src/vs/workbench/contrib/terminalContrib/quickFix/browser/terminalQuickFixService.ts +++ b/src/vs/workbench/contrib/terminalContrib/quickFix/browser/terminalQuickFixService.ts @@ -139,6 +139,14 @@ const quickFixExtensionPoint = ExtensionsRegistry.registerExtensionPoint Date: Tue, 22 Aug 2023 03:47:16 -0700 Subject: [PATCH 178/223] Fix var fallback --- src/vs/workbench/contrib/terminal/browser/media/terminal.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index 96e52e4816c..7bf0d8cd1c0 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -484,7 +484,7 @@ } .monaco-workbench .terminal .terminal-command-decoration.quick-fix { color: var(--vscode-editorLightBulb-foreground) !important; - background-color: var(--vscode-terminal-background, --vscode-panel-background); + background-color: var(--vscode-terminal-background, var(--vscode-panel-background)); } .monaco-workbench .terminal .terminal-command-decoration.quick-fix.explainOnly { /* Use success background to blend in with the terminal better as it's lower priority. We will From 5f97b6bf4fbdaf490152ecf88badb9f59f866e80 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 22 Aug 2023 03:55:36 -0700 Subject: [PATCH 179/223] Remove un used enum value --- .../contrib/terminal/browser/xterm/decorationStyles.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/decorationStyles.ts b/src/vs/workbench/contrib/terminal/browser/xterm/decorationStyles.ts index 46ee2d10d7a..4d475759e0b 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/decorationStyles.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/decorationStyles.ts @@ -29,8 +29,7 @@ export const enum DecorationSelector { Codicon = 'codicon', XtermDecoration = 'xterm-decoration', OverviewRuler = '.xterm-decoration-overview-ruler', - QuickFix = 'quick-fix', - LightBulb = 'codicon-light-bulb' + QuickFix = 'quick-fix' } export class TerminalDecorationHoverManager extends Disposable { From b191ce214320cff5c1d89e0d4a511e04a958eb34 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 22 Aug 2023 03:57:54 -0700 Subject: [PATCH 180/223] Fix var fallback in terminal Fixes #190965 --- .../contrib/terminal/browser/media/terminal.css | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index 1b240eafdab..3e92460720e 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -20,7 +20,7 @@ visibility: hidden; } .monaco-workbench .part.panel .pane-body.integrated-terminal .terminal-outer-container { - background-color: var(--vscode-terminal-background, --vscode-panel-background); + background-color: var(--vscode-terminal-background, var(--vscode-panel-background)); } .monaco-workbench .pane-body.integrated-terminal .terminal-outer-container, .monaco-workbench .pane-body.integrated-terminal .terminal-groups-container, @@ -32,7 +32,7 @@ } .monaco-workbench .part.sidebar .pane-body.integrated-terminal .terminal-outer-container, .monaco-workbench .part.auxiliarybar .pane-body.integrated-terminal .terminal-outer-container { - background-color: var(--vscode-terminal-background, --vscode-sideBar-background); + background-color: var(--vscode-terminal-background, var(--vscode-sideBar-background)); } .monaco-workbench .pane-body.integrated-terminal .split-view-view:not(:first-child), @@ -41,7 +41,7 @@ } .monaco-workbench .pane-body.integrated-terminal .terminal-drop-overlay { - background-color: var(--vscode-terminal-dropBackground, --vscode-editorGroup-dropBackground); + background-color: var(--vscode-terminal-dropBackground, var(--vscode-editorGroup-dropBackground)); } .monaco-workbench .pane-body.integrated-terminal .terminal-tabs-entry.is-active::before { @@ -78,7 +78,7 @@ } .monaco-workbench .terminal-editor .terminal-wrapper { - background-color: var(--vscode-terminal-background, --vscode-editorPane-background); + background-color: var(--vscode-terminal-background, var(--vscode-editorPane-background)); } .monaco-workbench .terminal-editor .terminal-wrapper, .monaco-workbench .pane-body.integrated-terminal .terminal-wrapper { @@ -484,7 +484,7 @@ } .monaco-workbench .terminal .terminal-command-decoration.quick-fix { color: var(--vscode-editorLightBulb-foreground) !important; - background-color: var(--vscode-terminal-background, --vscode-panel-background); + background-color: var(--vscode-terminal-background, var(--vscode-panel-background)); } .terminal-scroll-highlight { @@ -583,7 +583,7 @@ pointer-events: all; opacity: 1; z-index: 33; - background-color: var(--vscode-terminal-background, --vscode-panel-background); + background-color: var(--vscode-terminal-background, var(--vscode-panel-background)); } .monaco-workbench .xterm.terminal.hide { From 515c6b204fefa52688c8ff92757ff69d0917a36f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 22 Aug 2023 15:27:52 +0200 Subject: [PATCH 181/223] Unicode codepoint 0x202E causes the default filename in the tab title to also be right to left (fix #190133) (#190980) --- .../untitled/common/untitledTextEditorModel.ts | 3 ++- .../untitled/test/browser/untitledTextEditor.test.ts | 10 +++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/services/untitled/common/untitledTextEditorModel.ts b/src/vs/workbench/services/untitled/common/untitledTextEditorModel.ts index 93d906d886e..4b745f7af41 100644 --- a/src/vs/workbench/services/untitled/common/untitledTextEditorModel.ts +++ b/src/vs/workbench/services/untitled/common/untitledTextEditorModel.ts @@ -419,7 +419,8 @@ export class UntitledTextEditorModel extends BaseTextEditorModel implements IUnt startColumn: 1, endColumn: UntitledTextEditorModel.FIRST_LINE_NAME_CANDIDATE_MAX_LENGTH + 1 // first cap at FIRST_LINE_NAME_CANDIDATE_MAX_LENGTH }) - .trim().replace(/\s+/g, ' '); // normalize whitespaces + .trim().replace(/\s+/g, ' ') // normalize whitespaces + .replace(/\u202E/g, ''); // drop Right-to-Left Override character (#190133) firstLineText = firstLineText.substr(0, getCharContainingOffset( // finally cap at FIRST_LINE_NAME_MAX_LENGTH (grapheme aware #111235) firstLineText, UntitledTextEditorModel.FIRST_LINE_NAME_MAX_LENGTH)[0] diff --git a/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts b/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts index c724a93fd1b..935b78a3dfd 100644 --- a/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts +++ b/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts @@ -542,10 +542,14 @@ suite('Untitled text editors', () => { assert.strictEqual(input.getName(), '123456789012345678901234567890123456789'); assert.strictEqual(model.name, '123456789012345678901234567890123456789'); - assert.strictEqual(counter, 6); + model.textEditorModel?.setValue('hello\u202Eworld'); // do not allow RTL in names (#190133) + assert.strictEqual(input.getName(), 'helloworld'); + assert.strictEqual(model.name, 'helloworld'); + + assert.strictEqual(counter, 7); model.textEditorModel?.setValue('Hello\nWorld'); - assert.strictEqual(counter, 7); + assert.strictEqual(counter, 8); function createSingleEditOp(text: string, positionLineNumber: number, positionColumn: number, selectionLineNumber: number = positionLineNumber, selectionColumn: number = positionColumn): ISingleEditOperation { const range = new Range( @@ -563,7 +567,7 @@ suite('Untitled text editors', () => { } model.textEditorModel?.applyEdits([createSingleEditOp('hello', 2, 2)]); - assert.strictEqual(counter, 7); // change was not on first line + assert.strictEqual(counter, 8); // change was not on first line input.dispose(); model.dispose(); From 74c02f917d71d9efe3d7be7e3d8d32433affdefb Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 22 Aug 2023 15:40:28 +0200 Subject: [PATCH 182/223] changing the fallback of a variable to variable --- src/vs/editor/browser/viewParts/lines/viewLines.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/browser/viewParts/lines/viewLines.css b/src/vs/editor/browser/viewParts/lines/viewLines.css index 8fff7ae3155..fe686d3e441 100644 --- a/src/vs/editor/browser/viewParts/lines/viewLines.css +++ b/src/vs/editor/browser/viewParts/lines/viewLines.css @@ -20,8 +20,8 @@ } .mtkoverflow { - background-color: var(--vscode-button-background, --vscode-editor-background); - color: var(--vscode-button-foreground, --vscode-editor-foreground); + background-color: var(--vscode-button-background, var(--vscode-editor-background)); + color: var(--vscode-button-foreground, var(--vscode-editor-foreground)); border-width: 1px; border-style: solid; border-color: var(--vscode-contrastBorder); From 2c9e2b85b2191c184f118e5fc99e093546e2cce1 Mon Sep 17 00:00:00 2001 From: Ulugbek Abdullaev Date: Tue, 22 Aug 2023 15:46:44 +0200 Subject: [PATCH 183/223] use `LanguageFeaturesService` to support `MappedEditsProvider`s instead of creating a new `MappedEditsService` --- src/vs/editor/common/languages.ts | 32 ++++++++- .../common/services/languageFeatures.ts | 4 +- .../services/languageFeaturesService.ts | 3 +- src/vs/monaco.d.ts | 24 +++++++ .../api/browser/extensionHost.contribution.ts | 1 - .../api/browser/mainThreadLanguageFeatures.ts | 21 ++++++ .../api/browser/mainThreadMappedEdits.ts | 57 ---------------- .../workbench/api/common/extHost.api.impl.ts | 4 +- .../workbench/api/common/extHost.protocol.ts | 15 +---- .../api/common/extHostLanguageFeatures.ts | 52 ++++++++++++++- .../api/common/extHostMappedEdits.ts | 61 ----------------- .../api/common/extHostTypeConverters.ts | 7 +- .../browser/actions/chatCodeblockActions.ts | 33 +++++++--- .../common/mappedEdits.contribution.ts | 44 ++++++++++--- .../mappedEdits/browser/mappedEditsService.ts | 51 --------------- .../mappedEdits/common/mappedEdits.ts | 65 ------------------- src/vs/workbench/workbench.common.main.ts | 1 - 17 files changed, 196 insertions(+), 279 deletions(-) delete mode 100644 src/vs/workbench/api/browser/mainThreadMappedEdits.ts delete mode 100644 src/vs/workbench/api/common/extHostMappedEdits.ts delete mode 100644 src/vs/workbench/services/mappedEdits/browser/mappedEditsService.ts delete mode 100644 src/vs/workbench/services/mappedEdits/common/mappedEdits.ts diff --git a/src/vs/editor/common/languages.ts b/src/vs/editor/common/languages.ts index 409b993d05b..15b63cf0386 100644 --- a/src/vs/editor/common/languages.ts +++ b/src/vs/editor/common/languages.ts @@ -16,7 +16,7 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import { EditOperation, ISingleEditOperation } from 'vs/editor/common/core/editOperation'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; -import { Selection } from 'vs/editor/common/core/selection'; +import { ISelection, Selection } from 'vs/editor/common/core/selection'; import { LanguageId } from 'vs/editor/common/encodedTokenAttributes'; import * as model from 'vs/editor/common/model'; import { TokenizationRegistry as TokenizationRegistryImpl } from 'vs/editor/common/tokenizationRegistry'; @@ -2033,3 +2033,33 @@ export interface DocumentOnDropEditProvider { provideDocumentOnDropEdits(model: model.ITextModel, position: IPosition, dataTransfer: IReadonlyVSDataTransfer, token: CancellationToken): ProviderResult; } + +export interface RelatedContextItem { + readonly uri: URI; + readonly range: IRange; +} + +export interface MappedEditsContext { + selections: ISelection[]; + related: RelatedContextItem[]; +} + +export interface MappedEditsProvider { + + /** + * Provider maps code blocks from the chat into a workspace edit. + * + * @param document The document to provide mapped edits for. + * @param codeBlocks Code blocks that come from an LLM's reply. + * "Insert at cursor" in the panel chat only sends one edit that the user clicks on, but inline chat can send multiple blocks and let the lang server decide what to do with them. + * @param context The context for providing mapped edits. + * @param token A cancellation token. + * @returns A provider result of text edits. + */ + provideMappedEdits( + document: model.ITextModel, + codeBlocks: string[], + context: MappedEditsContext, + token: CancellationToken + ): Promise; +} diff --git a/src/vs/editor/common/services/languageFeatures.ts b/src/vs/editor/common/services/languageFeatures.ts index 2c80887df1a..df53beaecdb 100644 --- a/src/vs/editor/common/services/languageFeatures.ts +++ b/src/vs/editor/common/services/languageFeatures.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { LanguageFeatureRegistry, NotebookInfoResolver } from 'vs/editor/common/languageFeatureRegistry'; -import { CodeActionProvider, CodeLensProvider, CompletionItemProvider, DeclarationProvider, DefinitionProvider, DocumentColorProvider, DocumentFormattingEditProvider, DocumentHighlightProvider, DocumentOnDropEditProvider, DocumentPasteEditProvider, DocumentRangeFormattingEditProvider, DocumentRangeSemanticTokensProvider, DocumentSemanticTokensProvider, DocumentSymbolProvider, EvaluatableExpressionProvider, FoldingRangeProvider, HoverProvider, ImplementationProvider, InlayHintsProvider, InlineCompletionsProvider, InlineValuesProvider, LinkedEditingRangeProvider, LinkProvider, OnTypeFormattingEditProvider, ReferenceProvider, RenameProvider, SelectionRangeProvider, SignatureHelpProvider, TypeDefinitionProvider } from 'vs/editor/common/languages'; +import { CodeActionProvider, CodeLensProvider, CompletionItemProvider, DeclarationProvider, DefinitionProvider, DocumentColorProvider, DocumentFormattingEditProvider, DocumentHighlightProvider, DocumentOnDropEditProvider, DocumentPasteEditProvider, DocumentRangeFormattingEditProvider, DocumentRangeSemanticTokensProvider, DocumentSemanticTokensProvider, DocumentSymbolProvider, EvaluatableExpressionProvider, FoldingRangeProvider, HoverProvider, ImplementationProvider, InlayHintsProvider, InlineCompletionsProvider, InlineValuesProvider, LinkedEditingRangeProvider, LinkProvider, MappedEditsProvider, OnTypeFormattingEditProvider, ReferenceProvider, RenameProvider, SelectionRangeProvider, SignatureHelpProvider, TypeDefinitionProvider } from 'vs/editor/common/languages'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; export const ILanguageFeaturesService = createDecorator('ILanguageFeaturesService'); @@ -71,6 +71,8 @@ export interface ILanguageFeaturesService { readonly documentOnDropEditProvider: LanguageFeatureRegistry; + readonly mappedEditsProvider: LanguageFeatureRegistry; + // -- setNotebookTypeResolver(resolver: NotebookInfoResolver | undefined): void; diff --git a/src/vs/editor/common/services/languageFeaturesService.ts b/src/vs/editor/common/services/languageFeaturesService.ts index 35d321b4ef2..3ef13891fb7 100644 --- a/src/vs/editor/common/services/languageFeaturesService.ts +++ b/src/vs/editor/common/services/languageFeaturesService.ts @@ -5,7 +5,7 @@ import { URI } from 'vs/base/common/uri'; import { LanguageFeatureRegistry, NotebookInfo, NotebookInfoResolver } from 'vs/editor/common/languageFeatureRegistry'; -import { CodeActionProvider, CodeLensProvider, CompletionItemProvider, DocumentPasteEditProvider, DeclarationProvider, DefinitionProvider, DocumentColorProvider, DocumentFormattingEditProvider, DocumentHighlightProvider, DocumentOnDropEditProvider, DocumentRangeFormattingEditProvider, DocumentRangeSemanticTokensProvider, DocumentSemanticTokensProvider, DocumentSymbolProvider, EvaluatableExpressionProvider, FoldingRangeProvider, HoverProvider, ImplementationProvider, InlayHintsProvider, InlineCompletionsProvider, InlineValuesProvider, LinkedEditingRangeProvider, LinkProvider, OnTypeFormattingEditProvider, ReferenceProvider, RenameProvider, SelectionRangeProvider, SignatureHelpProvider, TypeDefinitionProvider } from 'vs/editor/common/languages'; +import { CodeActionProvider, CodeLensProvider, CompletionItemProvider, DocumentPasteEditProvider, DeclarationProvider, DefinitionProvider, DocumentColorProvider, DocumentFormattingEditProvider, DocumentHighlightProvider, DocumentOnDropEditProvider, DocumentRangeFormattingEditProvider, DocumentRangeSemanticTokensProvider, DocumentSemanticTokensProvider, DocumentSymbolProvider, EvaluatableExpressionProvider, FoldingRangeProvider, HoverProvider, ImplementationProvider, InlayHintsProvider, InlineCompletionsProvider, InlineValuesProvider, LinkedEditingRangeProvider, LinkProvider, OnTypeFormattingEditProvider, ReferenceProvider, RenameProvider, SelectionRangeProvider, SignatureHelpProvider, TypeDefinitionProvider, MappedEditsProvider } from 'vs/editor/common/languages'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; @@ -42,6 +42,7 @@ export class LanguageFeaturesService implements ILanguageFeaturesService { readonly documentSemanticTokensProvider = new LanguageFeatureRegistry(this._score.bind(this)); readonly documentOnDropEditProvider = new LanguageFeatureRegistry(this._score.bind(this)); readonly documentPasteEditProvider = new LanguageFeatureRegistry(this._score.bind(this)); + readonly mappedEditsProvider: LanguageFeatureRegistry = new LanguageFeatureRegistry(this._score.bind(this)); private _notebookTypeResolver?: NotebookInfoResolver; diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 8fbfcd493ae..4d749af6eaf 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -7891,6 +7891,30 @@ declare namespace monaco.languages { provideDocumentRangeSemanticTokens(model: editor.ITextModel, range: Range, token: CancellationToken): ProviderResult; } + export interface RelatedContextItem { + readonly uri: Uri; + readonly range: IRange; + } + + export interface MappedEditsContext { + selections: ISelection[]; + related: RelatedContextItem[]; + } + + export interface MappedEditsProvider { + /** + * Provider maps code blocks from the chat into a workspace edit. + * + * @param document The document to provide mapped edits for. + * @param codeBlocks Code blocks that come from an LLM's reply. + * "Insert at cursor" in the panel chat only sends one edit that the user clicks on, but inline chat can send multiple blocks and let the lang server decide what to do with them. + * @param context The context for providing mapped edits. + * @param token A cancellation token. + * @returns A provider result of text edits. + */ + provideMappedEdits(document: editor.ITextModel, codeBlocks: string[], context: MappedEditsContext, token: CancellationToken): Promise; + } + export interface ILanguageExtensionPoint { id: string; extensions?: string[]; diff --git a/src/vs/workbench/api/browser/extensionHost.contribution.ts b/src/vs/workbench/api/browser/extensionHost.contribution.ts index 6d836e715e1..774cc0cc158 100644 --- a/src/vs/workbench/api/browser/extensionHost.contribution.ts +++ b/src/vs/workbench/api/browser/extensionHost.contribution.ts @@ -86,7 +86,6 @@ import './mainThreadTimeline'; import './mainThreadTesting'; import './mainThreadSecretState'; import './mainThreadShare'; -import './mainThreadMappedEdits'; import './mainThreadProfilContentHandlers'; import './mainThreadSemanticSimilarity'; import './mainThreadIssueReporter'; diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 84247a03e27..fb79323e683 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -931,6 +931,13 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread } return provider.resolveDocumentOnDropFileData(requestId, dataId); } + + // --- mapped edits + + $registerMappedEditsProvider(handle: number, selector: IDocumentFilterDto[]): void { + const provider = new MainThreadMappedEditsProvider(handle, this._proxy, this._uriIdentService); + this._registrations.set(handle, this._languageFeaturesService.mappedEditsProvider.register(selector, provider)); + } } class MainThreadPasteEditProvider implements languages.DocumentPasteEditProvider { @@ -1124,3 +1131,17 @@ export class MainThreadDocumentRangeSemanticTokensProvider implements languages. throw new Error(`Unexpected`); } } + +export class MainThreadMappedEditsProvider implements languages.MappedEditsProvider { + + constructor( + private readonly _handle: number, + private readonly _proxy: ExtHostLanguageFeaturesShape, + private readonly _uriService: IUriIdentityService, + ) { } + + async provideMappedEdits(document: ITextModel, codeBlocks: string[], context: languages.MappedEditsContext, token: CancellationToken): Promise { + const res = await this._proxy.$provideMappedEdits(this._handle, document.uri, codeBlocks, context, token); + return res ? reviveWorkspaceEditDto(res, this._uriService) : null; + } +} diff --git a/src/vs/workbench/api/browser/mainThreadMappedEdits.ts b/src/vs/workbench/api/browser/mainThreadMappedEdits.ts deleted file mode 100644 index a268da7bbe1..00000000000 --- a/src/vs/workbench/api/browser/mainThreadMappedEdits.ts +++ /dev/null @@ -1,57 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; -import { reviveWorkspaceEditDto } from 'vs/workbench/api/browser/mainThreadBulkEdits'; -import { ExtHostContext, ExtHostMappedEditsShape, IDocumentFilterDto, MainContext, MainThreadMappedEditsShape } from 'vs/workbench/api/common/extHost.protocol'; -import { IMappedEditsProvider, IMappedEditsService } from 'vs/workbench/services/mappedEdits/common/mappedEdits'; -import { IExtHostContext, extHostNamedCustomer } from 'vs/workbench/services/extensions/common/extHostCustomers'; - -@extHostNamedCustomer(MainContext.MainThreadMappedEdits) -export class MainThreadMappedEdits implements MainThreadMappedEditsShape { - - private readonly proxy: ExtHostMappedEditsShape; - - private providers = new Map(); - - private providerDisposables = new Map(); - - constructor( - extHostContext: IExtHostContext, - @IMappedEditsService private readonly mappedEditsService: IMappedEditsService, - @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, - ) { - this.proxy = extHostContext.getProxy(ExtHostContext.ExtHostMappedEdits); - } - - $registerMappedEditsProvider(handle: number, selector: IDocumentFilterDto[]): void { - const provider: IMappedEditsProvider = { - selector, - provideMappedEdits: async (document, codeBlocks, context, token) => { - const result = await this.proxy.$provideMappedEdits(handle, document.uri, codeBlocks, context, token); - return result ? reviveWorkspaceEditDto(result, this.uriIdentityService) : null; - } - }; - this.providers.set(handle, provider); - const disposable = this.mappedEditsService.registerMappedEditsProvider(provider); - this.providerDisposables.set(handle, disposable); - } - - $unregisterMappedEditsProvider(handle: number): void { - if (this.providers.has(handle)) { - this.providers.delete(handle); - } - if (this.providerDisposables.has(handle)) { - this.providerDisposables.delete(handle); - } - } - - dispose(): void { - this.providers.clear(); - dispose(this.providerDisposables.values()); - this.providerDisposables.clear(); - } -} diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 3b77e80ec96..492598f96ff 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -106,7 +106,6 @@ import { IExtHostManagedSockets } from 'vs/workbench/api/common/extHostManagedSo import { ExtHostShare } from 'vs/workbench/api/common/extHostShare'; import { ExtHostChatProvider } from 'vs/workbench/api/common/extHostChatProvider'; import { ExtHostChatSlashCommands } from 'vs/workbench/api/common/extHostChatSlashCommand'; -import { ExtHostMappedEdits } from 'vs/workbench/api/common/extHostMappedEdits'; import { ExtHostChatVariables } from 'vs/workbench/api/common/extHostChatVariables'; export interface IExtensionRegistries { @@ -211,7 +210,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostChatSlashCommands = rpcProtocol.set(ExtHostContext.ExtHostChatSlashCommands, new ExtHostChatSlashCommands(rpcProtocol, extHostChatProvider, extHostLogService)); const extHostChatVariables = rpcProtocol.set(ExtHostContext.ExtHostChatVariables, new ExtHostChatVariables(rpcProtocol)); const extHostChat = rpcProtocol.set(ExtHostContext.ExtHostChat, new ExtHostChat(rpcProtocol, extHostLogService)); - const extHostMappedEdits = rpcProtocol.set(ExtHostContext.ExtHostMappedEdits, new ExtHostMappedEdits(rpcProtocol, extHostDocuments, uriTransformer)); const extHostSemanticSimilarity = rpcProtocol.set(ExtHostContext.ExtHostSemanticSimilarity, new ExtHostSemanticSimilarity(rpcProtocol)); const extHostIssueReporter = rpcProtocol.set(ExtHostContext.ExtHostIssueReporter, new ExtHostIssueReporter(rpcProtocol)); const extHostStatusBar = rpcProtocol.set(ExtHostContext.ExtHostStatusBar, new ExtHostStatusBar(rpcProtocol, extHostCommands.converter)); @@ -1348,7 +1346,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I }, registerMappedEditsProvider(selector: vscode.DocumentSelector, provider: vscode.MappedEditsProvider) { checkProposedApiEnabled(extension, 'mappedEditsProvider'); - return extHostMappedEdits.registerMappedEditsProvider(selector, provider); + return extHostLanguageFeatures.registerMappedEditsProvider(extension, selector, provider); } }; diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 686e4ad8af6..9e0e46a7578 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -377,7 +377,7 @@ export interface IRelatedContextItemDto { } export interface IMappedEditsContextDto { - selections: ISelection[]; //FIXME@ulugbekna: is this serializable? should I use ISelection? + selections: ISelection[]; related: IRelatedContextItemDto[]; } @@ -436,6 +436,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable { $resolvePasteFileData(handle: number, requestId: number, dataId: string): Promise; $resolveDocumentOnDropFileData(handle: number, requestId: number, dataId: string): Promise; $setLanguageConfiguration(handle: number, languageId: string, configuration: ILanguageConfigurationDto): void; + $registerMappedEditsProvider(handle: number, selector: IDocumentFilterDto[]): void; } export interface MainThreadLanguagesShape extends IDisposable { @@ -1325,11 +1326,6 @@ export interface MainThreadShareShape extends IDisposable { $unregisterShareProvider(handle: number): void; } -export interface MainThreadMappedEditsShape extends IDisposable { - $registerMappedEditsProvider(handle: number, selector: IDocumentFilterDto[]): void; - $unregisterMappedEditsProvider(handle: number): void; -} - export interface MainThreadTaskShape extends IDisposable { $createTaskId(task: tasks.ITaskDTO): Promise; $registerTaskProvider(handle: number, type: string): Promise; @@ -2014,6 +2010,7 @@ export interface ExtHostLanguageFeaturesShape { $provideTypeHierarchySubtypes(handle: number, sessionId: string, itemId: string, token: CancellationToken): Promise; $releaseTypeHierarchy(handle: number, sessionId: string): void; $provideDocumentOnDropEdits(handle: number, requestId: number, resource: UriComponents, position: IPosition, dataTransferDto: DataTransferDTO, token: CancellationToken): Promise; + $provideMappedEdits(handle: number, document: UriComponents, codeBlocks: string[], context: IMappedEditsContextDto, token: CancellationToken): Promise; } export interface ExtHostQuickOpenShape { @@ -2113,10 +2110,6 @@ export interface ExtHostShareShape { $provideShare(handle: number, shareableItem: IShareableItemDto, token: CancellationToken): Promise; } -export interface ExtHostMappedEditsShape { - $provideMappedEdits(handle: number, document: UriComponents, codeBlocks: string[], context: IMappedEditsContextDto, token: CancellationToken): Promise; -} - export interface ExtHostTaskShape { $provideTasks(handle: number, validTypes: { [key: string]: boolean }): Promise; $resolveTask(handle: number, taskDTO: tasks.ITaskDTO): Promise; @@ -2642,7 +2635,6 @@ export const MainContext = { MainThreadSCM: createProxyIdentifier('MainThreadSCM'), MainThreadSearch: createProxyIdentifier('MainThreadSearch'), MainThreadShare: createProxyIdentifier('MainThreadShare'), - MainThreadMappedEdits: createProxyIdentifier('MainThreadMappedEdits'), MainThreadTask: createProxyIdentifier('MainThreadTask'), MainThreadWindow: createProxyIdentifier('MainThreadWindow'), MainThreadLabelService: createProxyIdentifier('MainThreadLabelService'), @@ -2720,7 +2712,6 @@ export const ExtHostContext = { ExtHostChatSlashCommands: createProxyIdentifier('ExtHostChatSlashCommands'), ExtHostChatVariables: createProxyIdentifier('ExtHostChatVariables'), ExtHostChatProvider: createProxyIdentifier('ExtHostChatProvider'), - ExtHostMappedEdits: createProxyIdentifier('ExtHostMappedEdits'), ExtHostSemanticSimilarity: createProxyIdentifier('ExtHostSemanticSimilarity'), ExtHostTheming: createProxyIdentifier('ExtHostTheming'), ExtHostTunnelService: createProxyIdentifier('ExtHostTunnelService'), diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index cd8b2c1de2c..ec2fbcebeef 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -1817,6 +1817,34 @@ class DocumentOnDropEditAdapter { } } +class MappedEditsAdapter { + + constructor( + private readonly _documents: ExtHostDocuments, + private readonly _provider: vscode.MappedEditsProvider, + ) { } + + async provideMappedEdits( + resource: UriComponents, + codeBlocks: string[], + context: extHostProtocol.IMappedEditsContextDto, + token: CancellationToken + ): Promise { + + const uri = URI.revive(resource); + const doc = this._documents.getDocument(uri); + + const ctx = { + selections: context.selections.map(s => typeConvert.Selection.to(s)), + related: context.related.map(r => ({ uri: URI.revive(r.uri), range: typeConvert.Range.to(r.range) })), + }; + + const mappedEdits = await this._provider.provideMappedEdits(doc, codeBlocks, ctx, token); + + return mappedEdits ? typeConvert.WorkspaceEdit.from(mappedEdits) : null; + } +} + type Adapter = DocumentSymbolAdapter | CodeLensAdapter | DefinitionAdapter | HoverAdapter | DocumentHighlightAdapter | ReferenceAdapter | CodeActionAdapter | DocumentPasteEditProvider | DocumentFormattingAdapter | RangeFormattingAdapter | OnTypeFormattingAdapter | NavigateTypeAdapter | RenameAdapter @@ -1826,7 +1854,7 @@ type Adapter = DocumentSymbolAdapter | CodeLensAdapter | DefinitionAdapter | Hov | DocumentSemanticTokensAdapter | DocumentRangeSemanticTokensAdapter | EvaluatableExpressionAdapter | InlineValuesAdapter | LinkedEditingRangeAdapter | InlayHintsAdapter | InlineCompletionAdapter - | DocumentOnDropEditAdapter; + | DocumentOnDropEditAdapter | MappedEditsAdapter; class AdapterData { constructor( @@ -2383,7 +2411,14 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF } $provideFoldingRanges(handle: number, resource: UriComponents, context: vscode.FoldingContext, token: CancellationToken): Promise { - return this._withAdapter(handle, FoldingProviderAdapter, adapter => adapter.provideFoldingRanges(URI.revive(resource), context, token), undefined, token); + return this._withAdapter( + handle, + FoldingProviderAdapter, + (adapter) => + adapter.provideFoldingRanges(URI.revive(resource), context, token), + undefined, + token + ); } // --- smart select @@ -2462,6 +2497,19 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF Promise.resolve(adapter.provideDocumentOnDropEdits(requestId, URI.revive(resource), position, dataTransferDto, token)), undefined, undefined); } + // --- mapped edits + + registerMappedEditsProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.MappedEditsProvider): vscode.Disposable { + const handle = this._addNewAdapter(new MappedEditsAdapter(this._documents, provider), extension); + this._proxy.$registerMappedEditsProvider(handle, this._transformDocumentSelector(selector, extension)); + return this._createDisposable(handle); + } + + $provideMappedEdits(handle: number, document: UriComponents, codeBlocks: string[], context: extHostProtocol.IMappedEditsContextDto, token: CancellationToken): Promise { + return this._withAdapter(handle, MappedEditsAdapter, adapter => + Promise.resolve(adapter.provideMappedEdits(document, codeBlocks, context, token)), null, token); + } + // --- copy/paste actions registerDocumentPasteEditProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentPasteEditProvider, metadata: vscode.DocumentPasteProviderMetadata): vscode.Disposable { diff --git a/src/vs/workbench/api/common/extHostMappedEdits.ts b/src/vs/workbench/api/common/extHostMappedEdits.ts deleted file mode 100644 index c98adda03b2..00000000000 --- a/src/vs/workbench/api/common/extHostMappedEdits.ts +++ /dev/null @@ -1,61 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { CancellationToken } from 'vs/base/common/cancellation'; -import { URI, UriComponents } from 'vs/base/common/uri'; -import { IURITransformer } from 'vs/base/common/uriIpc'; -import { ExtHostMappedEditsShape, IMainContext, IMappedEditsContextDto, IWorkspaceEditDto, MainContext, MainThreadMappedEditsShape } from 'vs/workbench/api/common/extHost.protocol'; -import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; -import { Range, Selection, DocumentSelector, WorkspaceEdit } from 'vs/workbench/api/common/extHostTypeConverters'; -import type * as vscode from 'vscode'; - -export class ExtHostMappedEdits implements ExtHostMappedEditsShape { - - private static handlePool: number = 0; - - private proxy: MainThreadMappedEditsShape; - private providers = new Map(); - - constructor( - mainContext: IMainContext, - private readonly _documents: ExtHostDocuments, - private readonly uriTransformer: IURITransformer | undefined - ) { - this.proxy = mainContext.getProxy(MainContext.MainThreadMappedEdits); - } - - async $provideMappedEdits(handle: number, docUri: UriComponents, codeBlocks: string[], context: IMappedEditsContextDto, token: CancellationToken): Promise { - const provider = this.providers.get(handle); - if (!provider) { - return null; - } - const uri = URI.revive(docUri); - const doc = this._documents.getDocument(uri); - const ctx = { - selections: context.selections.map(s => Selection.to(s)), - related: context.related.map(r => ({ uri: URI.revive(r.uri), range: Range.to(r.range) })), - }; - const mappedEdits = await provider.provideMappedEdits(doc, codeBlocks, ctx, token); - if (!mappedEdits) { - return null; - } - - return WorkspaceEdit.from(mappedEdits); - } - - registerMappedEditsProvider(selector: vscode.DocumentSelector, provider: vscode.MappedEditsProvider): vscode.Disposable { - const handle = ExtHostMappedEdits.handlePool++; - this.providers.set(handle, provider); - this.proxy.$registerMappedEditsProvider(handle, DocumentSelector.from(selector, this.uriTransformer)); - return { - dispose: () => { - ExtHostMappedEdits.handlePool--; - this.proxy.$unregisterMappedEditsProvider(handle); - this.providers.delete(handle); - } - }; - } - -} diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 0f7efee2be4..e745869c9cf 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -32,7 +32,6 @@ import { IMarkerData, IRelatedInformation, MarkerSeverity, MarkerTag } from 'vs/ import { ProgressLocation as MainProgressLocation } from 'vs/platform/progress/common/progress'; import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; import { getPrivateApiFor } from 'vs/workbench/api/common/extHostTestingPrivateApi'; -import type * as mappedEdits from 'vs/workbench/services/mappedEdits/common/mappedEdits'; import { DEFAULT_EDITOR_ASSOCIATION, SaveReason } from 'vs/workbench/common/editor'; import { IViewBadge } from 'vs/workbench/common/views'; import { IChatFollowup, IChatReplyFollowup, IChatResponseCommandFollowup } from 'vs/workbench/contrib/chat/common/chatService'; @@ -1577,10 +1576,10 @@ export namespace MappedEditsContext { v.related.every(e => e && typeof e === 'object' && URI.isUri(e.uri) && e.range instanceof types.Range)); } - export function from(context: vscode.MappedEditsContext): mappedEdits.MappedEditsContext { + export function from(extContext: vscode.MappedEditsContext): languages.MappedEditsContext { return { - selections: context.selections.map(s => Selection.from(s)), - related: context.related.map(r => ({ + selections: extContext.selections.map(s => Selection.from(s)), + related: extContext.related.map(r => ({ uri: URI.from(r.uri), range: Range.from(r.range) })) diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts index 85fd1ee7ad2..3761e137dc5 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts @@ -25,13 +25,14 @@ import { IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat'; import { CONTEXT_IN_CHAT_SESSION, CONTEXT_PROVIDER_EXISTS } from 'vs/workbench/contrib/chat/common/chatContextKeys'; import { IChatCopyAction, IChatService, IChatUserActionEvent, InteractiveSessionCopyKind } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatResponseViewModel, isResponseVM } from 'vs/workbench/contrib/chat/common/chatViewModel'; -import { IMappedEditsService, RelatedContextItem } from 'vs/workbench/services/mappedEdits/common/mappedEdits'; import { insertCell } from 'vs/workbench/contrib/notebook/browser/controller/cellOperations'; import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellKind, NOTEBOOK_EDITOR_ID } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ITerminalEditorService, ITerminalGroupService, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; +import { WorkspaceEdit, RelatedContextItem } from 'vs/editor/common/languages'; export interface IChatCodeBlockActionContext { code: string; @@ -236,19 +237,33 @@ export function registerChatCodeBlockActions() { private async handleTextEditor(accessor: ServicesAccessor, codeEditor: ICodeEditor, activeModel: ITextModel, chatCodeBlockActionContext: IChatCodeBlockActionContext) { this.notifyUserAction(accessor, chatCodeBlockActionContext); + const bulkEditService = accessor.get(IBulkEditService); const codeEditorService = accessor.get(ICodeEditorService); - const mappedEditsService = accessor.get(IMappedEditsService); + + const mappedEditsProviders = accessor.get(ILanguageFeaturesService).mappedEditsProvider.ordered(activeModel); // try applying workspace edit that was returned by a MappedEditsProvider, else simply insert at selection - const selections = codeEditor.getSelections() ?? []; - const mappedEditsContext = { - selections, - related: [] as RelatedContextItem[], // FIXME@ulugbekna: this needs to be populated but we don't yet have a way to get this info from extensions - }; - const cancellationTokenSource = new CancellationTokenSource(); - const workspaceEdit = await mappedEditsService.provideMappedEdits(activeModel, [chatCodeBlockActionContext.code], mappedEditsContext, cancellationTokenSource.token); + let workspaceEdit: WorkspaceEdit | null = null; + + if (mappedEditsProviders.length > 0) { + const mostRelevantProvider = mappedEditsProviders[0]; + + const selections = codeEditor.getSelections() ?? []; + const mappedEditsContext = { + selections, + related: [] as RelatedContextItem[], // TODO@ulugbekna: we do have not yet decided what to populate this with + }; + const cancellationTokenSource = new CancellationTokenSource(); + + workspaceEdit = await mostRelevantProvider.provideMappedEdits( + activeModel, + [chatCodeBlockActionContext.code], + mappedEditsContext, + cancellationTokenSource.token); + } + if (workspaceEdit) { await bulkEditService.apply(workspaceEdit); } else { diff --git a/src/vs/workbench/contrib/mappedEdits/common/mappedEdits.contribution.ts b/src/vs/workbench/contrib/mappedEdits/common/mappedEdits.contribution.ts index 82171c7fbed..fc753a4b673 100644 --- a/src/vs/workbench/contrib/mappedEdits/common/mappedEdits.contribution.ts +++ b/src/vs/workbench/contrib/mappedEdits/common/mappedEdits.contribution.ts @@ -5,23 +5,47 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { URI } from 'vs/base/common/uri'; +import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { IMappedEditsService, MappedEditsContext } from 'vs/workbench/services/mappedEdits/common/mappedEdits'; +import * as languages from 'vs/editor/common/languages'; -CommandsRegistry.registerCommand('_executeMappedEditsProvider', async (accessor: ServicesAccessor, documentUri: URI, codeBlocks: string[], context: MappedEditsContext) => { +CommandsRegistry.registerCommand( + '_executeMappedEditsProvider', + async ( + accessor: ServicesAccessor, + documentUri: URI, + codeBlocks: string[], + context: languages.MappedEditsContext + ): Promise => { - const mappedEditsService = accessor.get(IMappedEditsService); - const modelService = accessor.get(ITextModelService); + const modelService = accessor.get(ITextModelService); + const langFeaturesService = accessor.get(ILanguageFeaturesService); - const document = await modelService.createModelReference(documentUri); + const document = await modelService.createModelReference(documentUri); - const cancellationTokenSource = new CancellationTokenSource(); + let result: languages.WorkspaceEdit | null = null; - const result = await mappedEditsService.provideMappedEdits(document.object.textEditorModel, codeBlocks, context, cancellationTokenSource.token); + try { + const providers = langFeaturesService.mappedEditsProvider.ordered(document.object.textEditorModel); - document.dispose(); + if (providers.length > 0) { + const mostRelevantProvider = providers[0]; - return result; -}); + const cancellationTokenSource = new CancellationTokenSource(); + + result = await mostRelevantProvider.provideMappedEdits( + document.object.textEditorModel, + codeBlocks, + context, + cancellationTokenSource.token + ); + } + } finally { + document.dispose(); + } + + return result; + } +); diff --git a/src/vs/workbench/services/mappedEdits/browser/mappedEditsService.ts b/src/vs/workbench/services/mappedEdits/browser/mappedEditsService.ts deleted file mode 100644 index fe1cec5815a..00000000000 --- a/src/vs/workbench/services/mappedEdits/browser/mappedEditsService.ts +++ /dev/null @@ -1,51 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { CancellationToken } from 'vs/base/common/cancellation'; -import { ITextModel } from 'vs/editor/common/model'; -import { IMappedEditsProvider, IMappedEditsService, MappedEditsContext } from 'vs/workbench/services/mappedEdits/common/mappedEdits'; -import { score } from 'vs/editor/common/languageSelector'; -import { WorkspaceEdit } from 'vs/editor/common/languages'; -import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; - -export class MappedEditsService implements IMappedEditsService { - readonly _serviceBrand: undefined; - - private readonly _providers = new Set(); - - constructor() { } - - registerMappedEditsProvider(provider: IMappedEditsProvider) { - this._providers.add(provider); - return { - dispose: () => { - this._providers.delete(provider); - } - }; - } - - async provideMappedEdits(document: ITextModel, codeBlocks: string[], context: MappedEditsContext, token: CancellationToken): Promise { - - const language = document.getLanguageId(); - - const providers = [...this._providers.values()] - .map((p): [IMappedEditsProvider, number] => { - const pts = score(p.selector, document.uri, language, true, undefined, undefined); - return [p, pts]; - }) - .filter(([p, pts]) => pts > 0) - .sort((a, b) => b[1] - a[1]); - - if (providers.length === 0) { - return null; - } - - const provider = providers[0][0]; - - return provider.provideMappedEdits(document, codeBlocks, context, token); - } -} - -registerSingleton(IMappedEditsService, MappedEditsService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/mappedEdits/common/mappedEdits.ts b/src/vs/workbench/services/mappedEdits/common/mappedEdits.ts deleted file mode 100644 index 9a60304881c..00000000000 --- a/src/vs/workbench/services/mappedEdits/common/mappedEdits.ts +++ /dev/null @@ -1,65 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { URI } from 'vs/base/common/uri'; -import { ITextModel } from 'vs/editor/common/model'; -import { CancellationToken } from 'vs/base/common/cancellation'; -import { LanguageSelector } from 'vs/editor/common/languageSelector'; -import { IDisposable } from 'vs/base/common/lifecycle'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { WorkspaceEdit } from 'vs/editor/common/languages'; -import { ISelection } from 'vs/editor/common/core/selection'; -import { IRange } from 'vs/editor/common/core/range'; - -export interface RelatedContextItem { - readonly uri: URI; - readonly range: IRange; -} - -export interface MappedEditsContext { - selections: ISelection[]; - - /** - * If there's no context, the array should be empty. It's also empty until we figure out how to compute this or retrieve from an extension (eg, copilot chat) - * - * TODO@ulugbekna: should this array be sorted from highest priority to lowest? - */ - related: RelatedContextItem[]; -} - -export interface IMappedEditsProvider { - - selector: LanguageSelector; - - /** - * Provide mapped edits for a given document. - * - * @param document The document to provide mapped edits for. - * @param codeBlocks Code blocks that come from an LLM's reply. - * "Insert at cursor" in the panel chat only sends one edit that the user clicks on, but inline chat can send multiple blocks and let the lang server decide what to do with them. - * @param context The context for providing mapped edits. - * @param token A cancellation token. - * @returns A provider result of text edits. - */ - provideMappedEdits( - document: ITextModel, - codeBlocks: string[], - context: MappedEditsContext, - token: CancellationToken - ): Promise; -} - - -export const IMappedEditsService = createDecorator('mappedEditsService'); - -export interface IMappedEditsService { - _serviceBrand: undefined; - registerMappedEditsProvider(provider: IMappedEditsProvider): IDisposable; - provideMappedEdits( - document: ITextModel, - codeBlocks: string[], - context: MappedEditsContext, - token: CancellationToken): Promise; -} diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index d30cd73ec49..98d5a3cc130 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -111,7 +111,6 @@ import 'vs/workbench/services/textMate/browser/textMateTokenizationFeature.contr import 'vs/workbench/services/userActivity/common/userActivityService'; import 'vs/workbench/services/userActivity/browser/userActivityBrowser'; import 'vs/workbench/services/issue/browser/issueTroubleshoot'; -import 'vs/workbench/services/mappedEdits/browser/mappedEditsService'; import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; From 7705edf96cd90eec4a0df8de811d1400ee796088 Mon Sep 17 00:00:00 2001 From: Ulugbek Abdullaev Date: Tue, 22 Aug 2023 15:56:54 +0200 Subject: [PATCH 184/223] add `null` as possible return type for the `vscode.executeMappedEditsProvider` --- src/vs/workbench/api/common/extHostApiCommands.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/api/common/extHostApiCommands.ts b/src/vs/workbench/api/common/extHostApiCommands.ts index fdb8ffa2cd6..84545808c0f 100644 --- a/src/vs/workbench/api/common/extHostApiCommands.ts +++ b/src/vs/workbench/api/common/extHostApiCommands.ts @@ -478,10 +478,10 @@ const newCommands: ApiCommand[] = [ (v: vscode.MappedEditsContext) => typeConverters.MappedEditsContext.from(v) ) ], - new ApiCommandResult( + new ApiCommandResult( 'A promise that resolves to a workspace edit or null', (value) => { - return typeConverters.WorkspaceEdit.to(value); + return value ? typeConverters.WorkspaceEdit.to(value) : null; }) ), ]; From fe52b50e41878a5b6dcbc20a7d755fc840441841 Mon Sep 17 00:00:00 2001 From: Ulugbek Abdullaev Date: Tue, 22 Aug 2023 15:57:40 +0200 Subject: [PATCH 185/223] add `mappedEditsProvider` proposed API ID to integration tests' package.json --- extensions/vscode-api-tests/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json index 5ee95741ff0..9d50e393547 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -19,6 +19,7 @@ "fileSearchProvider", "findTextInFiles", "fsChunks", + "mappedEditsProvider", "notebookCellExecutionState", "notebookDeprecated", "notebookLiveShare", @@ -45,7 +46,7 @@ "timeline", "tokenInformation", "treeItemCheckbox", - "treeViewActiveItem", + "treeViewActiveItem", "treeViewReveal", "workspaceTrust", "telemetry", From 7ab8588219218584983484d813e663d80db190cc Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 22 Aug 2023 07:45:18 -0700 Subject: [PATCH 186/223] xterm@5.3.0-beta.58 Fixes #189684 Part of #190195 (diagnostics) --- package.json | 16 +++++------ remote/package.json | 16 +++++------ remote/web/package.json | 12 ++++---- remote/web/yarn.lock | 48 +++++++++++++++---------------- remote/yarn.lock | 64 ++++++++++++++++++++--------------------- yarn.lock | 64 ++++++++++++++++++++--------------------- 6 files changed, 110 insertions(+), 110 deletions(-) diff --git a/package.json b/package.json index 7c5d9cb3444..b878c99812b 100644 --- a/package.json +++ b/package.json @@ -95,14 +95,14 @@ "vscode-oniguruma": "1.7.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "9.0.0", - "xterm": "5.3.0-beta.46", - "xterm-addon-canvas": "0.5.0-beta.9", - "xterm-addon-image": "0.6.0-beta.2", - "xterm-addon-search": "0.13.0-beta.8", - "xterm-addon-serialize": "0.11.0-beta.8", - "xterm-addon-unicode11": "0.5.0", - "xterm-addon-webgl": "0.16.0-beta.16", - "xterm-headless": "5.3.0-beta.46", + "xterm": "5.3.0-beta.58", + "xterm-addon-canvas": "0.5.0-beta.19", + "xterm-addon-image": "0.6.0-beta.11", + "xterm-addon-search": "0.13.0-beta.17", + "xterm-addon-serialize": "0.11.0-beta.17", + "xterm-addon-unicode11": "0.6.0-beta.9", + "xterm-addon-webgl": "0.16.0-beta.27", + "xterm-headless": "5.3.0-beta.58", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, diff --git a/remote/package.json b/remote/package.json index 28cf60a0252..6d6a0152a55 100644 --- a/remote/package.json +++ b/remote/package.json @@ -27,14 +27,14 @@ "vscode-oniguruma": "1.7.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "9.0.0", - "xterm": "5.3.0-beta.46", - "xterm-addon-canvas": "0.5.0-beta.9", - "xterm-addon-image": "0.6.0-beta.2", - "xterm-addon-search": "0.13.0-beta.8", - "xterm-addon-serialize": "0.11.0-beta.8", - "xterm-addon-unicode11": "0.5.0", - "xterm-addon-webgl": "0.16.0-beta.16", - "xterm-headless": "5.3.0-beta.46", + "xterm": "5.3.0-beta.58", + "xterm-addon-canvas": "0.5.0-beta.19", + "xterm-addon-image": "0.6.0-beta.11", + "xterm-addon-search": "0.13.0-beta.17", + "xterm-addon-serialize": "0.11.0-beta.17", + "xterm-addon-unicode11": "0.6.0-beta.9", + "xterm-addon-webgl": "0.16.0-beta.27", + "xterm-headless": "5.3.0-beta.58", "yauzl": "^2.9.2", "yazl": "^2.4.3" } diff --git a/remote/web/package.json b/remote/web/package.json index bd22a24243a..05686230a43 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -11,11 +11,11 @@ "tas-client-umd": "0.1.8", "vscode-oniguruma": "1.7.0", "vscode-textmate": "9.0.0", - "xterm": "5.3.0-beta.46", - "xterm-addon-canvas": "0.5.0-beta.9", - "xterm-addon-image": "0.6.0-beta.2", - "xterm-addon-search": "0.13.0-beta.8", - "xterm-addon-unicode11": "0.5.0", - "xterm-addon-webgl": "0.16.0-beta.16" + "xterm": "5.3.0-beta.58", + "xterm-addon-canvas": "0.5.0-beta.19", + "xterm-addon-image": "0.6.0-beta.11", + "xterm-addon-search": "0.13.0-beta.17", + "xterm-addon-unicode11": "0.6.0-beta.9", + "xterm-addon-webgl": "0.16.0-beta.27" } } diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index b57b5aa165d..dc817e45bf1 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -68,32 +68,32 @@ vscode-textmate@9.0.0: resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-9.0.0.tgz#313c6c8792b0507aef35aeb81b6b370b37c44d6c" integrity sha512-Cl65diFGxz7gpwbav10HqiY/eVYTO1sjQpmRmV991Bj7wAoOAjGQ97PpQcXorDE2Uc4hnGWLY17xme+5t6MlSg== -xterm-addon-canvas@0.5.0-beta.9: - version "0.5.0-beta.9" - resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.5.0-beta.9.tgz#d4a9536cf586f78a54527751e03abf6445613886" - integrity sha512-AMyFctHbIWx0aLcACINuZqFFNoz4ndGtIAb4pQguIl9t8r+2otiV11MY4FJmPM0kUzEzoLWzVFJaGxOzykCWuw== +xterm-addon-canvas@0.5.0-beta.19: + version "0.5.0-beta.19" + resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.5.0-beta.19.tgz#a2b67554191fae29c901c4a4b398fc28dc345afe" + integrity sha512-eF6b7SBZslmwqCLiWTGnjna4bdjd28cQ+ivZH8SVjE5xP3JGAHGWs/fZm5E3eR6pcHEOkX++/FxByuvhOVXIXQ== -xterm-addon-image@0.6.0-beta.2: - version "0.6.0-beta.2" - resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.6.0-beta.2.tgz#04c5ea11c89032b01b577b37f891109168ed0b09" - integrity sha512-58d1mqIpJwq8Zy/a/QM8JVuA1g5SceG8UvLoj9T6r+2XIktka6GK2oS66Fmo+0MP0s0HDPy+yJtl8lHY6UyzHw== +xterm-addon-image@0.6.0-beta.11: + version "0.6.0-beta.11" + resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.6.0-beta.11.tgz#17dffc5f38480a4fac231649ef6e313a8f46d614" + integrity sha512-KPsqJo8sSAawO4ze7xvjtS5evbPtlscy16NiSAteh/1AgOffmTnVhJTs3W84dnks2b13FaMEI/WOiCDIJjDV3A== -xterm-addon-search@0.13.0-beta.8: - version "0.13.0-beta.8" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.13.0-beta.8.tgz#90bc70bff87cd7e4f540cce51d9f968f04d5c0ef" - integrity sha512-Hlocg6gGvKAs4eRTW78wTtVqaOEurweJjdyPDjr7KrzwlEEsASsUXJaB2+vgsyTFKuYnCuLrp5u369jck993GQ== +xterm-addon-search@0.13.0-beta.17: + version "0.13.0-beta.17" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.13.0-beta.17.tgz#5379cf3085370c55241d99e95b008c0cbf4e6e53" + integrity sha512-0OKwV9isk4OXHLlhRE8Ohqnmpcd0ExWKtorjeaFxqm4f6xc30qZcJTs0hSMzVZ83foedPJcd6hH0hW2pnxOz3Q== -xterm-addon-unicode11@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.5.0.tgz#41c0d96acc1e3bb6c6596eee64e163b6bca74be7" - integrity sha512-Jm4/g4QiTxiKiTbYICQgC791ubhIZyoIwxAIgOW8z8HWFNY+lwk+dwaKEaEeGBfM48Vk8fklsUW9u/PlenYEBg== +xterm-addon-unicode11@0.6.0-beta.9: + version "0.6.0-beta.9" + resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.6.0-beta.9.tgz#1f476f77cf8c8e4e7ca1a1421b85ac45405db679" + integrity sha512-DH9OIH0EakIdCnzZcIH7NLgUqXwa8WT9fqmi+2CWzt4NN1AH5kSE/3Vk+/uurE5upxDloKw6dtLH9XHpfULV6g== -xterm-addon-webgl@0.16.0-beta.16: - version "0.16.0-beta.16" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.16.tgz#2c31308f8c7f636576720adca529297f8fff3224" - integrity sha512-k0ZSwpBVtxXaqUc29CmlfFgIK7LEYAmwcDC3QVp2ESZo2JHYuQrWFpImjbzbYu0ON6qqcfbX8SnbzRid/XfGUg== +xterm-addon-webgl@0.16.0-beta.27: + version "0.16.0-beta.27" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.27.tgz#f4133a40044f0b6448ea87152abf6c9009729100" + integrity sha512-YiwCvTvgfcNGtQdpzcgxKINE6mG63cPfDxfoxCAbodNGYF66k9VzhIXNdCQtVtu+s6+pEc9YequENe78SSZloA== -xterm@5.3.0-beta.46: - version "5.3.0-beta.46" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.46.tgz#9bd2b2a588c88ae64f1989ca3bf5bd9116554b5a" - integrity sha512-keB3C6sXm56ug1+hylAQiNyZP4YtPgd2W+Gu/ZKwJMk5haD25DEnenBYL++Cl3+sNGYxOGS42QlmYj6Mrq1LPw== +xterm@5.3.0-beta.58: + version "5.3.0-beta.58" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.58.tgz#4bae06639c90952b270e4714938c151ce339b5f0" + integrity sha512-EIOgp+7aqCToI0XjDqORXAe2lJS/XSZXWPzCpyTDmF+DJ56gyw+913gdPoIEWOw3mRylrJtEyIpFcdW7CDA6rQ== diff --git a/remote/yarn.lock b/remote/yarn.lock index e222314587d..68c99905917 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -877,45 +877,45 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -xterm-addon-canvas@0.5.0-beta.9: - version "0.5.0-beta.9" - resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.5.0-beta.9.tgz#d4a9536cf586f78a54527751e03abf6445613886" - integrity sha512-AMyFctHbIWx0aLcACINuZqFFNoz4ndGtIAb4pQguIl9t8r+2otiV11MY4FJmPM0kUzEzoLWzVFJaGxOzykCWuw== +xterm-addon-canvas@0.5.0-beta.19: + version "0.5.0-beta.19" + resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.5.0-beta.19.tgz#a2b67554191fae29c901c4a4b398fc28dc345afe" + integrity sha512-eF6b7SBZslmwqCLiWTGnjna4bdjd28cQ+ivZH8SVjE5xP3JGAHGWs/fZm5E3eR6pcHEOkX++/FxByuvhOVXIXQ== -xterm-addon-image@0.6.0-beta.2: - version "0.6.0-beta.2" - resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.6.0-beta.2.tgz#04c5ea11c89032b01b577b37f891109168ed0b09" - integrity sha512-58d1mqIpJwq8Zy/a/QM8JVuA1g5SceG8UvLoj9T6r+2XIktka6GK2oS66Fmo+0MP0s0HDPy+yJtl8lHY6UyzHw== +xterm-addon-image@0.6.0-beta.11: + version "0.6.0-beta.11" + resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.6.0-beta.11.tgz#17dffc5f38480a4fac231649ef6e313a8f46d614" + integrity sha512-KPsqJo8sSAawO4ze7xvjtS5evbPtlscy16NiSAteh/1AgOffmTnVhJTs3W84dnks2b13FaMEI/WOiCDIJjDV3A== -xterm-addon-search@0.13.0-beta.8: - version "0.13.0-beta.8" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.13.0-beta.8.tgz#90bc70bff87cd7e4f540cce51d9f968f04d5c0ef" - integrity sha512-Hlocg6gGvKAs4eRTW78wTtVqaOEurweJjdyPDjr7KrzwlEEsASsUXJaB2+vgsyTFKuYnCuLrp5u369jck993GQ== +xterm-addon-search@0.13.0-beta.17: + version "0.13.0-beta.17" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.13.0-beta.17.tgz#5379cf3085370c55241d99e95b008c0cbf4e6e53" + integrity sha512-0OKwV9isk4OXHLlhRE8Ohqnmpcd0ExWKtorjeaFxqm4f6xc30qZcJTs0hSMzVZ83foedPJcd6hH0hW2pnxOz3Q== -xterm-addon-serialize@0.11.0-beta.8: - version "0.11.0-beta.8" - resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.11.0-beta.8.tgz#343357aad7c549a37aacac2fb19692b1c2fd69cd" - integrity sha512-1jNrX+Y8dorWtjMvz02mRUP+ahqQ4STO0lYlKe8m06p+NYuFk24CylnPZFuBgk91OUU3RAdl4glaKh5M4f0/Zw== +xterm-addon-serialize@0.11.0-beta.17: + version "0.11.0-beta.17" + resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.11.0-beta.17.tgz#170738ab615e2974c593d41531d41c02b8af1da7" + integrity sha512-zNsUQfIbRD8fL0gHGJJ14DXL22Kduv6pR+QnISReoQqhEEsWANPKpOduJLce7B4y+BJuiOGkT7R7qbMKbj9Ecw== -xterm-addon-unicode11@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.5.0.tgz#41c0d96acc1e3bb6c6596eee64e163b6bca74be7" - integrity sha512-Jm4/g4QiTxiKiTbYICQgC791ubhIZyoIwxAIgOW8z8HWFNY+lwk+dwaKEaEeGBfM48Vk8fklsUW9u/PlenYEBg== +xterm-addon-unicode11@0.6.0-beta.9: + version "0.6.0-beta.9" + resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.6.0-beta.9.tgz#1f476f77cf8c8e4e7ca1a1421b85ac45405db679" + integrity sha512-DH9OIH0EakIdCnzZcIH7NLgUqXwa8WT9fqmi+2CWzt4NN1AH5kSE/3Vk+/uurE5upxDloKw6dtLH9XHpfULV6g== -xterm-addon-webgl@0.16.0-beta.16: - version "0.16.0-beta.16" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.16.tgz#2c31308f8c7f636576720adca529297f8fff3224" - integrity sha512-k0ZSwpBVtxXaqUc29CmlfFgIK7LEYAmwcDC3QVp2ESZo2JHYuQrWFpImjbzbYu0ON6qqcfbX8SnbzRid/XfGUg== +xterm-addon-webgl@0.16.0-beta.27: + version "0.16.0-beta.27" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.27.tgz#f4133a40044f0b6448ea87152abf6c9009729100" + integrity sha512-YiwCvTvgfcNGtQdpzcgxKINE6mG63cPfDxfoxCAbodNGYF66k9VzhIXNdCQtVtu+s6+pEc9YequENe78SSZloA== -xterm-headless@5.3.0-beta.46: - version "5.3.0-beta.46" - resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.3.0-beta.46.tgz#3f941f673e3c61aad2705e4b34b42fbe467aed48" - integrity sha512-e/VbZKrfyD1TTxOY5/jah3dUih6q8nTO5v5GQV7YOASp+yyI0XHKiDjLrCyFPQOLTKxCcRutMHvr+CoCcPpcHg== +xterm-headless@5.3.0-beta.58: + version "5.3.0-beta.58" + resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.3.0-beta.58.tgz#e6a3dbfafdb28435130ce3925ac3194c0ee2c281" + integrity sha512-ekzr3LX8p26qHDyVRs3lys08veHFv9wGOKaHd49kMgktICHsikJ48HNJq5Twp3ZnXyqlIP9CV34BZJ1SBfWmtQ== -xterm@5.3.0-beta.46: - version "5.3.0-beta.46" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.46.tgz#9bd2b2a588c88ae64f1989ca3bf5bd9116554b5a" - integrity sha512-keB3C6sXm56ug1+hylAQiNyZP4YtPgd2W+Gu/ZKwJMk5haD25DEnenBYL++Cl3+sNGYxOGS42QlmYj6Mrq1LPw== +xterm@5.3.0-beta.58: + version "5.3.0-beta.58" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.58.tgz#4bae06639c90952b270e4714938c151ce339b5f0" + integrity sha512-EIOgp+7aqCToI0XjDqORXAe2lJS/XSZXWPzCpyTDmF+DJ56gyw+913gdPoIEWOw3mRylrJtEyIpFcdW7CDA6rQ== yallist@^4.0.0: version "4.0.0" diff --git a/yarn.lock b/yarn.lock index b2b8a437cc0..96c77bcd45a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10759,45 +10759,45 @@ xtend@~2.1.1: dependencies: object-keys "~0.4.0" -xterm-addon-canvas@0.5.0-beta.9: - version "0.5.0-beta.9" - resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.5.0-beta.9.tgz#d4a9536cf586f78a54527751e03abf6445613886" - integrity sha512-AMyFctHbIWx0aLcACINuZqFFNoz4ndGtIAb4pQguIl9t8r+2otiV11MY4FJmPM0kUzEzoLWzVFJaGxOzykCWuw== +xterm-addon-canvas@0.5.0-beta.19: + version "0.5.0-beta.19" + resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.5.0-beta.19.tgz#a2b67554191fae29c901c4a4b398fc28dc345afe" + integrity sha512-eF6b7SBZslmwqCLiWTGnjna4bdjd28cQ+ivZH8SVjE5xP3JGAHGWs/fZm5E3eR6pcHEOkX++/FxByuvhOVXIXQ== -xterm-addon-image@0.6.0-beta.2: - version "0.6.0-beta.2" - resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.6.0-beta.2.tgz#04c5ea11c89032b01b577b37f891109168ed0b09" - integrity sha512-58d1mqIpJwq8Zy/a/QM8JVuA1g5SceG8UvLoj9T6r+2XIktka6GK2oS66Fmo+0MP0s0HDPy+yJtl8lHY6UyzHw== +xterm-addon-image@0.6.0-beta.11: + version "0.6.0-beta.11" + resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.6.0-beta.11.tgz#17dffc5f38480a4fac231649ef6e313a8f46d614" + integrity sha512-KPsqJo8sSAawO4ze7xvjtS5evbPtlscy16NiSAteh/1AgOffmTnVhJTs3W84dnks2b13FaMEI/WOiCDIJjDV3A== -xterm-addon-search@0.13.0-beta.8: - version "0.13.0-beta.8" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.13.0-beta.8.tgz#90bc70bff87cd7e4f540cce51d9f968f04d5c0ef" - integrity sha512-Hlocg6gGvKAs4eRTW78wTtVqaOEurweJjdyPDjr7KrzwlEEsASsUXJaB2+vgsyTFKuYnCuLrp5u369jck993GQ== +xterm-addon-search@0.13.0-beta.17: + version "0.13.0-beta.17" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.13.0-beta.17.tgz#5379cf3085370c55241d99e95b008c0cbf4e6e53" + integrity sha512-0OKwV9isk4OXHLlhRE8Ohqnmpcd0ExWKtorjeaFxqm4f6xc30qZcJTs0hSMzVZ83foedPJcd6hH0hW2pnxOz3Q== -xterm-addon-serialize@0.11.0-beta.8: - version "0.11.0-beta.8" - resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.11.0-beta.8.tgz#343357aad7c549a37aacac2fb19692b1c2fd69cd" - integrity sha512-1jNrX+Y8dorWtjMvz02mRUP+ahqQ4STO0lYlKe8m06p+NYuFk24CylnPZFuBgk91OUU3RAdl4glaKh5M4f0/Zw== +xterm-addon-serialize@0.11.0-beta.17: + version "0.11.0-beta.17" + resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.11.0-beta.17.tgz#170738ab615e2974c593d41531d41c02b8af1da7" + integrity sha512-zNsUQfIbRD8fL0gHGJJ14DXL22Kduv6pR+QnISReoQqhEEsWANPKpOduJLce7B4y+BJuiOGkT7R7qbMKbj9Ecw== -xterm-addon-unicode11@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.5.0.tgz#41c0d96acc1e3bb6c6596eee64e163b6bca74be7" - integrity sha512-Jm4/g4QiTxiKiTbYICQgC791ubhIZyoIwxAIgOW8z8HWFNY+lwk+dwaKEaEeGBfM48Vk8fklsUW9u/PlenYEBg== +xterm-addon-unicode11@0.6.0-beta.9: + version "0.6.0-beta.9" + resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.6.0-beta.9.tgz#1f476f77cf8c8e4e7ca1a1421b85ac45405db679" + integrity sha512-DH9OIH0EakIdCnzZcIH7NLgUqXwa8WT9fqmi+2CWzt4NN1AH5kSE/3Vk+/uurE5upxDloKw6dtLH9XHpfULV6g== -xterm-addon-webgl@0.16.0-beta.16: - version "0.16.0-beta.16" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.16.tgz#2c31308f8c7f636576720adca529297f8fff3224" - integrity sha512-k0ZSwpBVtxXaqUc29CmlfFgIK7LEYAmwcDC3QVp2ESZo2JHYuQrWFpImjbzbYu0ON6qqcfbX8SnbzRid/XfGUg== +xterm-addon-webgl@0.16.0-beta.27: + version "0.16.0-beta.27" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.27.tgz#f4133a40044f0b6448ea87152abf6c9009729100" + integrity sha512-YiwCvTvgfcNGtQdpzcgxKINE6mG63cPfDxfoxCAbodNGYF66k9VzhIXNdCQtVtu+s6+pEc9YequENe78SSZloA== -xterm-headless@5.3.0-beta.46: - version "5.3.0-beta.46" - resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.3.0-beta.46.tgz#3f941f673e3c61aad2705e4b34b42fbe467aed48" - integrity sha512-e/VbZKrfyD1TTxOY5/jah3dUih6q8nTO5v5GQV7YOASp+yyI0XHKiDjLrCyFPQOLTKxCcRutMHvr+CoCcPpcHg== +xterm-headless@5.3.0-beta.58: + version "5.3.0-beta.58" + resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.3.0-beta.58.tgz#e6a3dbfafdb28435130ce3925ac3194c0ee2c281" + integrity sha512-ekzr3LX8p26qHDyVRs3lys08veHFv9wGOKaHd49kMgktICHsikJ48HNJq5Twp3ZnXyqlIP9CV34BZJ1SBfWmtQ== -xterm@5.3.0-beta.46: - version "5.3.0-beta.46" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.46.tgz#9bd2b2a588c88ae64f1989ca3bf5bd9116554b5a" - integrity sha512-keB3C6sXm56ug1+hylAQiNyZP4YtPgd2W+Gu/ZKwJMk5haD25DEnenBYL++Cl3+sNGYxOGS42QlmYj6Mrq1LPw== +xterm@5.3.0-beta.58: + version "5.3.0-beta.58" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.58.tgz#4bae06639c90952b270e4714938c151ce339b5f0" + integrity sha512-EIOgp+7aqCToI0XjDqORXAe2lJS/XSZXWPzCpyTDmF+DJ56gyw+913gdPoIEWOw3mRylrJtEyIpFcdW7CDA6rQ== y18n@^3.2.1: version "3.2.2" From c8219ae7685f9fe9a6d0a2a7e23bba1591ccf4d2 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 22 Aug 2023 07:55:06 -0700 Subject: [PATCH 187/223] Change applyAtProcessCreation behavior Part of #179476 --- .../api/common/extHostTerminalService.ts | 15 +++++++++------ .../vscode.proposed.envCollectionOptions.d.ts | 15 +++++++-------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index d91e8fe990f..371eace01f7 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -965,22 +965,25 @@ class UnifiedEnvironmentVariableCollection { } const key = this.getKey(variable, mutator.scope); const current = this.map.get(key); + const newOptions = mutator.options ? { + applyAtProcessCreation: mutator.options.applyAtProcessCreation ?? false, + applyAtShellIntegration: mutator.options.applyAtShellIntegration ?? false, + } : { + applyAtProcessCreation: true + }; if ( !current || current.value !== mutator.value || current.type !== mutator.type || - current.options?.applyAtProcessCreation !== (mutator.options.applyAtProcessCreation ?? true) || - current.options?.applyAtShellIntegration !== (mutator.options.applyAtShellIntegration ?? false) || + current.options?.applyAtProcessCreation !== newOptions.applyAtProcessCreation || + current.options?.applyAtShellIntegration !== newOptions.applyAtShellIntegration || current.scope?.workspaceFolder?.index !== mutator.scope?.workspaceFolder?.index ) { const key = this.getKey(variable, mutator.scope); const value: IEnvironmentVariableMutator = { variable, ...mutator, - options: { - applyAtProcessCreation: mutator.options.applyAtProcessCreation ?? true, - applyAtShellIntegration: mutator.options.applyAtShellIntegration ?? false, - } + options: newOptions }; this.map.set(key, value); this._onDidChangeCollection.fire(); diff --git a/src/vscode-dts/vscode.proposed.envCollectionOptions.d.ts b/src/vscode-dts/vscode.proposed.envCollectionOptions.d.ts index d25a92725a4..9a854922a2d 100644 --- a/src/vscode-dts/vscode.proposed.envCollectionOptions.d.ts +++ b/src/vscode-dts/vscode.proposed.envCollectionOptions.d.ts @@ -13,16 +13,12 @@ declare module 'vscode' { export interface EnvironmentVariableMutatorOptions { /** * Apply to the environment just before the process is created. - * - * Defaults to true. */ applyAtProcessCreation?: boolean; /** * Apply to the environment in the shell integration script. Note that this _will not_ apply * the mutator if shell integration is disabled or not working for some reason. - * - * Defaults to false. */ applyAtShellIntegration?: boolean; } @@ -32,24 +28,27 @@ declare module 'vscode' { */ export interface EnvironmentVariableMutator { /** - * Options applied to the mutator. + * Options applied to the mutator */ readonly options: EnvironmentVariableMutatorOptions; } export interface EnvironmentVariableCollection extends Iterable<[variable: string, mutator: EnvironmentVariableMutator]> { /** - * @param options Options applied to the mutator. + * @param options Options applied to the mutator, when not options are provided this will + * default to `{ applyAtProcessCreation: true }` */ replace(variable: string, value: string, options?: EnvironmentVariableMutatorOptions): void; /** - * @param options Options applied to the mutator. + * @param options Options applied to the mutator, when not options are provided this will + * default to `{ applyAtProcessCreation: true }` */ append(variable: string, value: string, options?: EnvironmentVariableMutatorOptions): void; /** - * @param options Options applied to the mutator. + * @param options Options applied to the mutator, when not options are provided this will + * default to `{ applyAtProcessCreation: true }` */ prepend(variable: string, value: string, options?: EnvironmentVariableMutatorOptions): void; } From 4f8b2ecbee391337d93bf2fdfe17a48945e1a107 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 22 Aug 2023 07:56:09 -0700 Subject: [PATCH 188/223] Fix typo --- src/vscode-dts/vscode.proposed.envCollectionOptions.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vscode-dts/vscode.proposed.envCollectionOptions.d.ts b/src/vscode-dts/vscode.proposed.envCollectionOptions.d.ts index 9a854922a2d..1de5bed146e 100644 --- a/src/vscode-dts/vscode.proposed.envCollectionOptions.d.ts +++ b/src/vscode-dts/vscode.proposed.envCollectionOptions.d.ts @@ -28,7 +28,7 @@ declare module 'vscode' { */ export interface EnvironmentVariableMutator { /** - * Options applied to the mutator + * Options applied to the mutator. */ readonly options: EnvironmentVariableMutatorOptions; } From be2c1b6c06e90dc6b66d1abe513a8c2dbf5ffc26 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 22 Aug 2023 08:21:32 -0700 Subject: [PATCH 189/223] Pass in correct options --- src/vs/workbench/api/common/extHostTerminalService.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index 371eace01f7..01ad5427b7c 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -942,21 +942,21 @@ class UnifiedEnvironmentVariableCollection { if (this._extension && options) { checkProposedApiEnabled(this._extension, 'envCollectionOptions'); } - this._setIfDiffers(variable, { value, type: EnvironmentVariableMutatorType.Replace, options: options ?? {}, scope }); + this._setIfDiffers(variable, { value, type: EnvironmentVariableMutatorType.Replace, options: options ?? { applyAtProcessCreation: true }, scope }); } append(variable: string, value: string, options: vscode.EnvironmentVariableMutatorOptions | undefined, scope: vscode.EnvironmentVariableScope | undefined): void { if (this._extension && options) { checkProposedApiEnabled(this._extension, 'envCollectionOptions'); } - this._setIfDiffers(variable, { value, type: EnvironmentVariableMutatorType.Append, options: options ?? {}, scope }); + this._setIfDiffers(variable, { value, type: EnvironmentVariableMutatorType.Append, options: options ?? { applyAtProcessCreation: true }, scope }); } prepend(variable: string, value: string, options: vscode.EnvironmentVariableMutatorOptions | undefined, scope: vscode.EnvironmentVariableScope | undefined): void { if (this._extension && options) { checkProposedApiEnabled(this._extension, 'envCollectionOptions'); } - this._setIfDiffers(variable, { value, type: EnvironmentVariableMutatorType.Prepend, options: options ?? {}, scope }); + this._setIfDiffers(variable, { value, type: EnvironmentVariableMutatorType.Prepend, options: options ?? { applyAtProcessCreation: true }, scope }); } private _setIfDiffers(variable: string, mutator: vscode.EnvironmentVariableMutator & { scope: vscode.EnvironmentVariableScope | undefined }): void { From bdda40632a4d850a6411850e5d0a7cf03a840186 Mon Sep 17 00:00:00 2001 From: David Dossett Date: Tue, 22 Aug 2023 09:12:18 -0700 Subject: [PATCH 190/223] Tweak slash command styling..again (#190290) --- .../contrib/chat/browser/chatSlashCommandContentWidget.css | 4 ++-- src/vs/workbench/contrib/chat/browser/media/chat.css | 5 +++-- src/vs/workbench/contrib/inlineChat/browser/inlineChat.css | 2 +- .../workbench/contrib/inlineChat/browser/inlineChatWidget.ts | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatSlashCommandContentWidget.css b/src/vs/workbench/contrib/chat/browser/chatSlashCommandContentWidget.css index 6f78c0edda9..d2568dbbfed 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSlashCommandContentWidget.css +++ b/src/vs/workbench/contrib/chat/browser/chatSlashCommandContentWidget.css @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ .chat-slash-command-content-widget { - padding: 1px 3px; - border-radius: 3px; background-color: var(--vscode-chat-slashCommandBackground); color: var(--vscode-chat-slashCommandForeground); + border-radius: 3px; + padding: 1px; } diff --git a/src/vs/workbench/contrib/chat/browser/media/chat.css b/src/vs/workbench/contrib/chat/browser/media/chat.css index 7e193499146..a5275042f81 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chat.css +++ b/src/vs/workbench/contrib/chat/browser/media/chat.css @@ -228,9 +228,10 @@ margin: 0px 20px; background-color: var(--vscode-input-background); border: 1px solid var(--vscode-input-border, transparent); - border-radius: 2px; + border-radius: 4px; position: relative; - padding: 0 4px; + padding: 0 6px; + margin-bottom: 4px; align-items: center; justify-content: space-between; } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChat.css b/src/vs/workbench/contrib/inlineChat/browser/inlineChat.css index 457fd102c76..fe2e0ccde7a 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChat.css +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChat.css @@ -42,7 +42,7 @@ display: flex; align-items: center; justify-content: space-between; - padding: 2px 2px 2px 4px; + padding: 2px 2px 2px 6px; background-color: var(--vscode-inlineChatInput-background); cursor: text; } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts index bf16c0aec05..31d9fa9c36b 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts @@ -756,7 +756,7 @@ export class InlineChatWidget { this._slashCommandContentWidget.show(); // inject detail when otherwise empty - if (firstLine === `/${command.command} `) { + if (firstLine === `/${command.command}`) { newDecorations.push({ range: new Range(1, withSlash.length + 1, 1, withSlash.length + 2), options: { From e5a07457e479387985a04705c6d374e83ae90375 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 22 Aug 2023 15:27:59 +0200 Subject: [PATCH 191/223] Improves moved code arrow rendering. --- .../lib/stylelint/vscode-known-variables.json | 3 +- src/vs/base/common/arrays.ts | 17 ++ .../widget/diffEditorWidget2/colors.ts | 6 + .../diffEditorDecorations.ts | 63 +++--- .../diffEditorWidget2/diffEditorViewModel.ts | 4 +- .../diffEditorWidget2.contribution.ts | 7 +- .../diffEditorWidget2/diffEditorWidget2.ts | 24 ++- .../widget/diffEditorWidget2/lineAlignment.ts | 4 +- .../diffEditorWidget2/movedBlocksLines.ts | 189 +++++++++++++----- .../widget/diffEditorWidget2/style.css | 20 ++ src/vs/editor/common/core/lineRange.ts | 4 + src/vs/editor/common/core/offsetRange.ts | 58 ++++++ src/vs/monaco.d.ts | 1 + 13 files changed, 297 insertions(+), 103 deletions(-) diff --git a/build/lib/stylelint/vscode-known-variables.json b/build/lib/stylelint/vscode-known-variables.json index 73faa4aaa12..a7d22a17b85 100644 --- a/build/lib/stylelint/vscode-known-variables.json +++ b/build/lib/stylelint/vscode-known-variables.json @@ -99,6 +99,7 @@ "--vscode-diffEditor-insertedTextBackground", "--vscode-diffEditor-insertedTextBorder", "--vscode-diffEditor-move-border", + "--vscode-diffEditor-moveActive-border", "--vscode-diffEditor-removedLineBackground", "--vscode-diffEditor-removedTextBackground", "--vscode-diffEditor-removedTextBorder", @@ -778,4 +779,4 @@ "--z-index-notebook-sticky-scroll", "--zoom-factor" ] -} +} \ No newline at end of file diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index a54c2c59f9c..22c2859f5e9 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -707,6 +707,8 @@ export function tieBreakComparators(...comparators: Comparator[]): */ export const numberComparator: Comparator = (a, b) => a - b; +export const booleanComparator: Comparator = (a, b) => numberComparator(a ? 1 : 0, b ? 1 : 0); + export function reverseOrder(comparator: Comparator): Comparator { return (a, b) => -comparator(a, b); } @@ -754,6 +756,21 @@ export function findMinBy(items: readonly T[], comparator: Comparator): T return findMaxBy(items, (a, b) => -comparator(a, b)); } +export function findMaxIdxBy(items: readonly T[], comparator: Comparator): number { + if (items.length === 0) { + return -1; + } + + let maxIdx = 0; + for (let i = 1; i < items.length; i++) { + const item = items[i]; + if (comparator(item, items[maxIdx]) > 0) { + maxIdx = i; + } + } + return maxIdx; +} + export class ArrayQueue { private firstIdx = 0; private lastIdx = this.items.length - 1; diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/colors.ts b/src/vs/editor/browser/widget/diffEditorWidget2/colors.ts index 8c78445d74b..449e67f06f8 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/colors.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/colors.ts @@ -11,3 +11,9 @@ export const diffMoveBorder = registerColor( { dark: '#8b8b8b9c', light: '#8b8b8b9c', hcDark: '#8b8b8b9c', hcLight: '#8b8b8b9c', }, localize('diffEditor.move.border', 'The border color for text that got moved in the diff editor.') ); + +export const diffMoveBorderActive = registerColor( + 'diffEditor.moveActive.border', + { dark: '#FFA500', light: '#FFA500', hcDark: '#FFA500', hcLight: '#FFA500', }, + localize('diffEditor.moveActive.border', 'The active border color for text that got moved in the diff editor.') +); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorDecorations.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorDecorations.ts index 5d9af4098f9..194f37c9882 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorDecorations.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorDecorations.ts @@ -5,14 +5,12 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { IObservable, derived } from 'vs/base/common/observable'; -import { isDefined } from 'vs/base/common/types'; import { arrowRevertChange, diffAddDecoration, diffAddDecorationEmpty, diffDeleteDecoration, diffDeleteDecorationEmpty, diffLineAddDecorationBackground, diffLineAddDecorationBackgroundWithIndicator, diffLineDeleteDecorationBackground, diffLineDeleteDecorationBackgroundWithIndicator, diffWholeLineAddDecoration, diffWholeLineDeleteDecoration } from 'vs/editor/browser/widget/diffEditorWidget2/decorations'; import { DiffEditorEditors } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors'; import { DiffEditorOptions } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorOptions'; import { DiffEditorViewModel } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel'; import { MovedBlocksLinesPart } from 'vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines'; import { applyObservableDecorations } from 'vs/editor/browser/widget/diffEditorWidget2/utils'; -import { LineRange } from 'vs/editor/common/core/lineRange'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { IModelDeltaDecoration } from 'vs/editor/common/model'; @@ -42,46 +40,37 @@ export class DiffEditorDecorations extends Disposable { const originalDecorations: IModelDeltaDecoration[] = []; const modifiedDecorations: IModelDeltaDecoration[] = []; - for (const m of diff.mappings) { - const fullRangeOriginal = LineRange.subtract(m.lineRangeMapping.originalRange, currentMove?.lineRangeMapping.original) - .map(i => i.toInclusiveRange()).filter(isDefined); - for (const range of fullRangeOriginal) { - originalDecorations.push({ range, options: renderIndicators ? diffLineDeleteDecorationBackgroundWithIndicator : diffLineDeleteDecorationBackground }); - } - - const fullRangeModified = LineRange.subtract(m.lineRangeMapping.modifiedRange, currentMove?.lineRangeMapping.modified) - .map(i => i.toInclusiveRange()).filter(isDefined); - for (const range of fullRangeModified) { - modifiedDecorations.push({ range, options: renderIndicators ? diffLineAddDecorationBackgroundWithIndicator : diffLineAddDecorationBackground }); - } - - if (m.lineRangeMapping.modifiedRange.isEmpty || m.lineRangeMapping.originalRange.isEmpty) { - for (const range of fullRangeOriginal) { - originalDecorations.push({ range, options: diffWholeLineDeleteDecoration }); + if (!currentMove) { + for (const m of diff.mappings) { + if (!m.lineRangeMapping.originalRange.isEmpty) { + originalDecorations.push({ range: m.lineRangeMapping.originalRange.toInclusiveRange()!, options: renderIndicators ? diffLineDeleteDecorationBackgroundWithIndicator : diffLineDeleteDecorationBackground }); } - for (const range of fullRangeModified) { - modifiedDecorations.push({ range, options: diffWholeLineAddDecoration }); + if (!m.lineRangeMapping.modifiedRange.isEmpty) { + modifiedDecorations.push({ range: m.lineRangeMapping.modifiedRange.toInclusiveRange()!, options: renderIndicators ? diffLineAddDecorationBackgroundWithIndicator : diffLineAddDecorationBackground }); } - } else { - for (const i of m.lineRangeMapping.innerChanges || []) { - if (currentMove - && (currentMove.lineRangeMapping.original.intersect(new LineRange(i.originalRange.startLineNumber, i.originalRange.endLineNumber)) - || currentMove.lineRangeMapping.modified.intersect(new LineRange(i.modifiedRange.startLineNumber, i.modifiedRange.endLineNumber)))) { - continue; - } - // Don't show empty markers outside the line range - if (m.lineRangeMapping.originalRange.contains(i.originalRange.startLineNumber)) { - originalDecorations.push({ range: i.originalRange, options: (i.originalRange.isEmpty() && showEmptyDecorations) ? diffDeleteDecorationEmpty : diffDeleteDecoration }); + if (m.lineRangeMapping.modifiedRange.isEmpty || m.lineRangeMapping.originalRange.isEmpty) { + if (!m.lineRangeMapping.originalRange.isEmpty) { + originalDecorations.push({ range: m.lineRangeMapping.originalRange.toInclusiveRange()!, options: diffWholeLineDeleteDecoration }); } - if (m.lineRangeMapping.modifiedRange.contains(i.modifiedRange.startLineNumber)) { - modifiedDecorations.push({ range: i.modifiedRange, options: (i.modifiedRange.isEmpty() && showEmptyDecorations) ? diffAddDecorationEmpty : diffAddDecoration }); + if (!m.lineRangeMapping.modifiedRange.isEmpty) { + modifiedDecorations.push({ range: m.lineRangeMapping.modifiedRange.toInclusiveRange()!, options: diffWholeLineAddDecoration }); + } + } else { + for (const i of m.lineRangeMapping.innerChanges || []) { + // Don't show empty markers outside the line range + if (m.lineRangeMapping.originalRange.contains(i.originalRange.startLineNumber)) { + originalDecorations.push({ range: i.originalRange, options: (i.originalRange.isEmpty() && showEmptyDecorations) ? diffDeleteDecorationEmpty : diffDeleteDecoration }); + } + if (m.lineRangeMapping.modifiedRange.contains(i.modifiedRange.startLineNumber)) { + modifiedDecorations.push({ range: i.modifiedRange, options: (i.modifiedRange.isEmpty() && showEmptyDecorations) ? diffAddDecorationEmpty : diffAddDecoration }); + } } } - } - if (!m.lineRangeMapping.modifiedRange.isEmpty && this._options.shouldRenderRevertArrows.read(reader) && !currentMove) { - modifiedDecorations.push({ range: Range.fromPositions(new Position(m.lineRangeMapping.modifiedRange.startLineNumber, 1)), options: arrowRevertChange }); + if (!m.lineRangeMapping.modifiedRange.isEmpty && this._options.shouldRenderRevertArrows.read(reader) && !currentMove) { + modifiedDecorations.push({ range: Range.fromPositions(new Position(m.lineRangeMapping.modifiedRange.startLineNumber, 1)), options: arrowRevertChange }); + } } } @@ -107,7 +96,7 @@ export class DiffEditorDecorations extends Disposable { originalDecorations.push({ range: m.lineRangeMapping.original.toInclusiveRange()!, options: { description: 'moved', - blockClassName: 'movedOriginal', + blockClassName: 'movedOriginal' + (m === currentMove ? ' currentMove' : ''), blockPadding: [MovedBlocksLinesPart.movedCodeBlockPadding, 0, MovedBlocksLinesPart.movedCodeBlockPadding, MovedBlocksLinesPart.movedCodeBlockPadding], } }); @@ -115,7 +104,7 @@ export class DiffEditorDecorations extends Disposable { modifiedDecorations.push({ range: m.lineRangeMapping.modified.toInclusiveRange()!, options: { description: 'moved', - blockClassName: 'movedModified', + blockClassName: 'movedModified' + (m === currentMove ? ' currentMove' : ''), blockPadding: [4, 0, 4, 4], } }); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts index 448ccb5b27e..4927ebeb9c1 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts @@ -11,13 +11,14 @@ import { ISerializedLineRange, LineRange } from 'vs/editor/common/core/lineRange import { Range } from 'vs/editor/common/core/range'; import { IDocumentDiff, IDocumentDiffProvider } from 'vs/editor/common/diff/documentDiffProvider'; import { LineRangeMapping, MovedText, RangeMapping, SimpleLineRangeMapping } from 'vs/editor/common/diff/linesDiffComputer'; -import { lineRangeMappingFromRangeMappings } from 'vs/editor/common/diff/standardLinesDiffComputer'; +import { StandardLinesDiffComputer, lineRangeMappingFromRangeMappings } from 'vs/editor/common/diff/standardLinesDiffComputer'; import { IDiffEditorModel, IDiffEditorViewModel } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; import { TextEditInfo } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/beforeEditPositionMapper'; import { combineTextEditInfos } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/combineTextEditInfos'; import { lengthAdd, lengthDiffNonNegative, lengthGetLineCount, lengthOfRange, lengthToPosition, lengthZero, positionToLength } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/length'; import { DiffEditorOptions } from './diffEditorOptions'; +import { readHotReloadableExport } from 'vs/editor/browser/widget/diffEditorWidget2/utils'; export class DiffEditorViewModel extends Disposable implements IDiffEditorViewModel { private readonly _isDiffUpToDate = observableValue('isDiffUpToDate', false); @@ -147,6 +148,7 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo debouncer.cancel(); contentChangedSignal.read(reader); documentDiffProviderOptionChanged.read(reader); + readHotReloadableExport(StandardLinesDiffComputer, reader); this._isDiffUpToDate.set(false, undefined); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.contribution.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.contribution.ts index d312eb5629a..13c654d867f 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.contribution.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.contribution.ts @@ -91,14 +91,10 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { ) }); -/* -TODO@hediet add this back once move detection is more polished. -Users can still enable this via settings.json (config.diffEditor.experimental.showMoves). - MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: new ToggleShowMovedCodeBlocks().desc.id, - title: localize('showMoves', "Show Moves"), + title: localize('showMoves', "Show Moved Code Blocks"), icon: Codicon.move, toggled: ContextKeyEqualsExpr.create('config.diffEditor.experimental.showMoves', true), }, @@ -106,7 +102,6 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { group: '1_diff', when: ContextKeyEqualsExpr.create('diffEditorVersion', 2) }); -*/ const diffEditorCategory: ILocalizedString = { value: localize('diffEditor', 'Diff Editor'), diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts index 21b5f67aaae..63973ae2e03 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts @@ -79,6 +79,8 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { private readonly _options: DiffEditorOptions; private readonly _editors: DiffEditorEditors; + private readonly movedBlocksLinesPart = observableValue('MovedBlocksLinesPart', undefined); + constructor( private readonly _domElement: HTMLElement, options: Readonly, @@ -204,13 +206,15 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { this._register(keepAlive(this._layoutInfo, true)); - this._register(new MovedBlocksLinesPart( - this.elements.root, - this._diffModel, - this._layoutInfo.map(i => i.originalEditor), - this._layoutInfo.map(i => i.modifiedEditor), - this._editors, - )); + this._register(autorunWithStore((reader, store) => { + this.movedBlocksLinesPart.set(store.add(new (readHotReloadableExport(MovedBlocksLinesPart, reader))( + this.elements.root, + this._diffModel, + this._layoutInfo.map(i => i.originalEditor), + this._layoutInfo.map(i => i.modifiedEditor), + this._editors, + )), undefined); + })); this._register(applyStyle(this.elements.overlay, { width: this._layoutInfo.map((i, r) => i.originalEditor.width + (this._options.renderSideBySide.read(r) ? 0 : i.modifiedEditor.width)), @@ -285,13 +289,15 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { const originalWidth = sashLeft ?? Math.max(5, this._editors.original.getLayoutInfo().decorationsLeft); const modifiedWidth = width - originalWidth - (this._options.renderOverviewRuler.read(reader) ? OverviewRulerPart.ENTIRE_DIFF_OVERVIEW_WIDTH : 0); - this.elements.original.style.width = originalWidth + 'px'; + const movedBlocksLinesWidth = this.movedBlocksLinesPart.read(reader)?.width.read(reader) ?? 0; + const originalWidthWithoutMovedBlockLines = originalWidth - movedBlocksLinesWidth; + this.elements.original.style.width = originalWidthWithoutMovedBlockLines + 'px'; this.elements.original.style.left = '0px'; this.elements.modified.style.width = modifiedWidth + 'px'; this.elements.modified.style.left = originalWidth + 'px'; - this._editors.original.layout({ width: originalWidth, height }); + this._editors.original.layout({ width: originalWidthWithoutMovedBlockLines, height }); this._editors.modified.layout({ width: modifiedWidth, height }); return { diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/lineAlignment.ts b/src/vs/editor/browser/widget/diffEditorWidget2/lineAlignment.ts index 92fb780d62c..3a95ce28942 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/lineAlignment.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/lineAlignment.ts @@ -265,7 +265,7 @@ export class ViewZoneManager extends Disposable { } else { const delta = a.modifiedHeightInPx - a.originalHeightInPx; if (delta > 0) { - if (syncedMovedText?.lineRangeMapping.original.contains(a.originalRange.endLineNumberExclusive - 1)) { + if (syncedMovedText?.lineRangeMapping.original.delta(-1).deltaLength(2).contains(a.originalRange.endLineNumberExclusive - 1)) { continue; } @@ -276,7 +276,7 @@ export class ViewZoneManager extends Disposable { showInHiddenAreas: true, }); } else { - if (syncedMovedText?.lineRangeMapping.modified.contains(a.modifiedRange.endLineNumberExclusive - 1)) { + if (syncedMovedText?.lineRangeMapping.modified.delta(-1).deltaLength(2).contains(a.modifiedRange.endLineNumberExclusive - 1)) { continue; } diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts b/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts index b4b628c711f..0763643bf3a 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts @@ -3,17 +3,27 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable } from 'vs/base/common/lifecycle'; -import { IObservable, autorun, observableFromEvent, observableSignalFromEvent } from 'vs/base/common/observable'; +import { booleanComparator, compareBy, findMaxIdxBy, findMinBy, numberComparator, tieBreakComparators } from 'vs/base/common/arrays'; +import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import { IObservable, autorun, derived, keepAlive, observableFromEvent, observableSignalFromEvent, observableValue } from 'vs/base/common/observable'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { DiffEditorEditors } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors'; import { DiffEditorViewModel } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel'; import { EditorLayoutInfo } from 'vs/editor/common/config/editorOptions'; import { LineRange } from 'vs/editor/common/core/lineRange'; +import { OffsetRange, OffsetRangeSet } from 'vs/editor/common/core/offsetRange'; +import { MovedText } from 'vs/editor/common/diff/linesDiffComputer'; export class MovedBlocksLinesPart extends Disposable { public static readonly movedCodeBlockPadding = 4; + private readonly _element: SVGElement; + private readonly _originalScrollTop = observableFromEvent(this._editors.original.onDidScrollChange, () => this._editors.original.getScrollTop()); + private readonly _modifiedScrollTop = observableFromEvent(this._editors.modified.onDidScrollChange, () => this._editors.modified.getScrollTop()); + private readonly _viewZonesChanged = observableSignalFromEvent('onDidChangeViewZones', this._editors.modified.onDidChangeViewZones); + + public readonly width = observableValue('width', 0); + constructor( private readonly _rootElement: HTMLElement, private readonly _diffModel: IObservable, @@ -23,9 +33,10 @@ export class MovedBlocksLinesPart extends Disposable { ) { super(); - const element = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); - element.setAttribute('class', 'moved-blocks-lines'); - this._rootElement.appendChild(element); + this._element = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + this._element.setAttribute('class', 'moved-blocks-lines'); + this._rootElement.appendChild(this._element); + this._register(toDisposable(() => this._element.remove())); this._register(autorun(reader => { /** @description update moved blocks lines positioning */ @@ -35,58 +46,142 @@ export class MovedBlocksLinesPart extends Disposable { return; } - element.style.left = `${info.width - info.verticalScrollbarWidth}px`; - element.style.height = `${info.height}px`; - element.style.width = `${info.verticalScrollbarWidth + info.contentLeft - MovedBlocksLinesPart.movedCodeBlockPadding}px`; + this._element.style.left = `${info.width - info.verticalScrollbarWidth}px`; + this._element.style.height = `${info.height}px`; + this._element.style.width = `${info.verticalScrollbarWidth + info.contentLeft - MovedBlocksLinesPart.movedCodeBlockPadding + this.width.read(reader)}px`; })); - const originalScrollTop = observableFromEvent(this._editors.original.onDidScrollChange, () => this._editors.original.getScrollTop()); - const modifiedScrollTop = observableFromEvent(this._editors.modified.onDidScrollChange, () => this._editors.modified.getScrollTop()); - const viewZonesChanged = observableSignalFromEvent('onDidChangeViewZones', this._editors.modified.onDidChangeViewZones); + this._register(keepAlive(this._state, true)); + } - this._register(autorun(reader => { - element.replaceChildren(); + private readonly _state = derived(reader => { + /** @description update moved blocks lines */ - /** @description update moved blocks lines */ - const moves = this._diffModel.read(reader)?.diff.read(reader)?.movedTexts; - if (!moves) { - return; + this._element.replaceChildren(); + const model = this._diffModel.read(reader); + const moves = model?.diff.read(reader)?.movedTexts; + if (!moves || moves.length === 0) { + this.width.set(0, undefined); + return; + } + + this._viewZonesChanged.read(reader); + + const infoOrig = this._originalEditorLayoutInfo.read(reader); + const infoMod = this._modifiedEditorLayoutInfo.read(reader); + if (!infoOrig || !infoMod) { + this.width.set(0, undefined); + return; + } + + const lines = moves.map((move) => { + function computeLineStart(range: LineRange, editor: ICodeEditor) { + const t1 = editor.getTopForLineNumber(range.startLineNumber); + const t2 = editor.getTopForLineNumber(range.endLineNumberExclusive); + return (t1 + t2) / 2; } - viewZonesChanged.read(reader); + const start = computeLineStart(move.lineRangeMapping.original, this._editors.original); + const startOffset = this._originalScrollTop.read(reader); + const end = computeLineStart(move.lineRangeMapping.modified, this._editors.modified); + const endOffset = this._modifiedScrollTop.read(reader); - const info = this._originalEditorLayoutInfo.read(reader); - const info2 = this._modifiedEditorLayoutInfo.read(reader); - if (!info || !info2) { - return; + const from = start - startOffset; + const to = end - endOffset; + + const top = Math.min(start, end); + const bottom = Math.max(start, end); + + return { range: new OffsetRange(top, bottom), from, to, fromWithoutScroll: start, toWithoutScroll: end, move }; + }); + + lines.sort(tieBreakComparators( + compareBy(l => l.fromWithoutScroll > l.toWithoutScroll, booleanComparator), + compareBy(l => -l.fromWithoutScroll, numberComparator) + )); + + const layout = LinesLayout.compute(lines.map(l => l.range)); + + const padding = 10; + const lineAreaLeft = infoOrig.verticalScrollbarWidth; + const lineAreaWidth = (layout.getTrackCount() - 1) * 10 + padding * 2; + const width = lineAreaLeft + lineAreaWidth + (infoMod.contentLeft - MovedBlocksLinesPart.movedCodeBlockPadding); + + let idx = 0; + for (const line of lines) { + const track = layout.getTrack(idx); + const verticalY = lineAreaLeft + padding + track * 10; + + const arrowHeight = 15; + const arrowWidth = 15; + const right = width; + + const rectWidth = infoMod.glyphMarginWidth + infoMod.lineNumbersWidth; + const rectHeight = 18; + const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); + rect.classList.add('arrow-rectangle'); + rect.setAttribute('x', `${right - rectWidth}`); + rect.setAttribute('y', `${line.to - rectHeight / 2}`); + rect.setAttribute('width', `${rectWidth}`); + rect.setAttribute('height', `${rectHeight}`); + this._element.appendChild(rect); + + const path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + if (line.move === model.syncedMovedTexts.read(reader)) { + path.classList.add('currentMove'); } - const width = info.verticalScrollbarWidth + info.contentLeft - MovedBlocksLinesPart.movedCodeBlockPadding; + path.setAttribute('d', `M ${0} ${line.from} L ${verticalY} ${line.from} L ${verticalY} ${line.to} L ${right - arrowWidth} ${line.to}`); + path.setAttribute('fill', 'none'); + this._element.appendChild(path); - let idx = 0; - for (const m of moves) { - function computeLineStart(range: LineRange, editor: ICodeEditor) { - const t1 = editor.getTopForLineNumber(range.startLineNumber); - const t2 = editor.getTopForLineNumber(range.endLineNumberExclusive); - return (t1 + t2) / 2; + const arrowRight = document.createElementNS('http://www.w3.org/2000/svg', 'polygon'); + arrowRight.classList.add('arrow'); + if (line.move === model.syncedMovedTexts.read(reader)) { + arrowRight.classList.add('currentMove'); + } + arrowRight.setAttribute('points', `${right - arrowWidth},${line.to - arrowHeight / 2} ${right},${line.to} ${right - arrowWidth},${line.to + arrowHeight / 2}`); + this._element.appendChild(arrowRight); + + idx++; + } + + this.width.set(lineAreaWidth, undefined); + }); +} + +class LinesLayout { + public static compute(lines: OffsetRange[]): LinesLayout { + const setsPerTrack: OffsetRangeSet[] = []; + const trackPerLineIdx: number[] = []; + + for (const line of lines) { + let trackIdx = setsPerTrack.findIndex(set => !set.intersectsStrict(line)); + if (trackIdx === -1) { + const maxTrackCount = 6; + if (setsPerTrack.length >= maxTrackCount) { + trackIdx = findMaxIdxBy(setsPerTrack, compareBy(set => set.intersectWithRangeLength(line), numberComparator)); + } else { + trackIdx = setsPerTrack.length; + setsPerTrack.push(new OffsetRangeSet()); } - - const start = computeLineStart(m.lineRangeMapping.original, this._editors.original); - const startOffset = originalScrollTop.read(reader); - const end = computeLineStart(m.lineRangeMapping.modified, this._editors.modified); - const endOffset = modifiedScrollTop.read(reader); - - const top = start - startOffset; - const bottom = end - endOffset; - - const center = (width / 2) - moves.length * 5 + idx * 10; - idx++; - - const path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); - path.setAttribute('d', `M ${0} ${top} L ${center} ${top} L ${center} ${bottom} L ${width} ${bottom}`); - - path.setAttribute('fill', 'none'); - element.appendChild(path); } - })); + setsPerTrack[trackIdx].addRange(line); + trackPerLineIdx.push(trackIdx); + } + + return new LinesLayout(setsPerTrack.length, trackPerLineIdx); + } + + private constructor( + private readonly _trackCount: number, + private readonly trackPerLineIdx: number[] + ) { } + + getTrack(lineIdx: number): number { + return this.trackPerLineIdx[lineIdx]; + } + + getTrackCount(): number { + return this._trackCount; } } diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/style.css b/src/vs/editor/browser/widget/diffEditorWidget2/style.css index f8243853b70..8fd6377362f 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/style.css +++ b/src/vs/editor/browser/widget/diffEditorWidget2/style.css @@ -78,6 +78,26 @@ border: 2px solid var(--vscode-diffEditor-move-border); } +.monaco-editor .movedOriginal.currentMove, .monaco-editor .movedModified.currentMove { + border: 2px solid var(--vscode-diffEditor-moveActive-border); +} + +.monaco-diff-editor .moved-blocks-lines path.currentMove { + stroke: var(--vscode-diffEditor-moveActive-border); +} + +.monaco-diff-editor .moved-blocks-lines .arrow { + fill: var(--vscode-diffEditor-move-border); +} + +.monaco-diff-editor .moved-blocks-lines .arrow.currentMove { + fill: var(--vscode-diffEditor-moveActive-border); +} + +.monaco-diff-editor .moved-blocks-lines .arrow-rectangle { + fill: var(--vscode-editor-background); +} + .monaco-diff-editor .moved-blocks-lines { position: absolute; pointer-events: none; diff --git a/src/vs/editor/common/core/lineRange.ts b/src/vs/editor/common/core/lineRange.ts index c8779a2881a..568bf03ca2b 100644 --- a/src/vs/editor/common/core/lineRange.ts +++ b/src/vs/editor/common/core/lineRange.ts @@ -154,6 +154,10 @@ export class LineRange { return new LineRange(this.startLineNumber + offset, this.endLineNumberExclusive + offset); } + public deltaLength(offset: number): LineRange { + return new LineRange(this.startLineNumber, this.endLineNumberExclusive + offset); + } + /** * The number of lines this line range spans. */ diff --git a/src/vs/editor/common/core/offsetRange.ts b/src/vs/editor/common/core/offsetRange.ts index 14ff2039fc7..d46cf88a425 100644 --- a/src/vs/editor/common/core/offsetRange.ts +++ b/src/vs/editor/common/core/offsetRange.ts @@ -91,3 +91,61 @@ export class OffsetRange { return undefined; } } + +export class OffsetRangeSet { + private readonly _sortedRanges: OffsetRange[] = []; + + public addRange(range: OffsetRange): void { + let i = 0; + while (i < this._sortedRanges.length && this._sortedRanges[i].endExclusive < range.start) { + i++; + } + let j = i; + while (j < this._sortedRanges.length && this._sortedRanges[j].start <= range.endExclusive) { + j++; + } + if (i === j) { + this._sortedRanges.splice(i, 0, range); + } else { + const start = Math.min(range.start, this._sortedRanges[i].start); + const end = Math.max(range.endExclusive, this._sortedRanges[j - 1].endExclusive); + this._sortedRanges.splice(i, j - i, new OffsetRange(start, end)); + } + } + + public toString(): string { + return this._sortedRanges.map(r => r.toString()).join(', '); + } + + /** + * Returns of there is a value that is contained in this instance and the given range. + */ + public intersectsStrict(other: OffsetRange): boolean { + // TODO use binary search + let i = 0; + while (i < this._sortedRanges.length && this._sortedRanges[i].endExclusive <= other.start) { + i++; + } + return i < this._sortedRanges.length && this._sortedRanges[i].start < other.endExclusive; + } + + public intersectWithRange(other: OffsetRange): OffsetRangeSet { + // TODO use binary search + slice + const result = new OffsetRangeSet(); + for (const range of this._sortedRanges) { + const intersection = range.intersect(other); + if (intersection) { + result.addRange(intersection); + } + } + return result; + } + + public intersectWithRangeLength(other: OffsetRange): number { + return this.intersectWithRange(other).length; + } + + public get length(): number { + return this._sortedRanges.reduce((prev, cur) => prev + cur.length, 0); + } +} diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 8fbfcd493ae..e6cd4654946 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -2460,6 +2460,7 @@ declare namespace monaco.editor { * Moves this line range by the given offset of line numbers. */ delta(offset: number): LineRange; + deltaLength(offset: number): LineRange; /** * The number of lines this line range spans. */ From 3a26bb70967560f42b94749025cfb26cf7db97c3 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 22 Aug 2023 18:01:36 +0200 Subject: [PATCH 192/223] Fixes CI --- .../browser/widget/diffEditorWidget2/movedBlocksLines.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts b/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts index 0763643bf3a..4453cb7ce2d 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { booleanComparator, compareBy, findMaxIdxBy, findMinBy, numberComparator, tieBreakComparators } from 'vs/base/common/arrays'; +import { booleanComparator, compareBy, findMaxIdxBy, numberComparator, tieBreakComparators } from 'vs/base/common/arrays'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IObservable, autorun, derived, keepAlive, observableFromEvent, observableSignalFromEvent, observableValue } from 'vs/base/common/observable'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; @@ -12,7 +12,6 @@ import { DiffEditorViewModel } from 'vs/editor/browser/widget/diffEditorWidget2/ import { EditorLayoutInfo } from 'vs/editor/common/config/editorOptions'; import { LineRange } from 'vs/editor/common/core/lineRange'; import { OffsetRange, OffsetRangeSet } from 'vs/editor/common/core/offsetRange'; -import { MovedText } from 'vs/editor/common/diff/linesDiffComputer'; export class MovedBlocksLinesPart extends Disposable { public static readonly movedCodeBlockPadding = 4; From 026f38a82cf9c727823d828d9df05e376b441a02 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 22 Aug 2023 18:44:27 +0200 Subject: [PATCH 193/223] Refactor fix for #188104 (#190977) --- .../common/abstractExtensionManagementService.ts | 14 +++++--------- .../common/extensionManagement.ts | 1 + .../node/extensionManagementService.ts | 2 +- .../browser/gettingStartedService.ts | 5 +++-- .../common/webExtensionManagementService.ts | 2 +- 5 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts index 23f3a635495..829a9b679c9 100644 --- a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts +++ b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts @@ -16,7 +16,7 @@ import * as nls from 'vs/nls'; import { ExtensionManagementError, IExtensionGalleryService, IExtensionIdentifier, IExtensionManagementParticipant, IGalleryExtension, ILocalExtension, InstallOperation, IExtensionsControlManifest, StatisticType, isTargetPlatformCompatible, TargetPlatformToString, ExtensionManagementErrorCode, - InstallOptions, InstallVSIXOptions, UninstallOptions, Metadata, InstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionResult, UninstallExtensionEvent, IExtensionManagementService, InstallExtensionInfo, EXTENSION_INSTALL_SKIP_WALKTHROUGH_CONTEXT + InstallOptions, InstallVSIXOptions, UninstallOptions, Metadata, InstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionResult, UninstallExtensionEvent, IExtensionManagementService, InstallExtensionInfo, EXTENSION_INSTALL_DEP_PACK_CONTEXT } from 'vs/platform/extensionManagement/common/extensionManagement'; import { areSameExtensions, ExtensionKey, getGalleryExtensionId, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ExtensionType, IExtensionManifest, isApplicationScopedExtension, TargetPlatform } from 'vs/platform/extensions/common/extensions'; @@ -35,6 +35,7 @@ export interface IInstallExtensionTask { readonly source: IGalleryExtension | URI; readonly operation: InstallOperation; readonly profileLocation: URI; + readonly options: InstallExtensionTaskOptions; readonly verificationStatus?: ExtensionVerificationStatus; run(): Promise; waitUntilTaskIsFinished(): Promise; @@ -248,7 +249,6 @@ export abstract class AbstractExtensionManagementService extends Disposable impl allInstallExtensionTasks.push({ task: installExtensionTask, manifest }); let installExtensionHasDependents: boolean = false; - const hasPackExtensions = manifest.extensionPack && manifest.extensionPack.length > 0; try { if (installExtensionTaskOptions.donotIncludePackAndDependencies) { this.logService.info('Installing the extension without checking dependencies and pack', installExtensionTask.identifier.id); @@ -256,6 +256,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl try { const allDepsAndPackExtensionsToInstall = await this.getAllDepsAndPackExtensions(installExtensionTask.identifier, manifest, !!installExtensionTaskOptions.installOnlyNewlyAddedFromExtensionPack, !!installExtensionTaskOptions.installPreReleaseVersion, installExtensionTaskOptions.profileLocation); const installed = await this.getInstalled(undefined, installExtensionTaskOptions.profileLocation); + const options: InstallExtensionTaskOptions = { ...installExtensionTaskOptions, donotIncludePackAndDependencies: true, context: { ...installExtensionTaskOptions.context, [EXTENSION_INSTALL_DEP_PACK_CONTEXT]: true } }; for (const { gallery, manifest } of distinct(allDepsAndPackExtensionsToInstall, ({ gallery }) => gallery.identifier.id)) { installExtensionHasDependents = installExtensionHasDependents || !!manifest.extensionDependencies?.some(id => areSameExtensions({ id }, installExtensionTask.identifier)); const key = getInstallExtensionTaskKey(gallery); @@ -279,7 +280,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl })); } } else if (!installed.some(({ identifier }) => areSameExtensions(identifier, gallery.identifier))) { - const task = this.createInstallExtensionTask(manifest, gallery, { ...installExtensionTaskOptions, donotIncludePackAndDependencies: true }); + const task = this.createInstallExtensionTask(manifest, gallery, options); this.installingExtensions.set(key, { task, waitingTasks: [installExtensionTask] }); this._onInstallExtension.fire({ identifier: task.identifier, source: gallery, profileLocation: installExtensionTaskOptions.profileLocation }); this.logService.info('Installing extension:', task.identifier.id, installExtensionTask.identifier.id); @@ -343,12 +344,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl } } - const context = installExtensionTaskOptions.context ?? {}; - if (hasPackExtensions && task.identifier.id !== installExtensionTask.identifier.id) { - context[EXTENSION_INSTALL_SKIP_WALKTHROUGH_CONTEXT] = true; - } - - installResults.push({ local, identifier: task.identifier, operation: task.operation, source: task.source, context: context, profileLocation: task.profileLocation, applicationScoped: local.isApplicationScoped }); + installResults.push({ local, identifier: task.identifier, operation: task.operation, source: task.source, context: task.options.context, profileLocation: task.profileLocation, applicationScoped: local.isApplicationScoped }); } catch (error) { if (!URI.isUri(task.source)) { reportTelemetry(this.telemetryService, task.operation === InstallOperation.Update ? 'extensionGallery:update' : 'extensionGallery:install', { diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index 9a9abb281fc..dc4c1ccc02c 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -18,6 +18,7 @@ export const EXTENSION_IDENTIFIER_REGEX = new RegExp(EXTENSION_IDENTIFIER_PATTER export const WEB_EXTENSION_TAG = '__web_extension'; export const EXTENSION_INSTALL_SKIP_WALKTHROUGH_CONTEXT = 'skipWalkthrough'; export const EXTENSION_INSTALL_SYNC_CONTEXT = 'extensionsSync'; +export const EXTENSION_INSTALL_DEP_PACK_CONTEXT = 'dependecyOrPackExtensionInstall'; export function TargetPlatformToString(targetPlatform: TargetPlatform) { switch (targetPlatform) { diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 6c7b92de0ef..677149f937b 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -782,7 +782,7 @@ abstract class InstallExtensionTask extends AbstractExtensionTask { const hadLastFoucs = await this.hostService.hadLastFocus(); for (const e of result) { + const skipWalkthrough = e?.context?.[EXTENSION_INSTALL_SKIP_WALKTHROUGH_CONTEXT] || e?.context?.[EXTENSION_INSTALL_DEP_PACK_CONTEXT]; // If the window had last focus and the install didn't specify to skip the walkthrough // Then add it to the sessionInstallExtensions to be opened - if (hadLastFoucs && !e?.context?.[EXTENSION_INSTALL_SKIP_WALKTHROUGH_CONTEXT]) { + if (hadLastFoucs && !skipWalkthrough) { this.sessionInstalledExtensions.add(e.identifier.id.toLowerCase()); } this.progressByEvent(`extensionInstalled:${e.identifier.id.toLowerCase()}`); diff --git a/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts index f100b353488..fcff4bbe95c 100644 --- a/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts @@ -251,7 +251,7 @@ class InstallExtensionTask extends AbstractExtensionTask implem constructor( manifest: IExtensionManifest, private readonly extension: URI | IGalleryExtension, - private readonly options: InstallExtensionTaskOptions, + readonly options: InstallExtensionTaskOptions, private readonly webExtensionsScannerService: IWebExtensionsScannerService, private readonly userDataProfilesService: IUserDataProfilesService, ) { From d5f5ad201dcd9f8568bc2fd35cf48c034385aa55 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 22 Aug 2023 17:34:59 +0200 Subject: [PATCH 194/223] Joins consecutive moves that are close together --- src/vs/editor/common/core/lineRange.ts | 8 ++ src/vs/editor/common/core/offsetRange.ts | 12 ++ .../editor/common/diff/linesDiffComputer.ts | 7 ++ .../common/diff/standardLinesDiffComputer.ts | 118 ++++++++++++++---- 4 files changed, 122 insertions(+), 23 deletions(-) diff --git a/src/vs/editor/common/core/lineRange.ts b/src/vs/editor/common/core/lineRange.ts index 568bf03ca2b..73b0f7e5761 100644 --- a/src/vs/editor/common/core/lineRange.ts +++ b/src/vs/editor/common/core/lineRange.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { BugIndicatingError } from 'vs/base/common/errors'; +import { OffsetRange } from 'vs/editor/common/core/offsetRange'; import { Range } from 'vs/editor/common/core/range'; /** @@ -239,6 +240,13 @@ export class LineRange { public includes(lineNumber: number): boolean { return this.startLineNumber <= lineNumber && lineNumber < this.endLineNumberExclusive; } + + /** + * Converts this 1-based line range to a 0-based offset range (subtracts 1!). + */ + public toOffsetRange(): OffsetRange { + return new OffsetRange(this.startLineNumber - 1, this.endLineNumberExclusive - 1); + } } export type ISerializedLineRange = [startLineNumber: number, endLineNumberExclusive: number]; diff --git a/src/vs/editor/common/core/offsetRange.ts b/src/vs/editor/common/core/offsetRange.ts index d46cf88a425..d4129b1c532 100644 --- a/src/vs/editor/common/core/offsetRange.ts +++ b/src/vs/editor/common/core/offsetRange.ts @@ -48,6 +48,14 @@ export class OffsetRange { return new OffsetRange(this.start + offset, this.endExclusive + offset); } + public deltaStart(offset: number): OffsetRange { + return new OffsetRange(this.start + offset, this.endExclusive); + } + + public deltaEnd(offset: number): OffsetRange { + return new OffsetRange(this.start, this.endExclusive + offset); + } + public get length(): number { return this.endExclusive - this.start; } @@ -90,6 +98,10 @@ export class OffsetRange { } return undefined; } + + public slice(arr: T[]): T[] { + return arr.slice(this.start, this.endExclusive); + } } export class OffsetRangeSet { diff --git a/src/vs/editor/common/diff/linesDiffComputer.ts b/src/vs/editor/common/diff/linesDiffComputer.ts index 84505ab563a..d10888cb93f 100644 --- a/src/vs/editor/common/diff/linesDiffComputer.ts +++ b/src/vs/editor/common/diff/linesDiffComputer.ts @@ -155,6 +155,13 @@ export class SimpleLineRangeMapping { public flip(): SimpleLineRangeMapping { return new SimpleLineRangeMapping(this.modified, this.original); } + + public join(other: SimpleLineRangeMapping): SimpleLineRangeMapping { + return new SimpleLineRangeMapping( + this.original.join(other.original), + this.modified.join(other.modified), + ); + } } export class MovedText { diff --git a/src/vs/editor/common/diff/standardLinesDiffComputer.ts b/src/vs/editor/common/diff/standardLinesDiffComputer.ts index b9ef58b85c2..20279a9fe68 100644 --- a/src/vs/editor/common/diff/standardLinesDiffComputer.ts +++ b/src/vs/editor/common/diff/standardLinesDiffComputer.ts @@ -177,7 +177,7 @@ export class StandardLinesDiffComputer implements ILinesDiffComputer { } private computeMoves(changes: LineRangeMapping[], originalLines: string[], modifiedLines: string[], hashedOriginalLines: number[], hashedModifiedLines: number[], timeout: ITimeout, considerWhitespaceChanges: boolean): MovedText[] { - const moves: MovedText[] = []; + const moves: SimpleLineRangeMapping[] = []; const deletions = changes .filter(c => c.modifiedRange.isEmpty && c.originalRange.length >= 3) .map(d => new LineRangeFragment(d.originalRange, originalLines, d)); @@ -199,17 +199,15 @@ export class StandardLinesDiffComputer implements ILinesDiffComputer { } if (highestSimilarity > 0.90 && best) { - const moveChanges = this.refineDiff(originalLines, modifiedLines, new SequenceDiff( - new OffsetRange(deletion.range.startLineNumber - 1, deletion.range.endLineNumberExclusive - 1), - new OffsetRange(best.range.startLineNumber - 1, best.range.endLineNumberExclusive - 1) - ), timeout, considerWhitespaceChanges); - const mappings = lineRangeMappingFromRangeMappings(moveChanges.mappings, originalLines, modifiedLines, true); - insertions.delete(best); - moves.push(new MovedText(new SimpleLineRangeMapping(deletion.range, best.range), mappings)); + moves.push(new SimpleLineRangeMapping(deletion.range, best.range)); excludedChanges.add(deletion.source); excludedChanges.add(best.source); } + + if (!timeout.isValid()) { + return []; + } } const original3LineHashes = new SetMap(); @@ -242,9 +240,6 @@ export class StandardLinesDiffComputer implements ILinesDiffComputer { let lastMappings: PossibleMapping[] = []; for (let i = change.modifiedRange.startLineNumber; i < change.modifiedRange.endLineNumberExclusive - 2; i++) { const key = `${hashedModifiedLines[i - 1]}:${hashedModifiedLines[i + 1 - 1]}:${hashedModifiedLines[i + 2 - 1]}`; - - //const isWeakKey = (originalLines[i].trim().length + originalLines[i + 1].trim().length + originalLines[i + 2].trim().length < 20); - const currentModifiedRange = new LineRange(i, i + 3); const nextMappings: PossibleMapping[] = []; @@ -269,6 +264,10 @@ export class StandardLinesDiffComputer implements ILinesDiffComputer { }); lastMappings = nextMappings; } + + if (!timeout.isValid()) { + return []; + } } possibleMappings.sort(reverseOrder(compareBy(m => m.modifiedLineRange.length, numberComparator))); @@ -291,19 +290,49 @@ export class StandardLinesDiffComputer implements ILinesDiffComputer { const modifiedLineRange = s; const originalLineRange = s.delta(-diffOrigToMod); - const moveChanges = this.refineDiff(originalLines, modifiedLines, new SequenceDiff( - new OffsetRange(originalLineRange.startLineNumber - 1, originalLineRange.endLineNumberExclusive - 1), - new OffsetRange(modifiedLineRange.startLineNumber - 1, modifiedLineRange.endLineNumberExclusive - 1) - ), timeout, considerWhitespaceChanges); - const mappings = lineRangeMappingFromRangeMappings(moveChanges.mappings, originalLines, modifiedLines, true); - moves.push(new MovedText(new SimpleLineRangeMapping(originalLineRange, modifiedLineRange), mappings)); + moves.push(new SimpleLineRangeMapping(originalLineRange, modifiedLineRange)); modifiedSet.addRange(modifiedLineRange); originalSet.addRange(originalLineRange); } } - return moves; + // join moves + moves.sort(compareBy(m => m.original.startLineNumber, numberComparator)); + if (moves.length === 0) { + return []; + } + const joinedMoves = [moves[0]]; + for (let i = 1; i < moves.length; i++) { + const last = joinedMoves[joinedMoves.length - 1]; + const current = moves[i]; + + const originalDist = current.original.startLineNumber - last.original.endLineNumberExclusive; + const modifiedDist = current.modified.startLineNumber - last.modified.endLineNumberExclusive; + const currentMoveAfterLast = originalDist >= 0 && modifiedDist >= 0; + + if (currentMoveAfterLast && originalDist <= 1 && modifiedDist <= 1) { + joinedMoves[joinedMoves.length - 1] = last.join(current); + continue; + } + + const originalText = current.original.toOffsetRange().slice(originalLines).map(l => l.trim()).join('\n'); + if (originalText.length <= 10) { + // Ignore small moves + continue; + } + joinedMoves.push(current); + } + + const fullMoves = joinedMoves.map(m => { + const moveChanges = this.refineDiff(originalLines, modifiedLines, new SequenceDiff( + m.original.toOffsetRange(), + m.modified.toOffsetRange(), + ), timeout, considerWhitespaceChanges); + const mappings = lineRangeMappingFromRangeMappings(moveChanges.mappings, originalLines, modifiedLines, true); + return new MovedText(m, mappings); + }); + return fullMoves; } private refineDiff(originalLines: string[], modifiedLines: string[], diff: SequenceDiff, timeout: ITimeout, considerWhitespaceChanges: boolean): { mappings: RangeMapping[]; hitTimeout: boolean } { @@ -659,7 +688,7 @@ export class LinesSliceCharSequence implements ISequence { private readonly firstCharOffsetByLineMinusOne: number[] = []; public readonly lineRange: OffsetRange; // To account for trimming - private readonly offsetByLine: number[] = []; + private readonly additionalOffsetByLine: number[] = []; constructor(public readonly lines: string[], lineRange: OffsetRange, public readonly considerWhitespaceChanges: boolean) { // This slice has to have lineRange.length many \n! (otherwise diffing against an empty slice will be problematic) @@ -687,7 +716,7 @@ export class LinesSliceCharSequence implements ISequence { line = trimmedStartLine.trimEnd(); } - this.offsetByLine.push(offset); + this.additionalOffsetByLine.push(offset); for (let i = 0; i < line.length; i++) { this.elements.push(line.charCodeAt(i)); @@ -700,7 +729,7 @@ export class LinesSliceCharSequence implements ISequence { } } // To account for the last line - this.offsetByLine.push(0); + this.additionalOffsetByLine.push(0); } toString() { @@ -767,7 +796,7 @@ export class LinesSliceCharSequence implements ISequence { } const offsetOfFirstCharInLine = i === 0 ? 0 : this.firstCharOffsetByLineMinusOne[i - 1]; - return new Position(this.lineRange.start + i + 1, offset - offsetOfFirstCharInLine + 1 + this.offsetByLine[i]); + return new Position(this.lineRange.start + i + 1, offset - offsetOfFirstCharInLine + 1 + this.additionalOffsetByLine[i]); } public translateRange(range: OffsetRange): Range { @@ -805,9 +834,52 @@ export class LinesSliceCharSequence implements ISequence { return this.translateOffset(range.endExclusive).lineNumber - this.translateOffset(range.start).lineNumber; } - isStronglyEqual(offset1: number, offset2: number): boolean { + public isStronglyEqual(offset1: number, offset2: number): boolean { return this.elements[offset1] === this.elements[offset2]; } + + public extendToFullLines(range: OffsetRange): OffsetRange { + const firstIdx = findLastIdxMonotonous(this.firstCharOffsetByLineMinusOne, x => x <= range.start); + const lastIdx = findFirstIdxMonotonous(this.firstCharOffsetByLineMinusOne, x => range.endExclusive <= x); + + const start = firstIdx === -1 ? 0 : this.firstCharOffsetByLineMinusOne[firstIdx]; + const end = lastIdx === this.firstCharOffsetByLineMinusOne.length ? this.elements.length : this.firstCharOffsetByLineMinusOne[lastIdx]; + return new OffsetRange(start, end); + } +} + +/** + * @returns -1 if predicate is false for all items + */ +function findLastIdxMonotonous(arr: T[], predicate: (item: T) => boolean): number { + let i = 0; + let j = arr.length; + while (i < j) { + const k = Math.floor((i + j) / 2); + if (predicate(arr[k])) { + i = k + 1; + } else { + j = k; + } + } + return i - 1; +} + +/** + * @returns arr.length if predicate is false for all items + */ +function findFirstIdxMonotonous(arr: T[], predicate: (item: T) => boolean): number { + let i = 0; + let j = arr.length; + while (i < j) { + const k = Math.floor((i + j) / 2); + if (predicate(arr[k])) { + j = k; + } else { + i = k + 1; + } + } + return i; } function isWordChar(charCode: number): boolean { From 44f9326a27b58188dc51ee925ecdaa75ff3d6559 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 22 Aug 2023 17:53:57 +0200 Subject: [PATCH 195/223] Fixes CI --- src/vs/editor/common/core/lineRange.ts | 1 + src/vs/monaco.d.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/vs/editor/common/core/lineRange.ts b/src/vs/editor/common/core/lineRange.ts index 73b0f7e5761..70acb0476d6 100644 --- a/src/vs/editor/common/core/lineRange.ts +++ b/src/vs/editor/common/core/lineRange.ts @@ -243,6 +243,7 @@ export class LineRange { /** * Converts this 1-based line range to a 0-based offset range (subtracts 1!). + * @internal */ public toOffsetRange(): OffsetRange { return new OffsetRange(this.startLineNumber - 1, this.endLineNumberExclusive - 1); diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index e6cd4654946..c6b4adf801f 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -2546,6 +2546,7 @@ declare namespace monaco.editor { constructor(original: LineRange, modified: LineRange); toString(): string; flip(): SimpleLineRangeMapping; + join(other: SimpleLineRangeMapping): SimpleLineRangeMapping; } export interface IDimension { width: number; From 94e1fa6de86916a8235f6280a43809d90fc38422 Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 22 Aug 2023 11:40:44 -0700 Subject: [PATCH 196/223] Fix #190968. css variable fallback. --- src/vs/editor/browser/widget/media/diffEditor.css | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/browser/widget/media/diffEditor.css b/src/vs/editor/browser/widget/media/diffEditor.css index 66fa2b5ab83..6d086fe1031 100644 --- a/src/vs/editor/browser/widget/media/diffEditor.css +++ b/src/vs/editor/browser/widget/media/diffEditor.css @@ -81,7 +81,7 @@ } .monaco-editor .line-insert, .monaco-diff-editor .line-insert { - background-color: var(--vscode-diffEditor-insertedLineBackground, --vscode-diffEditor-insertedTextBackground); + background-color: var(--vscode-diffEditor-insertedLineBackground, var(--vscode-diffEditor-insertedTextBackground)); } .monaco-editor .line-insert, @@ -106,7 +106,7 @@ .monaco-editor .inline-added-margin-view-zone, .monaco-editor .gutter-insert, .monaco-diff-editor .gutter-insert { - background-color: var(--vscode-diffEditorGutter-insertedLineBackground, --vscode-diffEditor-insertedLineBackground, --vscode-diffEditor-insertedTextBackground); + background-color: var(--vscode-diffEditorGutter-insertedLineBackground, var(--vscode-diffEditor-insertedLineBackground), var(--vscode-diffEditor-insertedTextBackground)); } .monaco-editor .char-delete, .monaco-diff-editor .char-delete { @@ -114,12 +114,12 @@ } .monaco-editor .line-delete, .monaco-diff-editor .line-delete { - background-color: var(--vscode-diffEditor-removedLineBackground, --vscode-diffEditor-removedTextBackground); + background-color: var(--vscode-diffEditor-removedLineBackground, var(--vscode-diffEditor-removedTextBackground)); } .monaco-editor .inline-deleted-margin-view-zone, .monaco-editor .gutter-delete, .monaco-diff-editor .gutter-delete { - background-color: var(--vscode-diffEditorGutter-removedLineBackground, --vscode-diffEditor-removedLineBackground, --vscode-diffEditor-removedTextBackground); + background-color: var(--vscode-diffEditorGutter-removedLineBackground, var(--vscode-diffEditor-removedLineBackground), var(--vscode-diffEditor-removedTextBackground)); } .monaco-diff-editor.side-by-side .editor.modified { From 6d8b0c45e766114d0efa47a1d5e62a2dec2aa6b7 Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Tue, 22 Aug 2023 18:51:19 +0000 Subject: [PATCH 197/223] Doc review --- src/vscode-dts/vscode.proposed.envCollectionWorkspace.d.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vscode-dts/vscode.proposed.envCollectionWorkspace.d.ts b/src/vscode-dts/vscode.proposed.envCollectionWorkspace.d.ts index 43f77834b64..fb4b78e172b 100644 --- a/src/vscode-dts/vscode.proposed.envCollectionWorkspace.d.ts +++ b/src/vscode-dts/vscode.proposed.envCollectionWorkspace.d.ts @@ -25,6 +25,10 @@ declare module 'vscode' { * including the global collection. * * @param scope The scope to which the environment variable collection applies to. + * + * If a scope parameter is omitted, collection applicable to all relevant scopes for that parameter is + * returned. For instance, if the 'workspaceFolder' parameter is not specified, the collection that applies + * across all workspace folders will be returned. */ getScoped(scope: EnvironmentVariableScope): EnvironmentVariableCollection; } From 6a6dc14d4feb6bded3893c7a5f665800aa7bf19a Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Tue, 22 Aug 2023 18:55:42 +0000 Subject: [PATCH 198/223] Only one new line character in case there is no description --- .../browser/terminal.environmentChanges.contribution.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/environmentChanges/browser/terminal.environmentChanges.contribution.ts b/src/vs/workbench/contrib/terminalContrib/environmentChanges/browser/terminal.environmentChanges.contribution.ts index ed59231bb57..e4bcaf9b0c0 100644 --- a/src/vs/workbench/contrib/terminalContrib/environmentChanges/browser/terminal.environmentChanges.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/environmentChanges/browser/terminal.environmentChanges.contribution.ts @@ -55,15 +55,14 @@ function describeEnvironmentChanges(collection: IMergedEnvironmentVariableCollec content += '\n'; const globalDescription = globalDescriptions.get(ext); if (globalDescription) { - content += `\n${globalDescription}`; + content += `\n${globalDescription}\n`; } const workspaceDescription = workspaceDescriptions.get(ext); if (workspaceDescription) { // Only show '(workspace)' suffix if there is already a description for the extension. const workspaceSuffix = globalDescription ? ` (${localize('ScopedEnvironmentContributionInfo', 'workspace')})` : ''; - content += `\n${workspaceDescription}${workspaceSuffix}`; + content += `\n${workspaceDescription}${workspaceSuffix}\n`; } - content += '\n'; for (const mutator of coll.map.values()) { if (filterScope(mutator, scope) === false) { From d34d715248d5b107af6d2f98fc560a46637a89cf Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 22 Aug 2023 11:57:36 -0700 Subject: [PATCH 199/223] wip --- .../terminal.accessibility.contribution.ts | 2 +- .../browser/textAreaSyncAddon.ts | 43 +++++++++++++++---- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts index b69943c18de..3594567f2b1 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts @@ -40,7 +40,7 @@ class TextAreaSyncContribution extends DisposableStore implements ITerminalContr super(); } xtermReady(xterm: IXtermTerminal & { raw: Terminal }): void { - const addon = this._instantiationService.createInstance(TextAreaSyncAddon, this._instance.capabilities); + const addon = this._instantiationService.createInstance(TextAreaSyncAddon, this._instance.capabilities, this._instance.onDidFocus); xterm.raw.loadAddon(addon); addon.activate(xterm.raw); } diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts index 8ed986db3cf..9a52fc9c00a 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts @@ -8,6 +8,8 @@ import { IAccessibilityService } from 'vs/platform/accessibility/common/accessib import { ITerminalCapabilityStore, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; import { ITerminalLogService } from 'vs/platform/terminal/common/terminal'; import type { Terminal, ITerminalAddon } from 'xterm'; +import { Event } from 'vs/base/common/event'; +import { ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; export interface ITextAreaData { content: string; @@ -17,30 +19,47 @@ export interface ITextAreaData { export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { private _terminal: Terminal | undefined; private _onCursorMoveListener = this._register(new MutableDisposable()); + private _onDidFocusListener = this._register(new MutableDisposable()); + private _onKeyListener = this._register(new MutableDisposable()); activate(terminal: Terminal): void { this._terminal = terminal; if (this._accessibilityService.isScreenReaderOptimized()) { - this._onCursorMoveListener.value = this._terminal.onCursorMove(() => this._refreshTextArea()); + this._setListeners(); } } constructor( private readonly _capabilities: ITerminalCapabilityStore, + private readonly _onDidFocus: Event, @IAccessibilityService private readonly _accessibilityService: IAccessibilityService, @ITerminalLogService private readonly _logService: ITerminalLogService ) { super(); this._register(this._accessibilityService.onDidChangeScreenReaderOptimized(() => { - if (this._accessibilityService.isScreenReaderOptimized() && this._terminal) { + if (this._accessibilityService.isScreenReaderOptimized()) { this._refreshTextArea(); - this._onCursorMoveListener.value = this._terminal.onCursorMove(() => this._refreshTextArea()); + this._setListeners(); } else { this._onCursorMoveListener.clear(); + this._onDidFocusListener.clear(); + this._onKeyListener.clear(); } })); } - private _refreshTextArea(focusChanged?: boolean): void { + private _setListeners(): void { + if (this._accessibilityService.isScreenReaderOptimized() && this._terminal) { + this._onCursorMoveListener.value = this._terminal.onCursorMove(() => this._refreshTextArea()); + this._onDidFocusListener.value = this._onDidFocus(() => this._refreshTextArea()); + this._onKeyListener.value = this._terminal.onKey((e) => { + if (e.domEvent.key === 'UpArrow') { + this._refreshTextArea(); + } + }); + } + } + + private _refreshTextArea(): void { if (!this._terminal) { return; } @@ -59,18 +78,24 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { return; } let content: string | undefined; - if (currentCommand.commandStartX) { + if (currentCommand.commandStartX !== undefined) { // Left prompt content = line.substring(currentCommand.commandStartX); commandStartX = currentCommand.commandStartX; - } else if (currentCommand.commandRightPromptStartX) { + } else if (currentCommand.commandRightPromptStartX !== undefined) { // Right prompt content = line.substring(0, currentCommand.commandRightPromptStartX); commandStartX = 0; + } else { + this._logService.debug(`TextAreaSyncAddon#refreshTextArea: no commandStartX or commandRightPromptStartX`); } if (!content) { this._logService.debug(`TextAreaSyncAddon#refreshTextArea: no content`); + const textArea = this._terminal.textarea; + if (textArea) { + textArea.textContent = ''; + } return; } @@ -87,15 +112,15 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { this._logService.debug(`TextAreaSyncAddon#refreshTextArea: content is "${content}"`); this._logService.debug(`TextAreaSyncAddon#refreshTextArea: textContent is "${textArea.textContent}"`); - if (focusChanged || content !== textArea.textContent) { - textArea.textContent = content; + if (content !== textArea.textContent) { + textArea.textContent = content.trim(); this._logService.debug(`TextAreaSyncAddon#refreshTextArea: textContent changed to "${content}"`); } const cursorX = buffer.cursorX - commandStartX; this._logService.debug(`TextAreaSyncAddon#refreshTextArea: cursorX is ${cursorX}`); this._logService.debug(`TextAreaSyncAddon#refreshTextArea: selectionStart is ${textArea.selectionStart}`); - if (focusChanged || cursorX !== textArea.selectionStart) { + if (cursorX !== textArea.selectionStart) { textArea.selectionStart = cursorX; textArea.selectionEnd = cursorX; this._logService.debug(`TextAreaSyncAddon#refreshTextArea: selectionStart changed to ${cursorX}`); From f058931630bec023da5ea0ae633262f9ebe50703 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Tue, 22 Aug 2023 12:22:53 -0700 Subject: [PATCH 200/223] RelatedInformation & EmbeddingVector API (#191006) --- .../api/browser/extensionHost.contribution.ts | 2 + .../browser/mainThreadAiEmbeddingVector.ts | 41 +++++++ .../browser/mainThreadAiRelatedInformation.ts | 44 +++++++ .../workbench/api/common/extHost.api.impl.ts | 19 ++- .../workbench/api/common/extHost.protocol.ts | 24 ++++ .../api/common/extHostAiRelatedInformation.ts | 50 ++++++++ .../api/common/extHostEmbeddingVector.ts | 50 ++++++++ src/vs/workbench/api/common/extHostTypes.ts | 11 ++ .../browser/commandsQuickAccess.ts | 88 ++++++++++---- .../common/aiEmbeddingVectorService.ts | 114 ++++++++++++++++++ .../common/aiRelatedInformation.ts | 39 ++++++ .../common/aiRelatedInformationService.ts | 114 ++++++++++++++++++ .../common/extensionsApiProposals.ts | 1 + src/vs/workbench/workbench.common.main.ts | 2 + .../vscode.proposed.aiRelatedInformation.d.ts | 62 ++++++++++ 15 files changed, 638 insertions(+), 23 deletions(-) create mode 100644 src/vs/workbench/api/browser/mainThreadAiEmbeddingVector.ts create mode 100644 src/vs/workbench/api/browser/mainThreadAiRelatedInformation.ts create mode 100644 src/vs/workbench/api/common/extHostAiRelatedInformation.ts create mode 100644 src/vs/workbench/api/common/extHostEmbeddingVector.ts create mode 100644 src/vs/workbench/services/aiEmbeddingVector/common/aiEmbeddingVectorService.ts create mode 100644 src/vs/workbench/services/aiRelatedInformation/common/aiRelatedInformation.ts create mode 100644 src/vs/workbench/services/aiRelatedInformation/common/aiRelatedInformationService.ts create mode 100644 src/vscode-dts/vscode.proposed.aiRelatedInformation.d.ts diff --git a/src/vs/workbench/api/browser/extensionHost.contribution.ts b/src/vs/workbench/api/browser/extensionHost.contribution.ts index 774cc0cc158..8a71fe97ce1 100644 --- a/src/vs/workbench/api/browser/extensionHost.contribution.ts +++ b/src/vs/workbench/api/browser/extensionHost.contribution.ts @@ -88,6 +88,8 @@ import './mainThreadSecretState'; import './mainThreadShare'; import './mainThreadProfilContentHandlers'; import './mainThreadSemanticSimilarity'; +import './mainThreadAiRelatedInformation'; +import './mainThreadAiEmbeddingVector'; import './mainThreadIssueReporter'; export class ExtensionPoints implements IWorkbenchContribution { diff --git a/src/vs/workbench/api/browser/mainThreadAiEmbeddingVector.ts b/src/vs/workbench/api/browser/mainThreadAiEmbeddingVector.ts new file mode 100644 index 00000000000..954679e2c52 --- /dev/null +++ b/src/vs/workbench/api/browser/mainThreadAiEmbeddingVector.ts @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CancellationToken } from 'vs/base/common/cancellation'; +import { Disposable, DisposableMap } from 'vs/base/common/lifecycle'; +import { ExtHostAiEmbeddingVectorShape, ExtHostContext, MainContext, MainThreadAiEmbeddingVectorShape } from 'vs/workbench/api/common/extHost.protocol'; +import { IAiEmbeddingVectorProvider, IAiEmbeddingVectorService } from 'vs/workbench/services/aiEmbeddingVector/common/aiEmbeddingVectorService'; +import { IExtHostContext, extHostNamedCustomer } from 'vs/workbench/services/extensions/common/extHostCustomers'; + +@extHostNamedCustomer(MainContext.MainThreadAiEmbeddingVector) +export class MainThreadAiEmbeddingVector extends Disposable implements MainThreadAiEmbeddingVectorShape { + private readonly _proxy: ExtHostAiEmbeddingVectorShape; + private readonly _registrations = this._register(new DisposableMap()); + + constructor( + context: IExtHostContext, + @IAiEmbeddingVectorService private readonly _AiEmbeddingVectorService: IAiEmbeddingVectorService, + ) { + super(); + this._proxy = context.getProxy(ExtHostContext.ExtHostAiEmbeddingVector); + } + + $registerAiEmbeddingVectorProvider(model: string, handle: number): void { + const provider: IAiEmbeddingVectorProvider = { + provideAiEmbeddingVector: (strings: string[], token: CancellationToken) => { + return this._proxy.$provideAiEmbeddingVector( + handle, + strings, + token + ); + }, + }; + this._registrations.set(handle, this._AiEmbeddingVectorService.registerAiEmbeddingVectorProvider(model, provider)); + } + + $unregisterAiEmbeddingVectorProvider(handle: number): void { + this._registrations.deleteAndDispose(handle); + } +} diff --git a/src/vs/workbench/api/browser/mainThreadAiRelatedInformation.ts b/src/vs/workbench/api/browser/mainThreadAiRelatedInformation.ts new file mode 100644 index 00000000000..b198f41be47 --- /dev/null +++ b/src/vs/workbench/api/browser/mainThreadAiRelatedInformation.ts @@ -0,0 +1,44 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CancellationToken } from 'vs/base/common/cancellation'; +import { Disposable, DisposableMap } from 'vs/base/common/lifecycle'; +import { ExtHostAiRelatedInformationShape, ExtHostContext, MainContext, MainThreadAiRelatedInformationShape } from 'vs/workbench/api/common/extHost.protocol'; +import { RelatedInformationType } from 'vs/workbench/api/common/extHostTypes'; +import { IAiRelatedInformationProvider, IAiRelatedInformationService } from 'vs/workbench/services/aiRelatedInformation/common/aiRelatedInformation'; +import { IExtHostContext, extHostNamedCustomer } from 'vs/workbench/services/extensions/common/extHostCustomers'; +import { RelatedInformationResult } from 'vscode'; + +@extHostNamedCustomer(MainContext.MainThreadAiRelatedInformation) +export class MainThreadAiRelatedInformation extends Disposable implements MainThreadAiRelatedInformationShape { + private readonly _proxy: ExtHostAiRelatedInformationShape; + private readonly _registrations = this._register(new DisposableMap()); + + constructor( + context: IExtHostContext, + @IAiRelatedInformationService private readonly _aiRelatedInformationService: IAiRelatedInformationService, + ) { + super(); + this._proxy = context.getProxy(ExtHostContext.ExtHostAiRelatedInformation); + } + + $getAiRelatedInformation(query: string, types: RelatedInformationType[]): Promise { + // TODO: use a real cancellation token + return this._aiRelatedInformationService.getRelatedInformation(query, types, CancellationToken.None); + } + + $registerAiRelatedInformationProvider(handle: number, types: RelatedInformationType[]): void { + const provider: IAiRelatedInformationProvider = { + provideAiRelatedInformation: (query, types, token) => { + return this._proxy.$provideAiRelatedInformation(handle, query, types, token); + }, + }; + this._registrations.set(handle, this._aiRelatedInformationService.registerAiRelatedInformationProvider(types, provider)); + } + + $unregisterAiRelatedInformationProvider(handle: number): void { + this._registrations.deleteAndDispose(handle); + } +} diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 291edaf4e33..b3656684769 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -107,6 +107,8 @@ import { ExtHostShare } from 'vs/workbench/api/common/extHostShare'; import { ExtHostChatProvider } from 'vs/workbench/api/common/extHostChatProvider'; import { ExtHostChatSlashCommands } from 'vs/workbench/api/common/extHostChatSlashCommand'; import { ExtHostChatVariables } from 'vs/workbench/api/common/extHostChatVariables'; +import { ExtHostRelatedInformation } from 'vs/workbench/api/common/extHostAiRelatedInformation'; +import { ExtHostAiEmbeddingVector } from 'vs/workbench/api/common/extHostEmbeddingVector'; export interface IExtensionRegistries { mine: ExtensionDescriptionRegistry; @@ -211,6 +213,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostChatVariables = rpcProtocol.set(ExtHostContext.ExtHostChatVariables, new ExtHostChatVariables(rpcProtocol)); const extHostChat = rpcProtocol.set(ExtHostContext.ExtHostChat, new ExtHostChat(rpcProtocol, extHostLogService)); const extHostSemanticSimilarity = rpcProtocol.set(ExtHostContext.ExtHostSemanticSimilarity, new ExtHostSemanticSimilarity(rpcProtocol)); + const extHostAiRelatedInformation = rpcProtocol.set(ExtHostContext.ExtHostAiRelatedInformation, new ExtHostRelatedInformation(rpcProtocol)); + const extHostAiEmbeddingVector = rpcProtocol.set(ExtHostContext.ExtHostAiEmbeddingVector, new ExtHostAiEmbeddingVector(rpcProtocol)); const extHostIssueReporter = rpcProtocol.set(ExtHostContext.ExtHostIssueReporter, new ExtHostIssueReporter(rpcProtocol)); const extHostStatusBar = rpcProtocol.set(ExtHostContext.ExtHostStatusBar, new ExtHostStatusBar(rpcProtocol, extHostCommands.converter)); @@ -1323,6 +1327,18 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I registerSemanticSimilarityProvider(provider: vscode.SemanticSimilarityProvider) { checkProposedApiEnabled(extension, 'semanticSimilarity'); return extHostSemanticSimilarity.registerSemanticSimilarityProvider(extension, provider); + }, + getRelatedInformation(query: string, types: vscode.RelatedInformationType[]): Thenable { + checkProposedApiEnabled(extension, 'aiRelatedInformation'); + return extHostAiRelatedInformation.getRelatedInformation(extension, query, types); + }, + registerRelatedInformationProvider(types: vscode.RelatedInformationType[], provider: vscode.RelatedInformationProvider) { + checkProposedApiEnabled(extension, 'aiRelatedInformation'); + return extHostAiRelatedInformation.registerRelatedInformationProvider(extension, types, provider); + }, + registerEmbeddingVectorProvider(model: string, provider: vscode.EmbeddingVectorProvider) { + checkProposedApiEnabled(extension, 'aiRelatedInformation'); + return extHostAiEmbeddingVector.registerEmbeddingVectorProvider(extension, model, provider); } }; @@ -1560,7 +1576,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I InteractiveEditorResponseFeedbackKind: extHostTypes.InteractiveEditorResponseFeedbackKind, StackFrameFocus: extHostTypes.StackFrameFocus, ThreadFocus: extHostTypes.ThreadFocus, - NotebookCodeActionKind: extHostTypes.NotebookCodeActionKind + NotebookCodeActionKind: extHostTypes.NotebookCodeActionKind, + RelatedInformationType: extHostTypes.RelatedInformationType }; }; } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 6886d86e0a9..3ed42880b48 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -76,6 +76,7 @@ import { ISaveProfileResult } from 'vs/workbench/services/userDataProfile/common import { IChatMessage, IChatResponseFragment, IChatResponseProviderMetadata } from 'vs/workbench/contrib/chat/common/chatProvider'; import { IChatSlashFragment } from 'vs/workbench/contrib/chat/common/chatSlashCommands'; import { IChatRequestVariableValue, IChatVariableData } from 'vs/workbench/contrib/chat/common/chatVariables'; +import { RelatedInformationResult, RelatedInformationType } from 'vs/workbench/services/aiRelatedInformation/common/aiRelatedInformation'; export interface IWorkspaceData extends IStaticWorkspaceData { folders: { uri: UriComponents; name: string; index: number }[]; @@ -1662,6 +1663,25 @@ export interface MainThreadSemanticSimilarityShape extends IDisposable { $unregisterSemanticSimilarityProvider(handle: number): void; } +export interface ExtHostAiRelatedInformationShape { + $provideAiRelatedInformation(handle: number, query: string, types: RelatedInformationType[], token: CancellationToken): Promise; +} + +export interface MainThreadAiRelatedInformationShape { + $getAiRelatedInformation(query: string, types: RelatedInformationType[]): Promise; + $registerAiRelatedInformationProvider(handle: number, types: RelatedInformationType[]): void; + $unregisterAiRelatedInformationProvider(handle: number): void; +} + +export interface ExtHostAiEmbeddingVectorShape { + $provideAiEmbeddingVector(handle: number, strings: string[], token: CancellationToken): Promise; +} + +export interface MainThreadAiEmbeddingVectorShape { + $registerAiEmbeddingVectorProvider(model: string, handle: number): void; + $unregisterAiEmbeddingVectorProvider(handle: number): void; +} + export interface ExtHostSecretStateShape { $onDidChangePassword(e: { extensionId: string; key: string }): Promise; } @@ -2641,6 +2661,8 @@ export const MainContext = { MainThreadTesting: createProxyIdentifier('MainThreadTesting'), MainThreadLocalization: createProxyIdentifier('MainThreadLocalizationShape'), MainThreadSemanticSimilarity: createProxyIdentifier('MainThreadSemanticSimilarity'), + MainThreadAiRelatedInformation: createProxyIdentifier('MainThreadAiRelatedInformation'), + MainThreadAiEmbeddingVector: createProxyIdentifier('MainThreadAiEmbeddingVector'), MainThreadIssueReporter: createProxyIdentifier('MainThreadIssueReporter'), }; @@ -2701,6 +2723,8 @@ export const ExtHostContext = { ExtHostChatVariables: createProxyIdentifier('ExtHostChatVariables'), ExtHostChatProvider: createProxyIdentifier('ExtHostChatProvider'), ExtHostSemanticSimilarity: createProxyIdentifier('ExtHostSemanticSimilarity'), + ExtHostAiRelatedInformation: createProxyIdentifier('ExtHostAiRelatedInformation'), + ExtHostAiEmbeddingVector: createProxyIdentifier('ExtHostAiEmbeddingVector'), ExtHostTheming: createProxyIdentifier('ExtHostTheming'), ExtHostTunnelService: createProxyIdentifier('ExtHostTunnelService'), ExtHostManagedSockets: createProxyIdentifier('ExtHostManagedSockets'), diff --git a/src/vs/workbench/api/common/extHostAiRelatedInformation.ts b/src/vs/workbench/api/common/extHostAiRelatedInformation.ts new file mode 100644 index 00000000000..9dc39e42ae1 --- /dev/null +++ b/src/vs/workbench/api/common/extHostAiRelatedInformation.ts @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { ExtHostAiRelatedInformationShape, IMainContext, MainContext, MainThreadAiRelatedInformationShape } from 'vs/workbench/api/common/extHost.protocol'; +import type { CancellationToken, RelatedInformationProvider, RelatedInformationResult, RelatedInformationType } from 'vscode'; +import { Disposable } from 'vs/workbench/api/common/extHostTypes'; + +export class ExtHostRelatedInformation implements ExtHostAiRelatedInformationShape { + private _relatedInformationProviders: Map = new Map(); + private _nextHandle = 0; + + private readonly _proxy: MainThreadAiRelatedInformationShape; + + constructor(mainContext: IMainContext) { + this._proxy = mainContext.getProxy(MainContext.MainThreadAiRelatedInformation); + } + + async $provideAiRelatedInformation(handle: number, query: string, types: RelatedInformationType[], token: CancellationToken): Promise { + if (this._relatedInformationProviders.size === 0) { + throw new Error('No semantic similarity providers registered'); + } + + const provider = this._relatedInformationProviders.get(handle); + if (!provider) { + throw new Error('Semantic similarity provider not found'); + } + + // TODO: should this return undefined or an empty array? + const result = await provider.provideRelatedInformation(query, types, token) ?? []; + return result; + } + + getRelatedInformation(extension: IExtensionDescription, query: string, types: RelatedInformationType[]): Promise { + return this._proxy.$getAiRelatedInformation(query, types); + } + + registerRelatedInformationProvider(extension: IExtensionDescription, types: RelatedInformationType[], provider: RelatedInformationProvider): Disposable { + const handle = this._nextHandle; + this._nextHandle++; + this._relatedInformationProviders.set(handle, provider); + this._proxy.$registerAiRelatedInformationProvider(handle, types); + return new Disposable(() => { + this._proxy.$unregisterAiRelatedInformationProvider(handle); + this._relatedInformationProviders.delete(handle); + }); + } +} diff --git a/src/vs/workbench/api/common/extHostEmbeddingVector.ts b/src/vs/workbench/api/common/extHostEmbeddingVector.ts new file mode 100644 index 00000000000..20b3bcb266b --- /dev/null +++ b/src/vs/workbench/api/common/extHostEmbeddingVector.ts @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { ExtHostAiEmbeddingVectorShape, IMainContext, MainContext, MainThreadAiEmbeddingVectorShape } from 'vs/workbench/api/common/extHost.protocol'; +import type { CancellationToken, EmbeddingVectorProvider } from 'vscode'; +import { Disposable } from 'vs/workbench/api/common/extHostTypes'; + +export class ExtHostAiEmbeddingVector implements ExtHostAiEmbeddingVectorShape { + private _AiEmbeddingVectorProviders: Map = new Map(); + private _nextHandle = 0; + + private readonly _proxy: MainThreadAiEmbeddingVectorShape; + + constructor( + mainContext: IMainContext + ) { + this._proxy = mainContext.getProxy(MainContext.MainThreadAiEmbeddingVector); + } + + async $provideAiEmbeddingVector(handle: number, strings: string[], token: CancellationToken): Promise { + if (this._AiEmbeddingVectorProviders.size === 0) { + throw new Error('No embedding vector providers registered'); + } + + const provider = this._AiEmbeddingVectorProviders.get(handle); + if (!provider) { + throw new Error('Embedding vector provider not found'); + } + + const result = await provider.provideEmbeddingVector(strings, token); + if (!result) { + throw new Error('Embedding vector provider returned undefined'); + } + return result; + } + + registerEmbeddingVectorProvider(extension: IExtensionDescription, model: string, provider: EmbeddingVectorProvider): Disposable { + const handle = this._nextHandle; + this._nextHandle++; + this._AiEmbeddingVectorProviders.set(handle, provider); + this._proxy.$registerAiEmbeddingVectorProvider(model, handle); + return new Disposable(() => { + this._proxy.$unregisterAiEmbeddingVectorProvider(handle); + this._AiEmbeddingVectorProviders.delete(handle); + }); + } +} diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 2f2bba71433..ac36411c043 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -4117,3 +4117,14 @@ export class ChatMessage implements vscode.ChatMessage { } //#endregion + +//#region ai + +export enum RelatedInformationType { + SymbolInformation = 1, + CommandInformation = 2, + SearchInformation = 3, + SettingInformation = 4 +} + +//#endregion diff --git a/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts b/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts index dd837a29ec7..4df3b8e287f 100644 --- a/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts +++ b/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts @@ -36,6 +36,7 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { ISemanticSimilarityService } from 'vs/workbench/services/semanticSimilarity/common/semanticSimilarityService'; import { IChatService } from 'vs/workbench/contrib/chat/common/chatService'; import { ASK_QUICK_QUESTION_ACTION_ID } from 'vs/workbench/contrib/chat/browser/actions/chatQuickInputActions'; +import { CommandInformationResult, IAiRelatedInformationService, RelatedInformationType } from 'vs/workbench/services/aiRelatedInformation/common/aiRelatedInformation'; export class CommandsQuickAccessProvider extends AbstractEditorCommandsQuickAccessProvider { @@ -75,6 +76,7 @@ export class CommandsQuickAccessProvider extends AbstractEditorCommandsQuickAcce @IPreferencesService private readonly preferencesService: IPreferencesService, @IProductService private readonly productService: IProductService, @ISemanticSimilarityService private readonly semanticSimilarityService: ISemanticSimilarityService, + @IAiRelatedInformationService private readonly aiRelatedInformationService: IAiRelatedInformationService, @IChatService private readonly chatService: IChatService ) { super({ @@ -137,7 +139,12 @@ export class CommandsQuickAccessProvider extends AbstractEditorCommandsQuickAcce } protected hasAdditionalCommandPicks(filter: string, token: CancellationToken): boolean { - if (!this.useSemanticSimilarity || filter === '' || token.isCancellationRequested || !this.semanticSimilarityService.isEnabled()) { + if ( + !this.useSemanticSimilarity + || token.isCancellationRequested + || filter === '' + || !(this.semanticSimilarityService.isEnabled() || this.aiRelatedInformationService.isEnabled()) + ) { return false; } @@ -149,16 +156,49 @@ export class CommandsQuickAccessProvider extends AbstractEditorCommandsQuickAcce return []; } - const format = allPicks.map(p => p.commandId); - let scores: number[]; + let additionalPicks; + try { // Wait a bit to see if the user is still typing await timeout(CommandsQuickAccessProvider.SEMANTIC_SIMILARITY_DEBOUNCE, token); - scores = await this.semanticSimilarityService.getSimilarityScore(filter, format, token); + additionalPicks = this.aiRelatedInformationService.isEnabled() + ? await this.getRelatedInformationPicks(allPicks, picksSoFar, filter, token) + : this.semanticSimilarityService.isEnabled() + ? await this.getSemanticSimilarityPicks(allPicks, picksSoFar, filter, token) + : []; } catch (e) { return []; } + if (additionalPicks.length) { + additionalPicks.unshift({ + type: 'separator', + label: localize('semanticSimilarity', "similar commands") + }); + } + + if (picksSoFar.length || additionalPicks.length) { + additionalPicks.push({ + type: 'separator' + }); + } + + const info = this.chatService.getProviderInfos()[0]; + if (info) { + additionalPicks.push({ + label: localize('askXInChat', "Ask {0}: {1}", info.displayName, filter), + commandId: ASK_QUICK_QUESTION_ACTION_ID, + args: [filter] + }); + } + + return additionalPicks; + } + + private async getSemanticSimilarityPicks(allPicks: ICommandQuickPick[], picksSoFar: ICommandQuickPick[], filter: string, token: CancellationToken) { + const format = allPicks.map(p => p.commandId); + const scores = await this.semanticSimilarityService.getSimilarityScore(filter, format, token); + if (token.isCancellationRequested) { return []; } @@ -181,26 +221,30 @@ export class CommandsQuickAccessProvider extends AbstractEditorCommandsQuickAcce } } - if (numOfSmartPicks) { - additionalPicks.unshift({ - type: 'separator', - label: localize('semanticSimilarity', "similar commands") - }); - } + return additionalPicks; + } - if (picksSoFar.length || additionalPicks.length) { - additionalPicks.push({ - type: 'separator' - }); - } + private async getRelatedInformationPicks(allPicks: ICommandQuickPick[], picksSoFar: ICommandQuickPick[], filter: string, token: CancellationToken) { + const relatedInformation = await this.aiRelatedInformationService.getRelatedInformation( + filter, + [RelatedInformationType.CommandInformation], + token + ) as CommandInformationResult[]; - const info = this.chatService.getProviderInfos()[0]; - if (info) { - additionalPicks.push({ - label: localize('askXInChat', "Ask {0}: {1}", info.displayName, filter), - commandId: ASK_QUICK_QUESTION_ACTION_ID, - args: [filter] - }); + // Sort by weight descending to get the most relevant results first + relatedInformation.sort((a, b) => b.weight - a.weight); + + const setOfPicksSoFar = new Set(picksSoFar.map(p => p.commandId)); + const additionalPicks = new Array(); + + for (const info of relatedInformation) { + if (info.weight < CommandsQuickAccessProvider.SEMANTIC_SIMILARITY_THRESHOLD || additionalPicks.length === CommandsQuickAccessProvider.SEMANTIC_SIMILARITY_MAX_PICKS) { + break; + } + const pick = allPicks.find(p => p.commandId === info.command && !setOfPicksSoFar.has(p.commandId)); + if (pick) { + additionalPicks.push(pick); + } } return additionalPicks; diff --git a/src/vs/workbench/services/aiEmbeddingVector/common/aiEmbeddingVectorService.ts b/src/vs/workbench/services/aiEmbeddingVector/common/aiEmbeddingVectorService.ts new file mode 100644 index 00000000000..bb143a48893 --- /dev/null +++ b/src/vs/workbench/services/aiEmbeddingVector/common/aiEmbeddingVectorService.ts @@ -0,0 +1,114 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { CancelablePromise, createCancelablePromise, raceCancellablePromises, timeout } from 'vs/base/common/async'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { StopWatch } from 'vs/base/common/stopwatch'; +import { ILogService } from 'vs/platform/log/common/log'; + +export const IAiEmbeddingVectorService = createDecorator('IAiEmbeddingVectorService'); + +export interface IAiEmbeddingVectorService { + readonly _serviceBrand: undefined; + + isEnabled(): boolean; + getEmbeddingVector(str: string, token: CancellationToken): Promise; + getEmbeddingVector(strings: string[], token: CancellationToken): Promise; + registerAiEmbeddingVectorProvider(model: string, provider: IAiEmbeddingVectorProvider): IDisposable; +} + +export interface IAiEmbeddingVectorProvider { + provideAiEmbeddingVector(strings: string[], token: CancellationToken): Promise; +} + +export class AiEmbeddingVectorService implements IAiEmbeddingVectorService { + readonly _serviceBrand: undefined; + + static readonly DEFAULT_TIMEOUT = 1000 * 10; // 10 seconds + + private readonly _providers: IAiEmbeddingVectorProvider[] = []; + + constructor(@ILogService private readonly logService: ILogService) { } + + isEnabled(): boolean { + return this._providers.length > 0; + } + + registerAiEmbeddingVectorProvider(model: string, provider: IAiEmbeddingVectorProvider): IDisposable { + this._providers.push(provider); + return { + dispose: () => { + const index = this._providers.indexOf(provider); + if (index >= 0) { + this._providers.splice(index, 1); + } + } + }; + } + + getEmbeddingVector(str: string, token: CancellationToken): Promise; + getEmbeddingVector(strings: string[], token: CancellationToken): Promise; + async getEmbeddingVector(strings: string | string[], token: CancellationToken): Promise { + if (this._providers.length === 0) { + throw new Error('No embedding vector providers registered'); + } + + const stopwatch = StopWatch.create(); + + const cancellablePromises: Array> = []; + + const timer = timeout(AiEmbeddingVectorService.DEFAULT_TIMEOUT); + const disposable = token.onCancellationRequested(() => { + disposable.dispose(); + timer.cancel(); + }); + + for (const provider of this._providers) { + cancellablePromises.push(createCancelablePromise(async t => { + try { + return await provider.provideAiEmbeddingVector( + Array.isArray(strings) ? strings : [strings], + t + ); + } catch (e) { + // logged in extension host + } + // Wait for the timer to finish to allow for another provider to resolve. + // Alternatively, if something resolved, or we've timed out, this will throw + // as expected. + await timer; + throw new Error('Embedding vector provider timed out'); + })); + } + + cancellablePromises.push(createCancelablePromise(async (t) => { + const disposable = t.onCancellationRequested(() => { + timer.cancel(); + disposable.dispose(); + }); + await timer; + throw new Error('Embedding vector provider timed out'); + })); + + try { + const result = await raceCancellablePromises(cancellablePromises); + + // If we have a single result, return it directly, otherwise return an array. + // This aligns with the API overloads. + if (result.length === 1) { + return result[0]; + } + return result; + } finally { + stopwatch.stop(); + this.logService.trace(`[AiEmbeddingVectorService]: getEmbeddingVector took ${stopwatch.elapsed()}ms`); + } + } +} + +registerSingleton(IAiEmbeddingVectorService, AiEmbeddingVectorService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/aiRelatedInformation/common/aiRelatedInformation.ts b/src/vs/workbench/services/aiRelatedInformation/common/aiRelatedInformation.ts new file mode 100644 index 00000000000..75248fb38d5 --- /dev/null +++ b/src/vs/workbench/services/aiRelatedInformation/common/aiRelatedInformation.ts @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CancellationToken } from 'vs/base/common/cancellation'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +export const IAiRelatedInformationService = createDecorator('IAiRelatedInformationService'); + +export enum RelatedInformationType { + SymbolInformation = 1, + CommandInformation = 2, + SearchInformation = 3, + SettingInformation = 4 +} + +export interface RelatedInformationResult { + type: RelatedInformationType; + weight: number; +} + +export interface CommandInformationResult extends RelatedInformationResult { + type: RelatedInformationType.CommandInformation; + command: string; +} + +export interface IAiRelatedInformationService { + readonly _serviceBrand: undefined; + + isEnabled(): boolean; + getRelatedInformation(query: string, types: RelatedInformationType[], token: CancellationToken): Promise; + registerAiRelatedInformationProvider(types: RelatedInformationType[], provider: IAiRelatedInformationProvider): IDisposable; +} + +export interface IAiRelatedInformationProvider { + provideAiRelatedInformation(query: string, types: RelatedInformationType[], token: CancellationToken): Promise; +} diff --git a/src/vs/workbench/services/aiRelatedInformation/common/aiRelatedInformationService.ts b/src/vs/workbench/services/aiRelatedInformation/common/aiRelatedInformationService.ts new file mode 100644 index 00000000000..e0309ff37d1 --- /dev/null +++ b/src/vs/workbench/services/aiRelatedInformation/common/aiRelatedInformationService.ts @@ -0,0 +1,114 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CancellationToken } from 'vs/base/common/cancellation'; +import { CancelablePromise, createCancelablePromise, raceCancellablePromises, timeout } from 'vs/base/common/async'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { StopWatch } from 'vs/base/common/stopwatch'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IAiRelatedInformationService, IAiRelatedInformationProvider, RelatedInformationType, RelatedInformationResult } from 'vs/workbench/services/aiRelatedInformation/common/aiRelatedInformation'; + +export class AiRelatedInformationService implements IAiRelatedInformationService { + readonly _serviceBrand: undefined; + + static readonly DEFAULT_TIMEOUT = 1000 * 10; // 10 seconds + + private readonly _providers: Map = new Map(); + + constructor(@ILogService private readonly logService: ILogService) { } + + isEnabled(): boolean { + return this._providers.size > 0; + } + + registerAiRelatedInformationProvider(types: RelatedInformationType[], provider: IAiRelatedInformationProvider): IDisposable { + for (const type of types) { + const providers = this._providers.get(type) ?? []; + providers.push(provider); + this._providers.set(type, providers); + } + + return { + dispose: () => { + for (const type of types) { + const providers = this._providers.get(type) ?? []; + const index = providers.indexOf(provider); + if (index !== -1) { + providers.splice(index, 1); + } + if (providers.length === 0) { + this._providers.delete(type); + } + } + } + }; + } + + async getRelatedInformation(query: string, types: RelatedInformationType[], token: CancellationToken): Promise { + if (this._providers.size === 0) { + throw new Error('No related information providers registered'); + } + + // get providers for each type + const providers: IAiRelatedInformationProvider[] = []; + for (const type of types) { + const typeProviders = this._providers.get(type); + if (typeProviders) { + providers.push(...typeProviders); + } + } + + if (providers.length === 0) { + throw new Error('No related information providers registered for the given types'); + } + + const stopwatch = StopWatch.create(); + + const cancellablePromises: Array> = []; + + const timer = timeout(AiRelatedInformationService.DEFAULT_TIMEOUT); + const disposable = token.onCancellationRequested(() => { + disposable.dispose(); + timer.cancel(); + }); + + for (const provider of providers) { + cancellablePromises.push(createCancelablePromise(async t => { + try { + const result = await provider.provideAiRelatedInformation(query, types, t); + // double filter just in case + return result.filter(r => types.includes(r.type)); + } catch (e) { + // logged in extension host + } + // Wait for the timer to finish to allow for another provider to resolve. + // Alternatively, if something resolved, or we've timed out, this will throw + // as expected. + await timer; + throw new Error('Related information provider timed out'); + })); + } + + cancellablePromises.push(createCancelablePromise(async (t) => { + const disposable = t.onCancellationRequested(() => { + timer.cancel(); + disposable.dispose(); + }); + await timer; + throw new Error('Related information provider timed out'); + })); + + try { + const result = await raceCancellablePromises(cancellablePromises); + return result; + } finally { + stopwatch.stop(); + this.logService.trace(`[AiRelatedInformationService]: getRelatedInformation took ${stopwatch.elapsed()}ms`); + } + } +} + +registerSingleton(IAiRelatedInformationService, AiRelatedInformationService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index f3dc351a3a5..8b1cba0367d 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -6,6 +6,7 @@ // THIS IS A GENERATED FILE. DO NOT EDIT DIRECTLY. export const allApiProposals = Object.freeze({ + aiRelatedInformation: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.aiRelatedInformation.d.ts', authGetSessions: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.authGetSessions.d.ts', authSession: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.authSession.d.ts', canonicalUriProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.canonicalUriProvider.d.ts', diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index a52b2b4c274..13cdbab8e9c 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -68,6 +68,8 @@ import 'vs/workbench/services/configuration/common/jsonEditingService'; import 'vs/workbench/services/textmodelResolver/common/textModelResolverService'; import 'vs/workbench/services/editor/browser/editorService'; import 'vs/workbench/services/editor/browser/editorResolverService'; +import 'vs/workbench/services/aiEmbeddingVector/common/aiEmbeddingVectorService'; +import 'vs/workbench/services/aiRelatedInformation/common/aiRelatedInformationService'; import 'vs/workbench/services/history/browser/historyService'; import 'vs/workbench/services/activity/browser/activityService'; import 'vs/workbench/services/keybinding/browser/keybindingService'; diff --git a/src/vscode-dts/vscode.proposed.aiRelatedInformation.d.ts b/src/vscode-dts/vscode.proposed.aiRelatedInformation.d.ts new file mode 100644 index 00000000000..d3916c50608 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.aiRelatedInformation.d.ts @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/190909 + + export interface SearchResult { + // from Andrea + preview: string; + resource: Uri; + location: Range; + } + + export enum RelatedInformationType { + SymbolInformation = 1, + CommandInformation = 2, + SearchInformation = 3, + SettingInformation = 4 + } + + export interface RelatedInformationResult { + type: RelatedInformationType; + weight: number; + } + + export interface SymbolInformationResult extends RelatedInformationResult { + type: RelatedInformationType.SymbolInformation; + symbolInformation: SymbolInformation; + } + + export interface CommandInformationResult extends RelatedInformationResult { + type: RelatedInformationType.CommandInformation; + command: string; + } + + export interface SettingInformationResult extends RelatedInformationResult { + type: RelatedInformationType.SettingInformation; + setting: string; + } + + export interface SearchInformationResult extends RelatedInformationResult { + type: RelatedInformationType.SearchInformation; + searchResult: SearchResult; + } + + export interface RelatedInformationProvider { + provideRelatedInformation(query: string, types: RelatedInformationType[], token: CancellationToken): ProviderResult; + } + + export interface EmbeddingVectorProvider { + provideEmbeddingVector(strings: string[], token: CancellationToken): ProviderResult; + } + + export namespace ai { + export function getRelatedInformation(query: string, types: RelatedInformationType[], token: CancellationToken): Thenable; + export function registerRelatedInformationProvider(types: RelatedInformationType[], provider: RelatedInformationProvider): Disposable; + export function registerEmbeddingVectorProvider(model: string, provider: EmbeddingVectorProvider): Disposable; + } +} From 08a8a80a265ce0f47cc97131d6466b31b14fb2a6 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 22 Aug 2023 21:48:10 +0200 Subject: [PATCH 201/223] debt - change `listenStream` to use a cancellation token (#191003) //cc @connor4312 --- src/vs/base/common/stream.ts | 14 ++++++-------- src/vs/base/test/common/stream.test.ts | 11 +++++++---- .../contrib/files/browser/fileImportExport.ts | 4 ++-- .../workbench/services/textfile/common/encoding.ts | 9 ++++++--- 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/vs/base/common/stream.ts b/src/vs/base/common/stream.ts index 9f1039c0e52..055558fc748 100644 --- a/src/vs/base/common/stream.ts +++ b/src/vs/base/common/stream.ts @@ -3,8 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { CancellationToken } from 'vs/base/common/cancellation'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; /** * The payload that flows in readable stream events. @@ -567,17 +568,16 @@ export interface IStreamListener { /** * Helper to listen to all events of a T stream in proper order. */ -export function listenStream(stream: ReadableStreamEvents, listener: IStreamListener): IDisposable { - let destroyed = false; +export function listenStream(stream: ReadableStreamEvents, listener: IStreamListener, token?: CancellationToken): void { stream.on('error', error => { - if (!destroyed) { + if (!token?.isCancellationRequested) { listener.onError(error); } }); stream.on('end', () => { - if (!destroyed) { + if (!token?.isCancellationRequested) { listener.onEnd(); } }); @@ -586,12 +586,10 @@ export function listenStream(stream: ReadableStreamEvents, listener: IStre // into flowing mode. As such it is important to // add this listener last (DO NOT CHANGE!) stream.on('data', data => { - if (!destroyed) { + if (!token?.isCancellationRequested) { listener.onData(data); } }); - - return toDisposable(() => destroyed = true); } /** diff --git a/src/vs/base/test/common/stream.test.ts b/src/vs/base/test/common/stream.test.ts index 78c38d691a4..63d30530a95 100644 --- a/src/vs/base/test/common/stream.test.ts +++ b/src/vs/base/test/common/stream.test.ts @@ -6,6 +6,7 @@ import * as assert from 'assert'; import { timeout } from 'vs/base/common/async'; import { bufferToReadable, VSBuffer } from 'vs/base/common/buffer'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { consumeReadable, consumeStream, isReadable, isReadableBufferedStream, isReadableStream, listenStream, newWriteableStream, peekReadable, peekStream, prefixedReadable, prefixedStream, Readable, ReadableStream, toReadable, toStream, transform } from 'vs/base/common/stream'; suite('Stream', () => { @@ -351,14 +352,16 @@ suite('Stream', () => { assert.strictEqual(end, true); }); - test('listenStream - dispose', () => { + test('listenStream - cancellation', () => { const stream = newWriteableStream(strings => strings.join()); let error = false; let end = false; let data = ''; - const disposable = listenStream(stream, { + const cts = new CancellationTokenSource(); + + listenStream(stream, { onData: d => { data = d; }, @@ -368,9 +371,9 @@ suite('Stream', () => { onEnd: () => { end = true; } - }); + }, cts.token); - disposable.dispose(); + cts.cancel(); stream.write('Hello'); assert.strictEqual(data, ''); diff --git a/src/vs/workbench/contrib/files/browser/fileImportExport.ts b/src/vs/workbench/contrib/files/browser/fileImportExport.ts index e93695b3c16..56c7149a0ae 100644 --- a/src/vs/workbench/contrib/files/browser/fileImportExport.ts +++ b/src/vs/workbench/contrib/files/browser/fileImportExport.ts @@ -715,7 +715,7 @@ export class FileDownload { reject(canceled()); })); - disposables.add(listenStream(sourceStream, { + listenStream(sourceStream, { onData: data => { target.write(data.buffer); this.reportProgress(contents.name, contents.size, data.byteLength, operation); @@ -728,7 +728,7 @@ export class FileDownload { disposables.dispose(); resolve(); } - })); + }, token); }); } diff --git a/src/vs/workbench/services/textfile/common/encoding.ts b/src/vs/workbench/services/textfile/common/encoding.ts index 6675f732b0e..ad67fb4f422 100644 --- a/src/vs/workbench/services/textfile/common/encoding.ts +++ b/src/vs/workbench/services/textfile/common/encoding.ts @@ -6,6 +6,7 @@ import { Readable, ReadableStream, newWriteableStream, listenStream } from 'vs/base/common/stream'; import { VSBuffer, VSBufferReadable, VSBufferReadableStream } from 'vs/base/common/buffer'; import { importAMDNodeModule } from 'vs/amdX'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; export const UTF8 = 'utf8'; export const UTF8_with_bom = 'utf8bom'; @@ -124,6 +125,8 @@ export function toDecodeStream(source: VSBufferReadableStream, options: IDecodeS let decoder: IDecoderStream | undefined = undefined; + const cts = new CancellationTokenSource(); + const createDecoder = async () => { try { @@ -158,14 +161,14 @@ export function toDecodeStream(source: VSBufferReadableStream, options: IDecodeS } catch (error) { // Stop handling anything from the source and target - sourceListener?.dispose(); + cts.cancel(); target.destroy(); reject(error); } }; - const sourceListener = listenStream(source, { + listenStream(source, { onData: async chunk => { // if the decoder is ready, we just write directly @@ -205,7 +208,7 @@ export function toDecodeStream(source: VSBufferReadableStream, options: IDecodeS // end the target with the remainders of the decoder target.end(decoder?.end()); } - }); + }, cts.token); }); } From bcea55affb1ccdc0b15fe39c86a79268c16dab72 Mon Sep 17 00:00:00 2001 From: Justin Chen <54879025+justschen@users.noreply.github.com> Date: Tue, 22 Aug 2023 12:57:54 -0700 Subject: [PATCH 202/223] fixes hover/click ui bug in #190508 (#191009) * bug fix for issue with hover/clicks * code cleanup, removing spaces --- src/vs/platform/actionWidget/browser/actionWidget.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/actionWidget/browser/actionWidget.css b/src/vs/platform/actionWidget/browser/actionWidget.css index 1384b5f5e74..539af6ca265 100644 --- a/src/vs/platform/actionWidget/browser/actionWidget.css +++ b/src/vs/platform/actionWidget/browser/actionWidget.css @@ -94,7 +94,8 @@ .action-widget .monaco-list-row.action.option-disabled, .action-widget .monaco-list:focus .monaco-list-row.focused.action.option-disabled, -.action-widget .monaco-list-row.action.option-disabled .codicon { +.action-widget .monaco-list-row.action.option-disabled .codicon, +.action-widget .monaco-list:not(.drop-target):not(.dragging) .monaco-list-row:hover:not(.selected):not(.focused).option-disabled { color: var(--vscode-disabledForeground); } From f09b5e1c3a14e3b0a57ceffb6d42ee85993789b8 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 22 Aug 2023 13:06:01 -0700 Subject: [PATCH 203/223] use store --- .../browser/textAreaSyncAddon.ts | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts index 9a52fc9c00a..54599032dc5 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { ITerminalCapabilityStore, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; import { ITerminalLogService } from 'vs/platform/terminal/common/terminal'; @@ -18,9 +18,7 @@ export interface ITextAreaData { export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { private _terminal: Terminal | undefined; - private _onCursorMoveListener = this._register(new MutableDisposable()); - private _onDidFocusListener = this._register(new MutableDisposable()); - private _onKeyListener = this._register(new MutableDisposable()); + private _listeners = this._register(new MutableDisposable()); activate(terminal: Terminal): void { this._terminal = terminal; if (this._accessibilityService.isScreenReaderOptimized()) { @@ -40,22 +38,20 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { this._refreshTextArea(); this._setListeners(); } else { - this._onCursorMoveListener.clear(); - this._onDidFocusListener.clear(); - this._onKeyListener.clear(); + this._listeners.clear(); } })); } private _setListeners(): void { if (this._accessibilityService.isScreenReaderOptimized() && this._terminal) { - this._onCursorMoveListener.value = this._terminal.onCursorMove(() => this._refreshTextArea()); - this._onDidFocusListener.value = this._onDidFocus(() => this._refreshTextArea()); - this._onKeyListener.value = this._terminal.onKey((e) => { + this._listeners.value?.add(this._terminal.onCursorMove(() => this._refreshTextArea())); + this._listeners.value?.add(this._onDidFocus(() => this._refreshTextArea())); + this._listeners.value?.add(this._terminal.onKey((e) => { if (e.domEvent.key === 'UpArrow') { this._refreshTextArea(); } - }); + })); } } @@ -113,7 +109,7 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { this._logService.debug(`TextAreaSyncAddon#refreshTextArea: content is "${content}"`); this._logService.debug(`TextAreaSyncAddon#refreshTextArea: textContent is "${textArea.textContent}"`); if (content !== textArea.textContent) { - textArea.textContent = content.trim(); + textArea.textContent = content; this._logService.debug(`TextAreaSyncAddon#refreshTextArea: textContent changed to "${content}"`); } From c71d140106897d0225487c85d9e215d3fd8a06b3 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 22 Aug 2023 13:13:34 -0700 Subject: [PATCH 204/223] create store --- .../accessibility/browser/textAreaSyncAddon.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts index 54599032dc5..22df31960fd 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts @@ -10,6 +10,7 @@ import { ITerminalLogService } from 'vs/platform/terminal/common/terminal'; import type { Terminal, ITerminalAddon } from 'xterm'; import { Event } from 'vs/base/common/event'; import { ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { debounce } from 'vs/base/common/decorators'; export interface ITextAreaData { content: string; @@ -45,9 +46,10 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { private _setListeners(): void { if (this._accessibilityService.isScreenReaderOptimized() && this._terminal) { - this._listeners.value?.add(this._terminal.onCursorMove(() => this._refreshTextArea())); - this._listeners.value?.add(this._onDidFocus(() => this._refreshTextArea())); - this._listeners.value?.add(this._terminal.onKey((e) => { + this._listeners.value = new DisposableStore(); + this._listeners.value.add(this._terminal.onCursorMove(() => this._refreshTextArea())); + this._listeners.value.add(this._onDidFocus(() => this._refreshTextArea())); + this._listeners.value.add(this._terminal.onKey((e) => { if (e.domEvent.key === 'UpArrow') { this._refreshTextArea(); } @@ -55,6 +57,7 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { } } + @debounce(50) private _refreshTextArea(): void { if (!this._terminal) { return; From da896a565443d795eb4cfd99708206af0461af16 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 22 Aug 2023 13:17:23 -0700 Subject: [PATCH 205/223] add listener to textarea directly --- .../browser/terminal.accessibility.contribution.ts | 2 +- .../accessibility/browser/textAreaSyncAddon.ts | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts index 3594567f2b1..b69943c18de 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts @@ -40,7 +40,7 @@ class TextAreaSyncContribution extends DisposableStore implements ITerminalContr super(); } xtermReady(xterm: IXtermTerminal & { raw: Terminal }): void { - const addon = this._instantiationService.createInstance(TextAreaSyncAddon, this._instance.capabilities, this._instance.onDidFocus); + const addon = this._instantiationService.createInstance(TextAreaSyncAddon, this._instance.capabilities); xterm.raw.loadAddon(addon); addon.activate(xterm.raw); } diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts index 22df31960fd..454820589ba 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts @@ -8,9 +8,8 @@ import { IAccessibilityService } from 'vs/platform/accessibility/common/accessib import { ITerminalCapabilityStore, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; import { ITerminalLogService } from 'vs/platform/terminal/common/terminal'; import type { Terminal, ITerminalAddon } from 'xterm'; -import { Event } from 'vs/base/common/event'; -import { ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { debounce } from 'vs/base/common/decorators'; +import { addDisposableListener } from 'vs/base/browser/dom'; export interface ITextAreaData { content: string; @@ -29,7 +28,6 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { constructor( private readonly _capabilities: ITerminalCapabilityStore, - private readonly _onDidFocus: Event, @IAccessibilityService private readonly _accessibilityService: IAccessibilityService, @ITerminalLogService private readonly _logService: ITerminalLogService ) { @@ -45,10 +43,10 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { } private _setListeners(): void { - if (this._accessibilityService.isScreenReaderOptimized() && this._terminal) { + if (this._accessibilityService.isScreenReaderOptimized() && this._terminal?.textarea) { this._listeners.value = new DisposableStore(); this._listeners.value.add(this._terminal.onCursorMove(() => this._refreshTextArea())); - this._listeners.value.add(this._onDidFocus(() => this._refreshTextArea())); + this._listeners.value.add(addDisposableListener(this._terminal.textarea, 'focus', () => this._refreshTextArea())); this._listeners.value.add(this._terminal.onKey((e) => { if (e.domEvent.key === 'UpArrow') { this._refreshTextArea(); From d94db47be294698220a2598e639aba31babffb20 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Tue, 22 Aug 2023 13:32:14 -0700 Subject: [PATCH 206/223] Capitalize transpose command (#191011) --- .../editor/contrib/linesOperations/browser/linesOperations.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/linesOperations/browser/linesOperations.ts b/src/vs/editor/contrib/linesOperations/browser/linesOperations.ts index 0e3355907af..11a5c6c4f27 100644 --- a/src/vs/editor/contrib/linesOperations/browser/linesOperations.ts +++ b/src/vs/editor/contrib/linesOperations/browser/linesOperations.ts @@ -953,8 +953,8 @@ export class TransposeAction extends EditorAction { constructor() { super({ id: 'editor.action.transpose', - label: nls.localize('editor.transpose', "Transpose characters around the cursor"), - alias: 'Transpose characters around the cursor', + label: nls.localize('editor.transpose', "Transpose Characters around the Cursor"), + alias: 'Transpose Characters around the Cursor', precondition: EditorContextKeys.writable }); } From 7f459649e4ed959cc067374aefca6f10172c45c6 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 22 Aug 2023 13:56:58 -0700 Subject: [PATCH 207/223] get it to work via value instead of textContent --- .../accessibility/browser/textAreaSyncAddon.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts index 454820589ba..3251695238b 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts @@ -91,7 +91,7 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { this._logService.debug(`TextAreaSyncAddon#refreshTextArea: no content`); const textArea = this._terminal.textarea; if (textArea) { - textArea.textContent = ''; + textArea.value = ''; } return; } @@ -110,7 +110,7 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { this._logService.debug(`TextAreaSyncAddon#refreshTextArea: content is "${content}"`); this._logService.debug(`TextAreaSyncAddon#refreshTextArea: textContent is "${textArea.textContent}"`); if (content !== textArea.textContent) { - textArea.textContent = content; + textArea.value = content; this._logService.debug(`TextAreaSyncAddon#refreshTextArea: textContent changed to "${content}"`); } @@ -122,6 +122,5 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { textArea.selectionEnd = cursorX; this._logService.debug(`TextAreaSyncAddon#refreshTextArea: selectionStart changed to ${cursorX}`); } - // TODO: cursorY? } } From 0b13f1859caf75a2d3d16a29afcbef03e111447b Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Tue, 22 Aug 2023 14:03:06 -0700 Subject: [PATCH 208/223] Update Settings to use AiRelatedInformation (#191017) ref #190909 --- .../preferences/browser/preferencesSearch.ts | 60 ++++++++++++++++--- .../common/aiRelatedInformation.ts | 5 ++ .../common/semanticSimilarityService.ts | 6 ++ 3 files changed, 63 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts b/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts index 486c29e09be..fe7ce4b6848 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts @@ -21,6 +21,7 @@ import { ExtensionType } from 'vs/platform/extensions/common/extensions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ISemanticSimilarityService } from 'vs/workbench/services/semanticSimilarity/common/semanticSimilarityService'; +import { IAiRelatedInformationService, RelatedInformationType, SettingInformationResult } from 'vs/workbench/services/aiRelatedInformation/common/aiRelatedInformation'; export interface IEndpointDetails { urlBase?: string; @@ -302,8 +303,10 @@ class RemoteSearchKeysProvider { private settingsRecord: Record = {}; private currentPreferencesModel: ISettingsEditorModel | undefined; - constructor(private readonly semanticSimilarityService: ISemanticSimilarityService) { - } + constructor( + private readonly aiRelatedInformationService: IAiRelatedInformationService, + private readonly semanticSimilarityService: ISemanticSimilarityService + ) { } updateModel(preferencesModel: ISettingsEditorModel) { if (preferencesModel === this.currentPreferencesModel) { @@ -318,7 +321,10 @@ class RemoteSearchKeysProvider { this.settingKeys = []; this.settingsRecord = {}; - if (!this.semanticSimilarityService.isEnabled() || !this.currentPreferencesModel) { + if ( + !this.currentPreferencesModel || + (!this.semanticSimilarityService.isEnabled() && !this.aiRelatedInformationService.isEnabled()) + ) { return; } @@ -353,8 +359,9 @@ export class RemoteSearchProvider implements ISearchProvider { constructor( @ISemanticSimilarityService private readonly semanticSimilarityService: ISemanticSimilarityService, + @IAiRelatedInformationService private readonly aiRelatedInformationService: IAiRelatedInformationService ) { - this._keysProvider = new RemoteSearchKeysProvider(semanticSimilarityService); + this._keysProvider = new RemoteSearchKeysProvider(aiRelatedInformationService, semanticSimilarityService); } setFilter(filter: string) { @@ -362,11 +369,26 @@ export class RemoteSearchProvider implements ISearchProvider { } async searchModel(preferencesModel: ISettingsEditorModel, token?: CancellationToken | undefined): Promise { - if (!this.semanticSimilarityService.isEnabled() || !this._filter) { + if ( + !this._filter || + (!this.semanticSimilarityService.isEnabled() && !this.aiRelatedInformationService.isEnabled())) { return null; } this._keysProvider.updateModel(preferencesModel); + const filterMatches = this.aiRelatedInformationService.isEnabled() + ? await this.getAiRelatedInformationItems(token) + : this.semanticSimilarityService.isEnabled() + ? await this.getSemanticSimilarityItems(token) + : []; + + return { + filterMatches + }; + } + + // TODO: Remove this when all semantic similarity providers are migrated to aiRelatedInformationService + private async getSemanticSimilarityItems(token?: CancellationToken | undefined) { const settingKeys = this._keysProvider.getSettingKeys(); const settingsRecord = this._keysProvider.getSettingsRecord(); @@ -389,9 +411,31 @@ export class RemoteSearchProvider implements ISearchProvider { }); numOfSmartPicks++; } - return { - filterMatches - }; + + return filterMatches; + } + + private async getAiRelatedInformationItems(token?: CancellationToken | undefined) { + const settingsRecord = this._keysProvider.getSettingsRecord(); + + const filterMatches: ISettingMatch[] = []; + const relatedInformation = await this.aiRelatedInformationService.getRelatedInformation(this._filter, [RelatedInformationType.SettingInformation], token ?? CancellationToken.None) as SettingInformationResult[]; + relatedInformation.sort((a, b) => b.weight - a.weight); + + for (const info of relatedInformation) { + if (info.weight < RemoteSearchProvider.SEMANTIC_SIMILARITY_THRESHOLD || filterMatches.length === RemoteSearchProvider.SEMANTIC_SIMILARITY_MAX_PICKS) { + break; + } + const pick = info.setting; + filterMatches.push({ + setting: settingsRecord[pick], + matches: [settingsRecord[pick].range], + matchType: SettingMatchType.RemoteMatch, + score: info.weight + }); + } + + return filterMatches; } } diff --git a/src/vs/workbench/services/aiRelatedInformation/common/aiRelatedInformation.ts b/src/vs/workbench/services/aiRelatedInformation/common/aiRelatedInformation.ts index 75248fb38d5..a8bddf2c1e4 100644 --- a/src/vs/workbench/services/aiRelatedInformation/common/aiRelatedInformation.ts +++ b/src/vs/workbench/services/aiRelatedInformation/common/aiRelatedInformation.ts @@ -26,6 +26,11 @@ export interface CommandInformationResult extends RelatedInformationResult { command: string; } +export interface SettingInformationResult extends RelatedInformationResult { + type: RelatedInformationType.SettingInformation; + setting: string; +} + export interface IAiRelatedInformationService { readonly _serviceBrand: undefined; diff --git a/src/vs/workbench/services/semanticSimilarity/common/semanticSimilarityService.ts b/src/vs/workbench/services/semanticSimilarity/common/semanticSimilarityService.ts index ff203430c99..e322503d117 100644 --- a/src/vs/workbench/services/semanticSimilarity/common/semanticSimilarityService.ts +++ b/src/vs/workbench/services/semanticSimilarity/common/semanticSimilarityService.ts @@ -11,8 +11,14 @@ import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/ import { StopWatch } from 'vs/base/common/stopwatch'; import { ILogService } from 'vs/platform/log/common/log'; +/** + * @deprecated Use `IAiRelatedInformationService` instead. + */ export const ISemanticSimilarityService = createDecorator('ISemanticSimilarityService'); +/** + * @deprecated Use `IAiRelatedInformationService` instead. + */ export interface ISemanticSimilarityService { readonly _serviceBrand: undefined; From cbc1cba21b6e288c3d5e976853cc34c6335581fb Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 22 Aug 2023 14:09:11 -0700 Subject: [PATCH 209/223] use better event, fix bug --- .../accessibility/browser/textAreaSyncAddon.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts index 3251695238b..b914a3e65d9 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts @@ -47,11 +47,7 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { this._listeners.value = new DisposableStore(); this._listeners.value.add(this._terminal.onCursorMove(() => this._refreshTextArea())); this._listeners.value.add(addDisposableListener(this._terminal.textarea, 'focus', () => this._refreshTextArea())); - this._listeners.value.add(this._terminal.onKey((e) => { - if (e.domEvent.key === 'UpArrow') { - this._refreshTextArea(); - } - })); + this._listeners.value.add(this._terminal.onData((e) => this._refreshTextArea())); } } From 013600cf334f804298647fa0ab8a8dde99925596 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 22 Aug 2023 14:14:24 -0700 Subject: [PATCH 210/223] Don't show `copy image` in command palette (#190907) #190773 --- extensions/markdown-language-features/package.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index 60e80b169d3..78c71ce9138 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -123,7 +123,8 @@ "commands": [ { "command": "_markdown.copyImage", - "title": "%markdown.copyImage.title%" + "title": "%markdown.copyImage.title%", + "category": "Markdown" }, { "command": "markdown.showPreview", @@ -244,6 +245,10 @@ } ], "commandPalette": [ + { + "command": "_markdown.copyImage", + "when": "false" + }, { "command": "markdown.showPreview", "when": "editorLangId == markdown && !notebookEditorFocused", From b9e61998f8ead7f934491b0d844cc18f8693f71c Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 22 Aug 2023 14:36:20 -0700 Subject: [PATCH 211/223] clean up --- .../browser/textAreaSyncAddon.ts | 113 ++++++++---------- 1 file changed, 52 insertions(+), 61 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts index b914a3e65d9..c4171b11aa2 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts @@ -19,6 +19,9 @@ export interface ITextAreaData { export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { private _terminal: Terminal | undefined; private _listeners = this._register(new MutableDisposable()); + private _currentCommand: string | undefined; + private _cursorX: number | undefined; + activate(terminal: Terminal): void { this._terminal = terminal; if (this._accessibilityService.isScreenReaderOptimized()) { @@ -34,7 +37,7 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { super(); this._register(this._accessibilityService.onDidChangeScreenReaderOptimized(() => { if (this._accessibilityService.isScreenReaderOptimized()) { - this._refreshTextArea(); + this._syncTextArea(); this._setListeners(); } else { this._listeners.clear(); @@ -45,78 +48,66 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { private _setListeners(): void { if (this._accessibilityService.isScreenReaderOptimized() && this._terminal?.textarea) { this._listeners.value = new DisposableStore(); - this._listeners.value.add(this._terminal.onCursorMove(() => this._refreshTextArea())); - this._listeners.value.add(addDisposableListener(this._terminal.textarea, 'focus', () => this._refreshTextArea())); - this._listeners.value.add(this._terminal.onData((e) => this._refreshTextArea())); + this._listeners.value.add(this._terminal.onCursorMove(() => this._syncTextArea())); + this._listeners.value.add(addDisposableListener(this._terminal.textarea, 'focus', () => this._syncTextArea())); + this._listeners.value.add(this._terminal.onData((e) => this._syncTextArea())); } } @debounce(50) - private _refreshTextArea(): void { - if (!this._terminal) { - return; - } + private _syncTextArea(): void { this._logService.debug('TextAreaSyncAddon#refreshTextArea'); - const commandCapability = this._capabilities.get(TerminalCapability.CommandDetection); - const currentCommand = commandCapability?.currentCommand; - if (!currentCommand) { - this._logService.debug(`TextAreaSyncAddon#refreshTextArea: no currentCommand`); - return; - } - const buffer = this._terminal.buffer.active; - const line = buffer.getLine(buffer.cursorY)?.translateToString(true); - let commandStartX: number | undefined; - if (!line) { - this._logService.debug(`TextAreaSyncAddon#refreshTextArea: no line`); - return; - } - let content: string | undefined; - if (currentCommand.commandStartX !== undefined) { - // Left prompt - content = line.substring(currentCommand.commandStartX); - commandStartX = currentCommand.commandStartX; - } else if (currentCommand.commandRightPromptStartX !== undefined) { - // Right prompt - content = line.substring(0, currentCommand.commandRightPromptStartX); - commandStartX = 0; - } else { - this._logService.debug(`TextAreaSyncAddon#refreshTextArea: no commandStartX or commandRightPromptStartX`); - } - - if (!content) { - this._logService.debug(`TextAreaSyncAddon#refreshTextArea: no content`); - const textArea = this._terminal.textarea; - if (textArea) { - textArea.value = ''; - } - return; - } - - if (commandStartX === undefined) { - this._logService.debug(`TextAreaSyncAddon#refreshTextArea: no commandStartX`); - return; - } - - const textArea = this._terminal.textarea; + const textArea = this._terminal?.textarea; if (!textArea) { this._logService.debug(`TextAreaSyncAddon#refreshTextArea: no textarea`); return; } - this._logService.debug(`TextAreaSyncAddon#refreshTextArea: content is "${content}"`); - this._logService.debug(`TextAreaSyncAddon#refreshTextArea: textContent is "${textArea.textContent}"`); - if (content !== textArea.textContent) { - textArea.value = content; - this._logService.debug(`TextAreaSyncAddon#refreshTextArea: textContent changed to "${content}"`); + this._updateCommandAndCursor(); + + if (this._currentCommand !== textArea.value) { + textArea.value = this._currentCommand || ''; + this._logService.debug(`TextAreaSyncAddon#refreshTextArea: text changed to "${this._currentCommand}"`); + } else if (!this._currentCommand) { + textArea.value = ''; + this._logService.debug(`TextAreaSyncAddon#refreshTextArea: text cleared`); } - const cursorX = buffer.cursorX - commandStartX; - this._logService.debug(`TextAreaSyncAddon#refreshTextArea: cursorX is ${cursorX}`); - this._logService.debug(`TextAreaSyncAddon#refreshTextArea: selectionStart is ${textArea.selectionStart}`); - if (cursorX !== textArea.selectionStart) { - textArea.selectionStart = cursorX; - textArea.selectionEnd = cursorX; - this._logService.debug(`TextAreaSyncAddon#refreshTextArea: selectionStart changed to ${cursorX}`); + if (this._cursorX !== textArea.selectionStart) { + textArea.selectionStart = this._cursorX ?? 0; + textArea.selectionEnd = this._cursorX ?? 0; + this._logService.debug(`TextAreaSyncAddon#refreshTextArea: selection start/end changed to ${this._cursorX}`); + } + } + + private _updateCommandAndCursor(): void { + if (!this._terminal) { + return; + } + const commandCapability = this._capabilities.get(TerminalCapability.CommandDetection); + const currentCommand = commandCapability?.currentCommand; + if (!currentCommand) { + this._logService.debug(`TextAreaSyncAddon#updateCommandAndCursor: no current command`); + return; + } + const buffer = this._terminal.buffer.active; + const line = buffer.getLine(buffer.cursorY)?.translateToString(true); + if (!line) { + this._logService.debug(`TextAreaSyncAddon#updateCommandAndCursor: no line`); + return; + } + if (currentCommand.commandStartX !== undefined) { + // Left prompt + this._currentCommand = line.substring(currentCommand.commandStartX); + this._cursorX = buffer.cursorX - currentCommand.commandStartX; + } else if (currentCommand.commandRightPromptStartX !== undefined) { + // Right prompt + this._currentCommand = line.substring(0, currentCommand.commandRightPromptStartX); + this._cursorX = buffer.cursorX; + } else { + this._currentCommand = undefined; + this._cursorX = undefined; + this._logService.debug(`TextAreaSyncAddon#updateCommandAndCursor: neither commandStartX nor commandRightPromptStartX`); } } } From bb2964c3be29ab03734ff649ef30dd6fe7430752 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 22 Aug 2023 14:38:45 -0700 Subject: [PATCH 212/223] change names --- .../accessibility/browser/textAreaSyncAddon.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts index c4171b11aa2..666f27389d9 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts @@ -25,7 +25,7 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { activate(terminal: Terminal): void { this._terminal = terminal; if (this._accessibilityService.isScreenReaderOptimized()) { - this._setListeners(); + this._registerSyncListeners(); } } @@ -38,28 +38,28 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { this._register(this._accessibilityService.onDidChangeScreenReaderOptimized(() => { if (this._accessibilityService.isScreenReaderOptimized()) { this._syncTextArea(); - this._setListeners(); + this._registerSyncListeners(); } else { this._listeners.clear(); } })); } - private _setListeners(): void { + private _registerSyncListeners(): void { if (this._accessibilityService.isScreenReaderOptimized() && this._terminal?.textarea) { this._listeners.value = new DisposableStore(); this._listeners.value.add(this._terminal.onCursorMove(() => this._syncTextArea())); + this._listeners.value.add(this._terminal.onData(() => this._syncTextArea())); this._listeners.value.add(addDisposableListener(this._terminal.textarea, 'focus', () => this._syncTextArea())); - this._listeners.value.add(this._terminal.onData((e) => this._syncTextArea())); } } @debounce(50) private _syncTextArea(): void { - this._logService.debug('TextAreaSyncAddon#refreshTextArea'); + this._logService.debug('TextAreaSyncAddon#syncTextArea'); const textArea = this._terminal?.textarea; if (!textArea) { - this._logService.debug(`TextAreaSyncAddon#refreshTextArea: no textarea`); + this._logService.debug(`TextAreaSyncAddon#syncTextArea: no textarea`); return; } @@ -67,16 +67,16 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { if (this._currentCommand !== textArea.value) { textArea.value = this._currentCommand || ''; - this._logService.debug(`TextAreaSyncAddon#refreshTextArea: text changed to "${this._currentCommand}"`); + this._logService.debug(`TextAreaSyncAddon#syncTextArea: text changed to "${this._currentCommand}"`); } else if (!this._currentCommand) { textArea.value = ''; - this._logService.debug(`TextAreaSyncAddon#refreshTextArea: text cleared`); + this._logService.debug(`TextAreaSyncAddon#syncTextArea: text cleared`); } if (this._cursorX !== textArea.selectionStart) { textArea.selectionStart = this._cursorX ?? 0; textArea.selectionEnd = this._cursorX ?? 0; - this._logService.debug(`TextAreaSyncAddon#refreshTextArea: selection start/end changed to ${this._cursorX}`); + this._logService.debug(`TextAreaSyncAddon#syncTextArea: selection start/end changed to ${this._cursorX}`); } } From 870d659ffd7abc1ba47233888b82fdd180591db5 Mon Sep 17 00:00:00 2001 From: hsfzxjy Date: Wed, 23 Aug 2023 06:24:53 +0800 Subject: [PATCH 213/223] Fix nested list style in Markdown preview (#190936) --- .../markdown-language-features/media/markdown.css | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/extensions/markdown-language-features/media/markdown.css b/extensions/markdown-language-features/media/markdown.css index 939eafc442b..dba6176ffda 100644 --- a/extensions/markdown-language-features/media/markdown.css +++ b/extensions/markdown-language-features/media/markdown.css @@ -106,10 +106,10 @@ sup { line-height: 0; } -ul ul, -ul ol, -ol ul, -ol ol { +ul ul:first-child, +ul ol:first-child, +ol ul:first-child, +ol ol:first-child { margin-bottom: 0; } @@ -138,6 +138,10 @@ p { margin-bottom: 16px; } +li p { + margin-bottom: 0.7em; +} + ul, ol { margin-bottom: 0.7em; From 27907b6b7daf86ed03ea69b0bac745256a4c22b2 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 23 Aug 2023 00:27:49 +0200 Subject: [PATCH 214/223] Debt: remove no more needed EOL notifications for unsupported OS (fix #190680) (#191004) --- src/vs/workbench/electron-sandbox/window.ts | 137 ++++---------------- 1 file changed, 27 insertions(+), 110 deletions(-) diff --git a/src/vs/workbench/electron-sandbox/window.ts b/src/vs/workbench/electron-sandbox/window.ts index 5b625c9c856..1efcf6ba71f 100644 --- a/src/vs/workbench/electron-sandbox/window.ts +++ b/src/vs/workbench/electron-sandbox/window.ts @@ -723,118 +723,35 @@ export class NativeWindow extends Disposable { } } - // Windows 7/8/8.1 warning - if (isWindows) { - const version = this.environmentService.os.release.split('.'); - const majorVersion = version[0]; - const minorVersion = version[1]; - const eolReleases = new Map>([ - ['6', new Map([ - ['1', 'Windows 7 / Windows Server 2008 R2'], - ['2', 'Windows 8 / Windows Server 2012'], - ['3', 'Windows 8.1 / Windows Server 2012 R2'], - ])], - ]); + // Windows 32-bit warning + if (isWindows && this.environmentService.os.arch === 'ia32') { + const message = localize('windows32eolmessage', "{0} on Windows 32-bit will soon stop receiving updates. Consider upgrading to the 64-bit build.", this.productService.nameLong); + const actions = [{ + label: localize('windowseolBannerLearnMore', "Learn More"), + href: 'https://aka.ms/vscode-faq-old-windows' + }]; - // Refs https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-osversioninfoa - if (eolReleases.get(majorVersion)?.has(minorVersion)) { - const message = localize('windowseolmessage', "{0} on {1} will soon stop receiving updates. Consider upgrading your windows version.", this.productService.nameLong, eolReleases.get(majorVersion)?.get(minorVersion)); - const actions = [{ - label: localize('windowseolBannerLearnMore', "Learn More"), - href: 'https://aka.ms/vscode-faq-old-windows' - }]; + this.bannerService.show({ + id: 'windows32eol.banner', + message, + ariaLabel: localize('windowseolarialabel', "{0}. Use navigation keys to access banner actions.", message), + actions, + icon: Codicon.warning + }); - this.bannerService.show({ - id: 'windowseol.banner', - message, - ariaLabel: localize('windowseolarialabel', "{0}. Use navigation keys to access banner actions.", message), - actions, - icon: Codicon.warning - }); - - this.notificationService.prompt( - Severity.Warning, - message, - [{ - label: localize('learnMore', "Learn More"), - run: () => this.openerService.open(URI.parse('https://aka.ms/vscode-faq-old-windows')) - }], - { - neverShowAgain: { id: 'windowseol', isSecondary: true, scope: NeverShowAgainScope.APPLICATION }, - priority: NotificationPriority.URGENT, - sticky: true - } - ); - } - - else if (this.environmentService.os.arch === 'ia32') { - const message = localize('windows32eolmessage', "{0} on Windows 32-bit will soon stop receiving updates. Consider upgrading to the 64-bit build.", this.productService.nameLong); - const actions = [{ - label: localize('windowseolBannerLearnMore', "Learn More"), - href: 'https://aka.ms/vscode-faq-old-windows' - }]; - - this.bannerService.show({ - id: 'windows32eol.banner', - message, - ariaLabel: localize('windowseolarialabel', "{0}. Use navigation keys to access banner actions.", message), - actions, - icon: Codicon.warning - }); - - this.notificationService.prompt( - Severity.Warning, - message, - [{ - label: localize('learnMore', "Learn More"), - run: () => this.openerService.open(URI.parse('https://aka.ms/vscode-faq-old-windows')) - }], - { - neverShowAgain: { id: 'windows32eol', isSecondary: true, scope: NeverShowAgainScope.APPLICATION }, - priority: NotificationPriority.URGENT, - sticky: true - } - ); - } - } - - // MacOS 10.11 and 10.12 warning - if (isMacintosh) { - const majorVersion = this.environmentService.os.release.split('.')[0]; - const eolReleases = new Map([ - ['15', 'OS X El Capitan'], - ['16', 'macOS Sierra'], - ]); - // Refs https://en.wikipedia.org/wiki/Darwin_%28operating_system%29#Release_history - if (eolReleases.has(majorVersion)) { - const message = localize('macoseolmessage', "{0} on {1} will soon stop receiving updates. Consider upgrading your macOS version.", this.productService.nameLong, eolReleases.get(majorVersion)); - const actions = [{ - label: localize('macoseolBannerLearnMore', "Learn More"), - href: 'https://aka.ms/vscode-faq-old-macOS' - }]; - - this.bannerService.show({ - id: 'macoseol.banner', - message, - ariaLabel: localize('macoseolarialabel', "{0}. Use navigation keys to access banner actions.", message), - actions, - icon: Codicon.warning - }); - - this.notificationService.prompt( - Severity.Warning, - message, - [{ - label: localize('learnMore', "Learn More"), - run: () => this.openerService.open(URI.parse('https://aka.ms/vscode-faq-old-macOS')) - }], - { - neverShowAgain: { id: 'macoseol', isSecondary: true, scope: NeverShowAgainScope.APPLICATION }, - priority: NotificationPriority.URGENT, - sticky: true - } - ); - } + this.notificationService.prompt( + Severity.Warning, + message, + [{ + label: localize('learnMore', "Learn More"), + run: () => this.openerService.open(URI.parse('https://aka.ms/vscode-faq-old-windows')) + }], + { + neverShowAgain: { id: 'windows32eol', isSecondary: true, scope: NeverShowAgainScope.APPLICATION }, + priority: NotificationPriority.URGENT, + sticky: true + } + ); } // Slow shell environment progress indicator From 08b4e30259d6f0372106615e19096740fec3f20c Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Tue, 22 Aug 2023 15:53:56 -0700 Subject: [PATCH 215/223] =?UTF-8?q?=F0=9F=86=99distro=20(#191033)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b878c99812b..425451a5559 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.82.0", - "distro": "505b0336f71abdfa7d1ed0dbaafa20c812fa58e7", + "distro": "1591281180fd2cd18935e6847131d2d4213b7b69", "author": { "name": "Microsoft Corporation" }, From f7ceb0697bbbceb2e2286309ca96cf083e88d7cf Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 23 Aug 2023 01:27:31 +0200 Subject: [PATCH 216/223] Ports view no longer shows privacy column/toggle (#191037) Fixes #190920 --- .../api/browser/mainThreadTunnelService.ts | 2 +- .../contrib/remote/browser/tunnelView.ts | 57 ++++++++++++++----- 2 files changed, 43 insertions(+), 16 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadTunnelService.ts b/src/vs/workbench/api/browser/mainThreadTunnelService.ts index d41e861572b..678285a11c5 100644 --- a/src/vs/workbench/api/browser/mainThreadTunnelService.ts +++ b/src/vs/workbench/api/browser/mainThreadTunnelService.ts @@ -195,10 +195,10 @@ export class MainThreadTunnelService extends Disposable implements MainThreadTun }); } }; - this.tunnelService.setTunnelProvider(tunnelProvider); if (features) { this.tunnelService.setTunnelFeatures(features); } + this.tunnelService.setTunnelProvider(tunnelProvider); // At this point we clearly want the ports view/features since we have a tunnel factory this.contextKeyService.createKey(forwardedPortsViewEnabled.key, true); } diff --git a/src/vs/workbench/contrib/remote/browser/tunnelView.ts b/src/vs/workbench/contrib/remote/browser/tunnelView.ts index ac01011049a..2289a2ece04 100644 --- a/src/vs/workbench/contrib/remote/browser/tunnelView.ts +++ b/src/vs/workbench/contrib/remote/browser/tunnelView.ts @@ -744,7 +744,9 @@ export class TunnelPanel extends ViewPane { static readonly ID = TUNNEL_VIEW_ID; static readonly TITLE = nls.localize('remote.tunnel', "Ports"); + private panelContainer: HTMLElement | undefined; private table!: WorkbenchTable; + private tableDisposables: DisposableStore = this._register(new DisposableStore()); private tunnelTypeContext: IContextKey; private tunnelCloseableContext: IContextKey; private tunnelPrivacyContext: IContextKey; @@ -783,7 +785,7 @@ export class TunnelPanel extends ViewPane { this.tunnelCloseableContext = TunnelCloseableContextKey.bindTo(contextKeyService); this.tunnelPrivacyContext = TunnelPrivacyContextKey.bindTo(contextKeyService); this.tunnelPrivacyEnabledContext = TunnelPrivacyEnabledContextKey.bindTo(contextKeyService); - this.tunnelPrivacyEnabledContext.set(tunnelService.privacyOptions.length !== 0); + this.tunnelPrivacyEnabledContext.set(tunnelService.canChangePrivacy); this.tunnelProtocolContext = TunnelProtocolContextKey.bindTo(contextKeyService); this.tunnelViewFocusContext = TunnelViewFocusContextKey.bindTo(contextKeyService); this.tunnelViewSelectionContext = TunnelViewSelectionContextKey.bindTo(contextKeyService); @@ -806,6 +808,15 @@ export class TunnelPanel extends ViewPane { })); this.registerPrivacyActions(); + this._register(Event.once(this.tunnelService.onAddedTunnelProvider)(() => { + if (this.tunnelPrivacyEnabledContext.get() === false) { + this.tunnelPrivacyEnabledContext.set(tunnelService.canChangePrivacy); + updateActions(); + this.registerPrivacyActions(); + this.createTable(); + this.table.layout(this.height, this.width); + } + })); } private registerPrivacyActions() { @@ -827,11 +838,15 @@ export class TunnelPanel extends ViewPane { return this.remoteExplorerService.tunnelModel.forwarded.size + this.remoteExplorerService.tunnelModel.detected.size; } - protected override renderBody(container: HTMLElement): void { - super.renderBody(container); + private createTable(): void { + if (!this.panelContainer) { + return; + } + this.tableDisposables.clear(); - const panelContainer = dom.append(container, dom.$('.tree-explorer-viewlet-tree-view')); - const widgetContainer = dom.append(panelContainer, dom.$('.customview-tree')); + dom.clearNode(this.panelContainer); + + const widgetContainer = dom.append(this.panelContainer, dom.$('.customview-tree')); widgetContainer.classList.add('ports-view'); widgetContainer.classList.add('file-icon-themable-tree', 'show-file-icons'); @@ -874,18 +889,19 @@ export class TunnelPanel extends ViewPane { const actionRunner: ActionRunner = new ActionRunner(); actionBarRenderer.actionRunner = actionRunner; - this._register(this.table.onContextMenu(e => this.onContextMenu(e, actionRunner))); - this._register(this.table.onMouseDblClick(e => this.onMouseDblClick(e))); - this._register(this.table.onDidChangeFocus(e => this.onFocusChanged(e))); - this._register(this.table.onDidChangeSelection(e => this.onSelectionChanged(e))); - this._register(this.table.onDidFocus(() => this.tunnelViewFocusContext.set(true))); - this._register(this.table.onDidBlur(() => this.tunnelViewFocusContext.set(false))); + this.tableDisposables.add(this.table); + this.tableDisposables.add(this.table.onContextMenu(e => this.onContextMenu(e, actionRunner))); + this.tableDisposables.add(this.table.onMouseDblClick(e => this.onMouseDblClick(e))); + this.tableDisposables.add(this.table.onDidChangeFocus(e => this.onFocusChanged(e))); + this.tableDisposables.add(this.table.onDidChangeSelection(e => this.onSelectionChanged(e))); + this.tableDisposables.add(this.table.onDidFocus(() => this.tunnelViewFocusContext.set(true))); + this.tableDisposables.add(this.table.onDidBlur(() => this.tunnelViewFocusContext.set(false))); const rerender = () => this.table.splice(0, Number.POSITIVE_INFINITY, this.viewModel.all); rerender(); let lastPortCount = this.portCount; - this._register(Event.debounce(this.viewModel.onForwardedPortsChanged, (_last, e) => e, 50)(() => { + this.tableDisposables.add(Event.debounce(this.viewModel.onForwardedPortsChanged, (_last, e) => e, 50)(() => { const newPortCount = this.portCount; if (((lastPortCount === 0) || (newPortCount === 0)) && (lastPortCount !== newPortCount)) { this._onDidChangeViewWelcomeState.fire(); @@ -894,7 +910,7 @@ export class TunnelPanel extends ViewPane { rerender(); })); - this._register(this.table.onMouseClick(e => { + this.tableDisposables.add(this.table.onMouseClick(e => { if (this.hasOpenLinkModifier(e.browserEvent)) { const selection = this.table.getSelectedElements(); if ((selection.length === 0) || @@ -904,7 +920,7 @@ export class TunnelPanel extends ViewPane { } })); - this._register(this.table.onDidOpen(e => { + this.tableDisposables.add(this.table.onDidOpen(e => { if (!e.element || (e.element.tunnelType !== TunnelType.Forwarded)) { return; } @@ -913,7 +929,7 @@ export class TunnelPanel extends ViewPane { } })); - this._register(this.remoteExplorerService.onDidChangeEditable(e => { + this.tableDisposables.add(this.remoteExplorerService.onDidChangeEditable(e => { this.isEditing = !!this.remoteExplorerService.getEditableData(e?.tunnel, e?.editId); this._onDidChangeViewWelcomeState.fire(); @@ -938,6 +954,13 @@ export class TunnelPanel extends ViewPane { })); } + protected override renderBody(container: HTMLElement): void { + super.renderBody(container); + + this.panelContainer = dom.append(container, dom.$('.tree-explorer-viewlet-tree-view')); + this.createTable(); + } + override shouldShowWelcome(): boolean { return this.viewModel.isEmpty() && !this.isEditing; } @@ -1048,7 +1071,11 @@ export class TunnelPanel extends ViewPane { } } + private height = 0; + private width = 0; protected override layoutBody(height: number, width: number): void { + this.height = height; + this.width = width; super.layoutBody(height, width); this.table.layout(height, width); } From 1fe8359ed0ef9c52bd6986565da97c395607a130 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 22 Aug 2023 17:29:51 -0700 Subject: [PATCH 217/223] cli: implement 'server of server' for a local web server (#191014) Closes https://github.com/microsoft/vscode/issues/168492 This implements @aeschli's 'server server' concept in a new `code serve-web` command. Command line args are similar to the standalone web server. The first time a user hits that page, the latest version of the VS Code web server will be downloaded and run. Thanks to Martin's previous PRs, all resources the page requests are prefixed with `/`. The latest release version is cached, but when the page is loaded again and there's a new release, a the new server version will be downloaded and started up. Behind the scenes the servers all listen on named pipes/sockets and the CLI acts as a proxy server to those sockets. Servers without connections for an hour will be shut down automatically. --- .vscode/shared.code-snippets | 2 +- cli/Cargo.lock | 4 +- cli/Cargo.toml | 2 +- cli/src/bin/code/main.rs | 6 +- cli/src/commands.rs | 1 + cli/src/commands/args.rs | 31 + cli/src/commands/serve_web.rs | 617 ++++++++++++++++++ cli/src/self_update.rs | 12 +- cli/src/state.rs | 6 + cli/src/tunnels/legal.rs | 19 +- cli/src/update_service.rs | 16 +- cli/src/util/errors.rs | 45 +- cli/src/util/sync.rs | 2 +- src/vs/code/node/cli.ts | 50 +- src/vs/platform/environment/common/argv.ts | 12 +- src/vs/platform/environment/node/argv.ts | 11 + .../platform/environment/node/argvHelper.ts | 6 +- 17 files changed, 753 insertions(+), 89 deletions(-) create mode 100644 cli/src/commands/serve_web.rs diff --git a/.vscode/shared.code-snippets b/.vscode/shared.code-snippets index fb3df23dd42..f473425b76f 100644 --- a/.vscode/shared.code-snippets +++ b/.vscode/shared.code-snippets @@ -6,7 +6,7 @@ // Placeholders with the same ids are connected. // Example: "MSFT Copyright Header": { - "scope": "javascript,typescript,css", + "scope": "javascript,typescript,css,rust", "prefix": [ "header", "stub", diff --git a/cli/Cargo.lock b/cli/Cargo.lock index f7c60045c12..a67cc7cf3bd 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -983,9 +983,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index a25bee1f2ce..18f18069c1f 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -37,7 +37,7 @@ libc = "0.2.144" tunnels = { git = "https://github.com/microsoft/dev-tunnels", rev = "2621784a9ad72aa39500372391332a14bad581a3", default-features = false, features = ["connections"] } keyring = { version = "2.0.3", default-features = false, features = ["linux-secret-service-rt-tokio-crypto-openssl"] } dialoguer = "0.10.4" -hyper = "0.14.26" +hyper = { version = "0.14.26", features = ["server", "http1", "runtime"] } indicatif = "0.17.4" tempfile = "3.5.0" clap_lex = "0.5.0" diff --git a/cli/src/bin/code/main.rs b/cli/src/bin/code/main.rs index 8c32ee14d89..b104976b9ab 100644 --- a/cli/src/bin/code/main.rs +++ b/cli/src/bin/code/main.rs @@ -8,7 +8,7 @@ use std::process::Command; use clap::Parser; use cli::{ - commands::{args, tunnels, update, version, CommandContext}, + commands::{args, serve_web, tunnels, update, version, CommandContext}, constants::get_default_user_agent, desktop, log, state::LauncherPaths, @@ -99,6 +99,10 @@ async fn main() -> Result<(), std::convert::Infallible> { tunnels::command_shell(context!(), cs_args).await } + Some(args::Commands::ServeWeb(sw_args)) => { + serve_web::serve_web(context!(), sw_args).await + } + Some(args::Commands::Tunnel(tunnel_args)) => match tunnel_args.subcommand { Some(args::TunnelSubcommand::Prune) => tunnels::prune(context!()).await, Some(args::TunnelSubcommand::Unregister) => tunnels::unregister(context!()).await, diff --git a/cli/src/commands.rs b/cli/src/commands.rs index 754729f2c04..d10a52ad774 100644 --- a/cli/src/commands.rs +++ b/cli/src/commands.rs @@ -9,4 +9,5 @@ pub mod args; pub mod tunnels; pub mod update; pub mod version; +pub mod serve_web; pub use context::CommandContext; diff --git a/cli/src/commands/args.rs b/cli/src/commands/args.rs index 9caee09ed64..cce01c52fd9 100644 --- a/cli/src/commands/args.rs +++ b/cli/src/commands/args.rs @@ -172,11 +172,42 @@ pub enum Commands { /// Changes the version of the editor you're using. Version(VersionArgs), + /// Runs a local web version of VS Code. + ServeWeb(ServeWebArgs), + /// Runs the control server on process stdin/stdout #[clap(hide = true)] CommandShell(CommandShellArgs), } +#[derive(Args, Debug, Clone)] +pub struct ServeWebArgs { + /// Host to listen on, defaults to 'localhost' + #[clap(long)] + pub host: Option, + /// Port to listen on. If 0 is passed a random free port is picked. + #[clap(long, default_value_t = 8000)] + pub port: u16, + /// A secret that must be included with all requests. + #[clap(long)] + pub connection_token: Option, + /// Run without a connection token. Only use this if the connection is secured by other means. + #[clap(long)] + pub without_connection_token: bool, + /// If set, the user accepts the server license terms and the server will be started without a user prompt. + #[clap(long)] + pub accept_server_license_terms: bool, + /// Specifies the directory that server data is kept in. + #[clap(long)] + pub server_data_dir: Option, + /// Specifies the directory that user data is kept in. Can be used to open multiple distinct instances of Code. + #[clap(long)] + pub user_data_dir: Option, + /// Set the root path for extensions. + #[clap(long)] + pub extensions_dir: Option, +} + #[derive(Args, Debug, Clone)] pub struct CommandShellArgs { /// Listen on a socket instead of stdin/stdout. diff --git a/cli/src/commands/serve_web.rs b/cli/src/commands/serve_web.rs new file mode 100644 index 00000000000..b2bf4d431e4 --- /dev/null +++ b/cli/src/commands/serve_web.rs @@ -0,0 +1,617 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +use std::collections::HashMap; +use std::convert::Infallible; +use std::net::{IpAddr, Ipv4Addr, SocketAddr}; +use std::path::PathBuf; +use std::sync::{Arc, Mutex}; +use std::time::{Duration, Instant}; + +use hyper::service::{make_service_fn, service_fn}; +use hyper::{Body, Request, Response, Server}; +use tokio::io::{AsyncBufReadExt, BufReader}; +use tokio::pin; +use tokio::process::Command; + +use crate::async_pipe::{get_socket_name, get_socket_rw_stream, AsyncPipe}; +use crate::constants::VSCODE_CLI_QUALITY; +use crate::download_cache::DownloadCache; +use crate::log; +use crate::options::Quality; +use crate::update_service::{ + unzip_downloaded_release, Platform, Release, TargetKind, UpdateService, +}; +use crate::util::errors::AnyError; +use crate::util::http::{self, ReqwestSimpleHttp}; +use crate::util::io::SilentCopyProgress; +use crate::util::sync::{new_barrier, Barrier, BarrierOpener}; +use crate::{ + tunnels::legal, + util::{errors::CodeError, prereqs::PreReqChecker}, +}; + +use super::{args::ServeWebArgs, CommandContext}; + +/// Length of a commit hash, for validation +const COMMIT_HASH_LEN: usize = 40; +/// Number of seconds where, if there's no connections to a VS Code server, +/// the server is shut down. +const SERVER_IDLE_TIMEOUT_SECS: u64 = 60 * 60; +/// Number of seconds in which the server times out when there is a connection +/// (should be large enough to basically never happen) +const SERVER_ACTIVE_TIMEOUT_SECS: u64 = SERVER_IDLE_TIMEOUT_SECS * 24 * 30 * 12; +/// How long to cache the "latest" version we get from the update service. +const RELEASE_CACHE_SECS: u64 = 60 * 60; + +/// Implements the vscode "server of servers". Clients who go to the URI get +/// served the latest version of the VS Code server whenever they load the +/// page. The VS Code server prefixes all assets and connections it loads with +/// its version string, so existing clients can continue to get served even +/// while new clients get new VS Code Server versions. +pub async fn serve_web(ctx: CommandContext, mut args: ServeWebArgs) -> Result { + legal::require_consent(&ctx.paths, args.accept_server_license_terms)?; + let mut addr: SocketAddr = match &args.host { + Some(h) => h.parse().map_err(CodeError::InvalidHostAddress)?, + None => SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0), + }; + addr.set_port(args.port); + + let platform: crate::update_service::Platform = PreReqChecker::new().verify().await?; + + if !args.without_connection_token { + // Ensure there's a defined connection token, since if multiple server versions + // are excuted, they will need to have a single shared token. + let connection_token = args + .connection_token + .clone() + .unwrap_or_else(|| uuid::Uuid::new_v4().to_string()); + ctx.log.result(format!( + "Web UI available at http://{}?tkn={}", + addr, connection_token, + )); + args.connection_token = Some(connection_token); + } else { + ctx.log + .result(format!("Web UI available at http://{}", addr)); + args.connection_token = None; + } + + let cm = ConnectionManager::new(&ctx, platform, args); + let make_svc = make_service_fn(move |_conn| { + let cm = cm.clone(); + let log = ctx.log.clone(); + let service = service_fn(move |req| handle(cm.clone(), log.clone(), req)); + async move { Ok::<_, Infallible>(service) } + }); + + let server = Server::bind(&addr).serve(make_svc); + + server.await.map_err(CodeError::CouldNotListenOnInterface)?; + + Ok(0) +} + +/// Handler function for an inbound request +async fn handle( + cm: Arc, + log: log::Logger, + req: Request, +) -> Result, Infallible> { + let release = if let Some((r, _)) = get_release_from_path(req.uri().path(), cm.platform) { + r + } else { + match cm.get_latest_release().await { + Ok(r) => r, + Err(e) => { + error!(log, "error getting latest version: {}", e); + return Ok(response::code_err(e)); + } + } + }; + + Ok(match cm.get_connection(release).await { + Ok(rw) => { + if req.headers().contains_key(hyper::header::UPGRADE) { + forward_ws_req_to_server(cm.log.clone(), rw, req).await + } else { + forward_http_req_to_server(rw, req).await + } + } + Err(CodeError::ServerNotYetDownloaded) => response::wait_for_download(), + Err(e) => response::code_err(e), + }) +} + +/// Gets the release info from the VS Code path prefix, which is in the +/// format `/-/...` +fn get_release_from_path(path: &str, platform: Platform) -> Option<(Release, String)> { + if !path.starts_with('/') { + return None; // paths must start with '/' + } + + let path = &path[1..]; + let i = path.find('/').unwrap_or(path.len()); + let quality_commit_sep = path.get(..i).and_then(|p| p.find('-'))?; + + let (quality_commit, remaining) = path.split_at(i); + let (quality, commit) = quality_commit.split_at(quality_commit_sep); + + if !is_commit_hash(commit) { + return None; + } + + Some(( + Release { + // remember to trim off the leading '/' which is now part of th quality + quality: Quality::try_from(quality).ok()?, + commit: commit.to_string(), + platform, + target: TargetKind::Web, + name: "".to_string(), + }, + remaining.to_string(), + )) +} + +/// Proxies the standard HTTP request to the async pipe, returning the piped response +async fn forward_http_req_to_server( + (rw, handle): (AsyncPipe, ConnectionHandle), + req: Request, +) -> Response { + let (mut request_sender, connection) = + match hyper::client::conn::Builder::new().handshake(rw).await { + Ok(r) => r, + Err(e) => return response::connection_err(e), + }; + + tokio::spawn(connection); + + let res = request_sender + .send_request(req) + .await + .unwrap_or_else(response::connection_err); + + // technically, we should buffer the body into memory since it may not be + // read at this point, but because the keepalive time is very large + // there's not going to be responses that take hours to send and x + // cause us to kill the server before the response is sent + drop(handle); + + res +} + +/// Proxies the websocket request to the async pipe +async fn forward_ws_req_to_server( + log: log::Logger, + (rw, handle): (AsyncPipe, ConnectionHandle), + mut req: Request, +) -> Response { + // splicing of client and servers inspired by https://github.com/hyperium/hyper/blob/fece9f7f50431cf9533cfe7106b53a77b48db699/examples/upgrades.rs + let (mut request_sender, connection) = + match hyper::client::conn::Builder::new().handshake(rw).await { + Ok(r) => r, + Err(e) => return response::connection_err(e), + }; + + tokio::spawn(connection); + + let mut proxied_req = Request::builder().uri(req.uri()); + for (k, v) in req.headers() { + proxied_req = proxied_req.header(k, v); + } + + let mut res = request_sender + .send_request(proxied_req.body(Body::empty()).unwrap()) + .await + .unwrap_or_else(response::connection_err); + + let mut proxied_res = Response::new(Body::empty()); + *proxied_res.status_mut() = res.status(); + for (k, v) in res.headers() { + proxied_res.headers_mut().insert(k, v.clone()); + } + + // only start upgrade at this point in case the server decides to deny socket + if res.status() == hyper::StatusCode::SWITCHING_PROTOCOLS { + tokio::spawn(async move { + let (s_req, s_res) = + tokio::join!(hyper::upgrade::on(&mut req), hyper::upgrade::on(&mut res)); + + match (s_req, s_res) { + (Err(e1), Err(e2)) => debug!( + log, + "client ({}) and server ({}) websocket upgrade failed", e1, e2 + ), + (Err(e1), _) => debug!(log, "client ({}) websocket upgrade failed", e1), + (_, Err(e2)) => debug!(log, "server ({}) websocket upgrade failed", e2), + (Ok(mut s_req), Ok(mut s_res)) => { + trace!(log, "websocket upgrade succeeded"); + let r = tokio::io::copy_bidirectional(&mut s_req, &mut s_res).await; + trace!(log, "websocket closed (error: {:?})", r.err()); + } + } + + drop(handle); + }); + } + + proxied_res +} + +/// Returns whether the string looks like a commit hash. +fn is_commit_hash(s: &str) -> bool { + s.len() == COMMIT_HASH_LEN && s.chars().all(|c| c.is_ascii_hexdigit()) +} + +/// Module holding original responses the CLI's server makes. +mod response { + use const_format::concatcp; + + use crate::constants::QUALITYLESS_SERVER_NAME; + + use super::*; + + pub fn connection_err(err: hyper::Error) -> Response { + Response::builder() + .status(503) + .body(Body::from(format!("Error connecting to server: {:?}", err))) + .unwrap() + } + + pub fn code_err(err: CodeError) -> Response { + Response::builder() + .status(500) + .body(Body::from(format!("Error serving request: {}", err))) + .unwrap() + } + + pub fn wait_for_download() -> Response { + Response::builder() + .status(202) + .header("Content-Type", "text/html") // todo: get latest + .body(Body::from(concatcp!("The latest version of the ", QUALITYLESS_SERVER_NAME, " is downloading, please wait a moment...", ))) + .unwrap() + } +} + +/// Handle returned when getting a stream to the server, used to refcount +/// connections to a server so it can be disposed when there are no more clients. +struct ConnectionHandle { + client_counter: Arc>, +} + +impl ConnectionHandle { + pub fn new(client_counter: Arc>) -> Self { + client_counter.send_modify(|v| { + *v += 1; + }); + Self { client_counter } + } +} + +impl Drop for ConnectionHandle { + fn drop(&mut self) { + self.client_counter.send_modify(|v| { + *v -= 1; + }); + } +} + +type StartData = (PathBuf, Arc>); + +/// State stored in the ConnectionManager for each server version. +struct VersionState { + downloaded: bool, + socket_path: Barrier>, +} + +type ConnectionStateMap = Arc>>; + +/// Manages the connections to running web UI instances. Multiple web servers +/// can run concurrently, with routing based on the URL path. +struct ConnectionManager { + pub platform: Platform, + pub log: log::Logger, + args: ServeWebArgs, + /// Cache where servers are stored + cache: DownloadCache, + /// Mapping of (Quality, Commit) to the state each server is in + state: ConnectionStateMap, + /// Update service instance + update_service: UpdateService, + /// Cache of the latest released version, storing the time we checked as well + latest_version: tokio::sync::Mutex>, +} + +fn key_for_release(release: &Release) -> (Quality, String) { + (release.quality, release.commit.clone()) +} + +impl ConnectionManager { + pub fn new(ctx: &CommandContext, platform: Platform, args: ServeWebArgs) -> Arc { + Arc::new(Self { + platform, + args, + log: ctx.log.clone(), + cache: DownloadCache::new(ctx.paths.web_server_storage()), + update_service: UpdateService::new( + ctx.log.clone(), + Arc::new(ReqwestSimpleHttp::with_client(ctx.http.clone())), + ), + state: ConnectionStateMap::default(), + latest_version: tokio::sync::Mutex::default(), + }) + } + + /// Gets a connection to a server version + pub async fn get_connection( + &self, + release: Release, + ) -> Result<(AsyncPipe, ConnectionHandle), CodeError> { + // todo@connor4312: there is likely some performance benefit to + // implementing a 'keepalive' for these connections. + let (path, counter) = self.get_version_data(release).await?; + let handle = ConnectionHandle::new(counter); + let rw = get_socket_rw_stream(&path).await?; + Ok((rw, handle)) + } + + /// Gets the latest release for the CLI quality, caching its result for some + /// time to allow for fast loads. + pub async fn get_latest_release(&self) -> Result { + let mut latest = self.latest_version.lock().await; + let now = Instant::now(); + if let Some((checked_at, release)) = &*latest { + if checked_at.elapsed() < Duration::from_secs(RELEASE_CACHE_SECS) { + return Ok(release.clone()); + } + } + + let quality = VSCODE_CLI_QUALITY + .ok_or_else(|| CodeError::UpdatesNotConfigured("no configured quality")) + .and_then(|q| { + Quality::try_from(q).map_err(|_| CodeError::UpdatesNotConfigured("unknown quality")) + })?; + + let release = self + .update_service + .get_latest_commit(self.platform, TargetKind::Web, quality) + .await + .map_err(|e| CodeError::UpdateCheckFailed(e.to_string())); + + // If the update service is unavailable and we have stale data, use that + if let (Err(e), Some((_, previous))) = (&release, &*latest) { + warning!(self.log, "error getting latest release, using stale: {}", e); + return Ok(previous.clone()); + } + + let release = release?; + debug!(self.log, "refreshed latest release: {}", release); + *latest = Some((now, release.clone())); + + Ok(release) + } + + /// Gets the StartData for the a version of the VS Code server, triggering + /// download/start if necessary. It returns `CodeError::ServerNotYetDownloaded` + /// while the server is downloading, which is used to have a refresh loop on the page. + async fn get_version_data(&self, release: Release) -> Result { + self.get_version_data_inner(release)? + .wait() + .await + .unwrap() + .map_err(CodeError::ServerDownloadError) + } + + fn get_version_data_inner( + &self, + release: Release, + ) -> Result>, CodeError> { + let mut state = self.state.lock().unwrap(); + let key = key_for_release(&release); + if let Some(s) = state.get_mut(&key) { + if !s.downloaded { + if s.socket_path.is_open() { + s.downloaded = true; + } else { + return Err(CodeError::ServerNotYetDownloaded); + } + } + + return Ok(s.socket_path.clone()); + } + + let (socket_path, opener) = new_barrier(); + let state_map_dup = self.state.clone(); + let args = StartArgs { + args: self.args.clone(), + log: self.log.clone(), + opener, + release, + }; + + if let Some(p) = self.cache.exists(&args.release.commit) { + state.insert( + key.clone(), + VersionState { + socket_path: socket_path.clone(), + downloaded: true, + }, + ); + + tokio::spawn(async move { + Self::start_version(args, p).await; + state_map_dup.lock().unwrap().remove(&key); + }); + Ok(socket_path) + } else { + state.insert( + key.clone(), + VersionState { + socket_path, + downloaded: false, + }, + ); + let update_service = self.update_service.clone(); + let cache = self.cache.clone(); + tokio::spawn(async move { + Self::download_version(args, update_service.clone(), cache.clone()).await; + state_map_dup.lock().unwrap().remove(&key); + }); + Err(CodeError::ServerNotYetDownloaded) + } + } + + /// Downloads a server version into the cache and starts it. + async fn download_version( + args: StartArgs, + update_service: UpdateService, + cache: DownloadCache, + ) { + let release_for_fut = args.release.clone(); + let log_for_fut = args.log.clone(); + let dir_fut = cache.create(&args.release.commit, |target_dir| async move { + info!(log_for_fut, "Downloading server {}", release_for_fut.commit); + let tmpdir = tempfile::tempdir().unwrap(); + let response = update_service.get_download_stream(&release_for_fut).await?; + + let name = response.url_path_basename().unwrap(); + let archive_path = tmpdir.path().join(name); + http::download_into_file( + &archive_path, + log_for_fut.get_download_logger("Downloading server:"), + response, + ) + .await?; + unzip_downloaded_release(&archive_path, &target_dir, SilentCopyProgress())?; + Ok(()) + }); + + match dir_fut.await { + Err(e) => args.opener.open(Err(e.to_string())), + Ok(dir) => Self::start_version(args, dir).await, + } + } + + /// Starts a downloaded server that can be found in the given `path`. + async fn start_version(args: StartArgs, path: PathBuf) { + info!(args.log, "Starting server {}", args.release.commit); + + let executable = path + .join("bin") + .join(args.release.quality.server_entrypoint()); + let socket_path = get_socket_name(); + + #[cfg(not(windows))] + let mut cmd = Command::new(&executable); + #[cfg(windows)] + let mut cmd = { + let mut cmd = Command::new("cmd"); + cmd.arg("/Q"); + cmd.arg("/C"); + cmd.arg(&executable); + cmd + }; + + cmd.stdin(std::process::Stdio::null()); + cmd.stderr(std::process::Stdio::piped()); + cmd.stdout(std::process::Stdio::piped()); + cmd.arg("--socket-path"); + cmd.arg(&socket_path); + + // License agreement already checked by the `server_web` function. + cmd.args(["--accept-server-license-terms"]); + + if let Some(a) = &args.args.server_data_dir { + cmd.arg("--server-data-dir"); + cmd.arg(a); + } + if let Some(a) = &args.args.user_data_dir { + cmd.arg("--user-data-dir"); + cmd.arg(a); + } + if let Some(a) = &args.args.extensions_dir { + cmd.arg("--extensions-dir"); + cmd.arg(a); + } + if args.args.without_connection_token { + cmd.arg("--without-connection-token"); + } + if let Some(ct) = &args.args.connection_token { + cmd.arg("--connection-token"); + cmd.arg(ct); + } + + // removed, otherwise the workbench will not be usable when running the CLI from sources. + cmd.env_remove("VSCODE_DEV"); + + let mut child = match cmd.spawn() { + Ok(c) => c, + Err(e) => { + args.opener.open(Err(e.to_string())); + return; + } + }; + + let (mut stdout, mut stderr) = ( + BufReader::new(child.stdout.take().unwrap()).lines(), + BufReader::new(child.stderr.take().unwrap()).lines(), + ); + + // wrapped option to prove that we only use this once in the loop + let (counter_tx, mut counter_rx) = tokio::sync::watch::channel(0); + let mut opener = Some((args.opener, socket_path, Arc::new(counter_tx))); + let commit_prefix = &args.release.commit[..7]; + let kill_timer = tokio::time::sleep(Duration::from_secs(SERVER_IDLE_TIMEOUT_SECS)); + pin!(kill_timer); + + loop { + tokio::select! { + Ok(Some(l)) = stdout.next_line() => { + info!(args.log, "[{} stdout]: {}", commit_prefix, l); + + if l.contains("Server bound to") { + if let Some((opener, path, counter_tx)) = opener.take() { + opener.open(Ok((path, counter_tx))); + } + } + } + Ok(Some(l)) = stderr.next_line() => { + info!(args.log, "[{} stderr]: {}", commit_prefix, l); + }, + n = counter_rx.changed() => { + kill_timer.as_mut().reset(match n { + // err means that the record was dropped + Err(_) => tokio::time::Instant::now(), + Ok(_) => { + if *counter_rx.borrow() == 0 { + tokio::time::Instant::now() + Duration::from_secs(SERVER_IDLE_TIMEOUT_SECS) + } else { + tokio::time::Instant::now() + Duration::from_secs(SERVER_ACTIVE_TIMEOUT_SECS) + } + } + }); + } + _ = &mut kill_timer => { + info!(args.log, "[{} process]: idle timeout reached, ending", commit_prefix); + let _ = child.kill().await; + break; + } + e = child.wait() => { + info!(args.log, "[{} process]: exited: {:?}", commit_prefix, e); + break; + } + } + } + } +} + +struct StartArgs { + log: log::Logger, + args: ServeWebArgs, + release: Release, + opener: BarrierOpener>, +} diff --git a/cli/src/self_update.rs b/cli/src/self_update.rs index 2e95719a3b9..4a878dc5447 100644 --- a/cli/src/self_update.rs +++ b/cli/src/self_update.rs @@ -11,7 +11,7 @@ use crate::{ options::Quality, update_service::{unzip_downloaded_release, Platform, Release, TargetKind, UpdateService}, util::{ - errors::{wrap, AnyError, CorruptDownload, UpdatesNotConfigured}, + errors::{wrap, AnyError, CodeError, CorruptDownload}, http, io::{ReportCopyProgress, SilentCopyProgress}, }, @@ -27,14 +27,16 @@ pub struct SelfUpdate<'a> { impl<'a> SelfUpdate<'a> { pub fn new(update_service: &'a UpdateService) -> Result { let commit = VSCODE_CLI_COMMIT - .ok_or_else(|| UpdatesNotConfigured("unknown build commit".to_string()))?; + .ok_or_else(|| CodeError::UpdatesNotConfigured("unknown build commit"))?; let quality = VSCODE_CLI_QUALITY - .ok_or_else(|| UpdatesNotConfigured("no configured quality".to_string())) - .and_then(|q| Quality::try_from(q).map_err(UpdatesNotConfigured))?; + .ok_or_else(|| CodeError::UpdatesNotConfigured("no configured quality")) + .and_then(|q| { + Quality::try_from(q).map_err(|_| CodeError::UpdatesNotConfigured("unknown quality")) + })?; let platform = Platform::env_default().ok_or_else(|| { - UpdatesNotConfigured("Unknown platform, please report this error".to_string()) + CodeError::UpdatesNotConfigured("Unknown platform, please report this error") })?; Ok(Self { diff --git a/cli/src/state.rs b/cli/src/state.rs index 1b1ff343da5..8815e2df40c 100644 --- a/cli/src/state.rs +++ b/cli/src/state.rs @@ -212,4 +212,10 @@ impl LauncherPaths { ) }) } + + /// Suggested path for web server storage + pub fn web_server_storage(&self) -> PathBuf { + self.root.join("serve-web") + } + } diff --git a/cli/src/tunnels/legal.rs b/cli/src/tunnels/legal.rs index 676ccb7da55..35316af4fde 100644 --- a/cli/src/tunnels/legal.rs +++ b/cli/src/tunnels/legal.rs @@ -2,9 +2,9 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -use crate::constants::{IS_INTERACTIVE_CLI, PRODUCT_NAME_LONG}; +use crate::constants::IS_INTERACTIVE_CLI; use crate::state::{LauncherPaths, PersistedState}; -use crate::util::errors::{AnyError, MissingLegalConsent}; +use crate::util::errors::{AnyError, CodeError}; use crate::util::input::prompt_yn; use lazy_static::lazy_static; use serde::{Deserialize, Serialize}; @@ -46,23 +46,14 @@ pub fn require_consent( if accept_server_license_terms { load.consented = Some(true); } else if !*IS_INTERACTIVE_CLI { - return Err(MissingLegalConsent( - "Run this command again with --accept-server-license-terms to indicate your agreement." - .to_string(), - ) - .into()); + return Err(CodeError::NeedsInteractiveLegalConsent.into()); } else { match prompt_yn(prompt) { Ok(true) => { load.consented = Some(true); } - Ok(false) => { - return Err(AnyError::from(MissingLegalConsent(format!( - "Sorry you cannot use {} CLI without accepting the terms.", - PRODUCT_NAME_LONG - )))) - } - Err(e) => return Err(AnyError::from(MissingLegalConsent(e.to_string()))), + Ok(false) => return Err(CodeError::DeniedLegalConset.into()), + Err(_) => return Err(CodeError::NeedsInteractiveLegalConsent.into()), } } diff --git a/cli/src/update_service.rs b/cli/src/update_service.rs index b03d8ea5963..d218e4a1333 100644 --- a/cli/src/update_service.rs +++ b/cli/src/update_service.rs @@ -11,7 +11,7 @@ use crate::{ constants::VSCODE_CLI_UPDATE_ENDPOINT, debug, log, options, spanf, util::{ - errors::{AnyError, CodeError, UpdatesNotConfigured, WrappedError}, + errors::{AnyError, CodeError, WrappedError}, http::{BoxedHttp, SimpleResponse}, io::ReportCopyProgress, tar, zipper, @@ -19,6 +19,7 @@ use crate::{ }; /// Implementation of the VS Code Update service for use in the CLI. +#[derive(Clone)] pub struct UpdateService { client: BoxedHttp, log: log::Logger, @@ -54,6 +55,10 @@ fn quality_download_segment(quality: options::Quality) -> &'static str { } } +fn get_update_endpoint() -> Result<&'static str, CodeError> { + VSCODE_CLI_UPDATE_ENDPOINT.ok_or_else(|| CodeError::UpdatesNotConfigured("no service url")) +} + impl UpdateService { pub fn new(log: log::Logger, http: BoxedHttp) -> Self { UpdateService { client: http, log } @@ -66,8 +71,7 @@ impl UpdateService { quality: options::Quality, version: &str, ) -> Result { - let update_endpoint = - VSCODE_CLI_UPDATE_ENDPOINT.ok_or_else(UpdatesNotConfigured::no_url)?; + let update_endpoint = get_update_endpoint()?; let download_segment = target .download_segment(platform) .ok_or_else(|| CodeError::UnsupportedPlatform(platform.to_string()))?; @@ -108,8 +112,7 @@ impl UpdateService { target: TargetKind, quality: options::Quality, ) -> Result { - let update_endpoint = - VSCODE_CLI_UPDATE_ENDPOINT.ok_or_else(UpdatesNotConfigured::no_url)?; + let update_endpoint = get_update_endpoint()?; let download_segment = target .download_segment(platform) .ok_or_else(|| CodeError::UnsupportedPlatform(platform.to_string()))?; @@ -144,8 +147,7 @@ impl UpdateService { /// Gets the download stream for the release. pub async fn get_download_stream(&self, release: &Release) -> Result { - let update_endpoint = - VSCODE_CLI_UPDATE_ENDPOINT.ok_or_else(UpdatesNotConfigured::no_url)?; + let update_endpoint = get_update_endpoint()?; let download_segment = release .target .download_segment(release.platform) diff --git a/cli/src/util/errors.rs b/cli/src/util/errors.rs index c82e14acc8b..38d9b36f54b 100644 --- a/cli/src/util/errors.rs +++ b/cli/src/util/errors.rs @@ -108,16 +108,6 @@ impl StatusError { } } -// When the user has not consented to the licensing terms in using the Launcher -#[derive(Debug)] -pub struct MissingLegalConsent(pub String); - -impl std::fmt::Display for MissingLegalConsent { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{}", self.0) - } -} - // When the provided connection token doesn't match the one used to set up the original VS Code Server // This is most likely due to a new user joining. #[derive(Debug)] @@ -313,20 +303,6 @@ impl std::fmt::Display for ServerHasClosed { } } -#[derive(Debug)] -pub struct UpdatesNotConfigured(pub String); - -impl UpdatesNotConfigured { - pub fn no_url() -> Self { - UpdatesNotConfigured("no service url".to_owned()) - } -} - -impl std::fmt::Display for UpdatesNotConfigured { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "Update service is not configured: {}", self.0) - } -} #[derive(Debug)] pub struct ServiceAlreadyRegistered(); @@ -517,10 +493,28 @@ pub enum CodeError { KeyringTimeout, #[error("no host is connected to the tunnel relay")] NoTunnelEndpoint, + #[error("could not parse `host`: {0}")] + InvalidHostAddress(std::net::AddrParseError), + #[error("could not start server on the given host/port: {0}")] + CouldNotListenOnInterface(hyper::Error), + #[error( + "Run this command again with --accept-server-license-terms to indicate your agreement." + )] + NeedsInteractiveLegalConsent, + #[error("Sorry, you cannot use this CLI without accepting the terms.")] + DeniedLegalConset, + #[error("The server is not yet downloaded, try again shortly.")] + ServerNotYetDownloaded, + #[error("An error was encountered downloading the server, please retry: {0}")] + ServerDownloadError(String), + #[error("Updates are are not available: {0}")] + UpdatesNotConfigured(&'static str), + // todo: can be specialized when update service is moved to CodeErrors + #[error("Could not check for update: {0}")] + UpdateCheckFailed(String), } makeAnyError!( - MissingLegalConsent, MismatchConnectionToken, DevTunnelError, StatusError, @@ -543,7 +537,6 @@ makeAnyError!( ServerHasClosed, ServiceAlreadyRegistered, WindowsNeedsElevation, - UpdatesNotConfigured, CorruptDownload, MissingHomeDirectory, OAuthError, diff --git a/cli/src/util/sync.rs b/cli/src/util/sync.rs index 8b653cd2d53..67c777b75ed 100644 --- a/cli/src/util/sync.rs +++ b/cli/src/util/sync.rs @@ -63,7 +63,7 @@ impl BarrierOpener { /// and is thereafter permanently closed. It can contain a value. pub fn new_barrier() -> (Barrier, BarrierOpener) where - T: Copy, + T: Clone, { let (closed_tx, closed_rx) = watch::channel(None); (Barrier(closed_rx), BarrierOpener(Arc::new(closed_tx))) diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts index 3b823317303..4770e9ef0bd 100644 --- a/src/vs/code/node/cli.ts +++ b/src/vs/code/node/cli.ts @@ -15,7 +15,7 @@ import { whenDeleted, writeFileSync } from 'vs/base/node/pfs'; import { findFreePort } from 'vs/base/node/ports'; import { watchFileContents } from 'vs/platform/files/node/watcher/nodejs/nodejsWatcherLib'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; -import { buildHelpMessage, buildVersionMessage, OPTIONS } from 'vs/platform/environment/node/argv'; +import { buildHelpMessage, buildVersionMessage, NATIVE_CLI_COMMANDS, OPTIONS } from 'vs/platform/environment/node/argv'; import { addArg, parseCLIProcessArgv } from 'vs/platform/environment/node/argvHelper'; import { getStdinFilePath, hasStdinWithoutTty, readFromStdin, stdinDataListener } from 'vs/platform/environment/node/stdin'; import { createWaitMarkerFileSync } from 'vs/platform/environment/node/wait'; @@ -51,31 +51,33 @@ export async function main(argv: string[]): Promise { return; } - if (args.tunnel) { - if (!product.tunnelApplicationName) { - console.error(`'tunnel' command not supported in ${product.applicationName}`); - return; - } - const tunnelArgs = argv.slice(argv.indexOf('tunnel') + 1); // all arguments behind `tunnel` - return new Promise((resolve, reject) => { - let tunnelProcess: ChildProcess; - const stdio: StdioOptions = ['ignore', 'pipe', 'pipe']; - if (process.env['VSCODE_DEV']) { - tunnelProcess = spawn('cargo', ['run', '--', 'tunnel', ...tunnelArgs], { cwd: join(getAppRoot(), 'cli'), stdio }); - } else { - const appPath = process.platform === 'darwin' - // ./Contents/MacOS/Electron => ./Contents/Resources/app/bin/code-tunnel-insiders - ? join(dirname(dirname(process.execPath)), 'Resources', 'app') - : dirname(process.execPath); - const tunnelCommand = join(appPath, 'bin', `${product.tunnelApplicationName}${isWindows ? '.exe' : ''}`); - tunnelProcess = spawn(tunnelCommand, ['tunnel', ...tunnelArgs], { cwd: cwd(), stdio }); + for (const subcommand of NATIVE_CLI_COMMANDS) { + if (args[subcommand]) { + if (!product.tunnelApplicationName) { + console.error(`'${subcommand}' command not supported in ${product.applicationName}`); + return; } + const tunnelArgs = argv.slice(argv.indexOf(subcommand) + 1); // all arguments behind `tunnel` + return new Promise((resolve, reject) => { + let tunnelProcess: ChildProcess; + const stdio: StdioOptions = ['ignore', 'pipe', 'pipe']; + if (process.env['VSCODE_DEV']) { + tunnelProcess = spawn('cargo', ['run', '--', subcommand, ...tunnelArgs], { cwd: join(getAppRoot(), 'cli'), stdio }); + } else { + const appPath = process.platform === 'darwin' + // ./Contents/MacOS/Electron => ./Contents/Resources/app/bin/code-tunnel-insiders + ? join(dirname(dirname(process.execPath)), 'Resources', 'app') + : dirname(process.execPath); + const tunnelCommand = join(appPath, 'bin', `${product.tunnelApplicationName}${isWindows ? '.exe' : ''}`); + tunnelProcess = spawn(tunnelCommand, [subcommand, ...tunnelArgs], { cwd: cwd(), stdio }); + } - tunnelProcess.stdout!.pipe(process.stdout); - tunnelProcess.stderr!.pipe(process.stderr); - tunnelProcess.on('exit', resolve); - tunnelProcess.on('error', reject); - }); + tunnelProcess.stdout!.pipe(process.stdout); + tunnelProcess.stderr!.pipe(process.stderr); + tunnelProcess.on('exit', resolve); + tunnelProcess.on('error', reject); + }); + } } // Help diff --git a/src/vs/platform/environment/common/argv.ts b/src/vs/platform/environment/common/argv.ts index 17476d94187..b63a262137f 100644 --- a/src/vs/platform/environment/common/argv.ts +++ b/src/vs/platform/environment/common/argv.ts @@ -3,15 +3,18 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +export interface INativeCliOptions { + 'cli-data-dir'?: string; + 'disable-telemetry'?: boolean; + 'telemetry-level'?: string; +} + /** * A list of command line arguments we support natively. */ export interface NativeParsedArgs { // subcommands - tunnel?: { - 'cli-data-dir'?: string; - 'disable-telemetry'?: boolean; - 'telemetry-level'?: string; + tunnel?: INativeCliOptions & { user: { login: { 'access-token'?: string; @@ -19,6 +22,7 @@ export interface NativeParsedArgs { }; }; }; + 'serve-web'?: INativeCliOptions; _: string[]; 'folder-uri'?: string[]; // undefined or array of 1 or more 'file-uri'?: string[]; // undefined or array of 1 or more diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index b3e423e20f9..63243fcf3a0 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -44,6 +44,8 @@ export type OptionDescriptions = { Subcommand }; +export const NATIVE_CLI_COMMANDS = ['tunnel', 'serve-web'] as const; + export const OPTIONS: OptionDescriptions> = { 'tunnel': { type: 'subcommand', @@ -66,6 +68,15 @@ export const OPTIONS: OptionDescriptions> = { } } }, + 'serve-web': { + type: 'subcommand', + description: 'Make the current machine accessible from vscode.dev or other machines through a secure tunnel', + options: { + 'cli-data-dir': { type: 'string', args: 'dir', description: localize('cliDataDir', "Directory where CLI metadata should be stored.") }, + 'disable-telemetry': { type: 'boolean' }, + 'telemetry-level': { type: 'string' }, + } + }, 'diff': { type: 'boolean', cat: 'o', alias: 'd', args: ['file', 'file'], description: localize('diff', "Compare two files with each other.") }, 'merge': { type: 'boolean', cat: 'o', alias: 'm', args: ['path1', 'path2', 'base', 'result'], description: localize('merge', "Perform a three-way merge by providing paths for two modified versions of a file, the common origin of both modified versions and the output file to save merge results.") }, diff --git a/src/vs/platform/environment/node/argvHelper.ts b/src/vs/platform/environment/node/argvHelper.ts index 74a7369225d..d8cefb6df67 100644 --- a/src/vs/platform/environment/node/argvHelper.ts +++ b/src/vs/platform/environment/node/argvHelper.ts @@ -7,7 +7,7 @@ import * as assert from 'assert'; import { IProcessEnvironment } from 'vs/base/common/platform'; import { localize } from 'vs/nls'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; -import { ErrorReporter, OPTIONS, parseArgs } from 'vs/platform/environment/node/argv'; +import { ErrorReporter, NATIVE_CLI_COMMANDS, OPTIONS, parseArgs } from 'vs/platform/environment/node/argv'; function parseAndValidate(cmdLineArgs: string[], reportWarnings: boolean): NativeParsedArgs { const onMultipleValues = (id: string, val: string) => { @@ -21,14 +21,14 @@ function parseAndValidate(cmdLineArgs: string[], reportWarnings: boolean): Nativ }; const getSubcommandReporter = (command: string) => ({ onUnknownOption: (id: string) => { - if (command !== 'tunnel') { + if (!(NATIVE_CLI_COMMANDS as readonly string[]).includes(command)) { console.warn(localize('unknownSubCommandOption', "Warning: '{0}' is not in the list of known options for subcommand '{1}'", id, command)); } }, onMultipleValues, onEmptyValue, onDeprecatedOption, - getSubcommandReporter: command !== 'tunnel' ? getSubcommandReporter : undefined + getSubcommandReporter: (NATIVE_CLI_COMMANDS as readonly string[]).includes(command) ? getSubcommandReporter : undefined }); const errorReporter: ErrorReporter = { onUnknownOption: (id) => { From 6408ba941f7e961e00eb7a4cc66745894127ab03 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 22 Aug 2023 17:50:58 +0200 Subject: [PATCH 218/223] fixes https://github.com/microsoft/vscode/issues/190961, fixes https://github.com/microsoft/vscode/issues/190959 --- .../diff/algorithms/joinSequenceDiffs.ts | 23 +++++++++++++++++++ .../advanced.expected.diff.json | 4 ++-- .../advanced.expected.diff.json | 4 ++-- .../noise-1/advanced.expected.diff.json | 4 ++-- .../ts-class/advanced.expected.diff.json | 4 ++-- .../advanced.expected.diff.json | 12 +++++----- .../ts-example1/advanced.expected.diff.json | 4 ++-- .../advanced.expected.diff.json | 4 ++-- .../ws-alignment/advanced.expected.diff.json | 4 ++-- 9 files changed, 43 insertions(+), 20 deletions(-) diff --git a/src/vs/editor/common/diff/algorithms/joinSequenceDiffs.ts b/src/vs/editor/common/diff/algorithms/joinSequenceDiffs.ts index b76c82d371b..c79d557e409 100644 --- a/src/vs/editor/common/diff/algorithms/joinSequenceDiffs.ts +++ b/src/vs/editor/common/diff/algorithms/joinSequenceDiffs.ts @@ -149,6 +149,29 @@ export function removeRandomMatches(sequence1: LinesSliceCharSequence, sequence2 diffs = result; } while (counter++ < 10 && shouldRepeat); + // Remove short suffixes/prefixes + for (let i = 0; i < diffs.length; i++) { + const cur = diffs[i]; + + let range1 = cur.seq1Range; + let range2 = cur.seq2Range; + + const fullRange1 = sequence1.extendToFullLines(cur.seq1Range); + const prefix = sequence1.getText(new OffsetRange(fullRange1.start, cur.seq1Range.start)); + if (prefix.length > 0 && prefix.trim().length <= 3 && cur.seq1Range.length + cur.seq2Range.length > 100) { + range1 = cur.seq1Range.deltaStart(-prefix.length); + range2 = cur.seq2Range.deltaStart(-prefix.length); + } + + const suffix = sequence1.getText(new OffsetRange(cur.seq1Range.endExclusive, fullRange1.endExclusive)); + if (suffix.length > 0 && (suffix.trim().length <= 3 && cur.seq1Range.length + cur.seq2Range.length > 150)) { + range1 = range1.deltaEnd(suffix.length); + range2 = range2.deltaEnd(suffix.length); + } + + diffs[i] = new SequenceDiff(range1, range2); + } + return diffs; } diff --git a/src/vs/editor/test/node/diffing/fixtures/class-replacement/advanced.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/class-replacement/advanced.expected.diff.json index 2b8f1b4ab1a..5cbd3c45666 100644 --- a/src/vs/editor/test/node/diffing/fixtures/class-replacement/advanced.expected.diff.json +++ b/src/vs/editor/test/node/diffing/fixtures/class-replacement/advanced.expected.diff.json @@ -13,8 +13,8 @@ "modifiedRange": "[29,37)", "innerChanges": [ { - "originalRange": "[29,1 -> 60,48]", - "modifiedRange": "[29,1 -> 36,66]" + "originalRange": "[29,1 -> 61,1]", + "modifiedRange": "[29,1 -> 37,1]" } ] }, diff --git a/src/vs/editor/test/node/diffing/fixtures/method-splitting/advanced.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/method-splitting/advanced.expected.diff.json index 8ac0682b508..b1de25a5907 100644 --- a/src/vs/editor/test/node/diffing/fixtures/method-splitting/advanced.expected.diff.json +++ b/src/vs/editor/test/node/diffing/fixtures/method-splitting/advanced.expected.diff.json @@ -13,8 +13,8 @@ "modifiedRange": "[6,11)", "innerChanges": [ { - "originalRange": "[6,2 -> 11,49]", - "modifiedRange": "[6,2 -> 6,91]" + "originalRange": "[6,1 -> 11,49]", + "modifiedRange": "[6,1 -> 6,91]" }, { "originalRange": "[12,76 -> 12,76]", diff --git a/src/vs/editor/test/node/diffing/fixtures/noise-1/advanced.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/noise-1/advanced.expected.diff.json index 5a679831447..20190a83798 100644 --- a/src/vs/editor/test/node/diffing/fixtures/noise-1/advanced.expected.diff.json +++ b/src/vs/editor/test/node/diffing/fixtures/noise-1/advanced.expected.diff.json @@ -21,8 +21,8 @@ "modifiedRange": "[53,6 -> 53,45]" }, { - "originalRange": "[52,77 -> 55,53]", - "modifiedRange": "[53,98 -> 65,66]" + "originalRange": "[52,77 -> 56,1]", + "modifiedRange": "[53,98 -> 66,1]" } ] } diff --git a/src/vs/editor/test/node/diffing/fixtures/ts-class/advanced.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/ts-class/advanced.expected.diff.json index 92f5e20707f..0b00ea98beb 100644 --- a/src/vs/editor/test/node/diffing/fixtures/ts-class/advanced.expected.diff.json +++ b/src/vs/editor/test/node/diffing/fixtures/ts-class/advanced.expected.diff.json @@ -47,8 +47,8 @@ "modifiedRange": "[8,12)", "innerChanges": [ { - "originalRange": "[11,10 -> 20,25]", - "modifiedRange": "[8,10 -> 11,51]" + "originalRange": "[11,10 -> 21,1]", + "modifiedRange": "[8,10 -> 12,1]" } ] }, diff --git a/src/vs/editor/test/node/diffing/fixtures/ts-confusing-2/advanced.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/ts-confusing-2/advanced.expected.diff.json index f6817defb49..3835a5ceb3c 100644 --- a/src/vs/editor/test/node/diffing/fixtures/ts-confusing-2/advanced.expected.diff.json +++ b/src/vs/editor/test/node/diffing/fixtures/ts-confusing-2/advanced.expected.diff.json @@ -43,8 +43,8 @@ "modifiedRange": "[14,18)", "innerChanges": [ { - "originalRange": "[14,120 -> 14,211]", - "modifiedRange": "[14,120 -> 17,1]" + "originalRange": "[14,120 -> 15,1]", + "modifiedRange": "[14,120 -> 18,1]" } ] }, @@ -65,8 +65,8 @@ "modifiedRange": "[21,18 -> 21,43]" }, { - "originalRange": "[17,71 -> 23,4]", - "modifiedRange": "[21,74 -> 21,81]" + "originalRange": "[17,71 -> 24,1]", + "modifiedRange": "[21,74 -> 22,1]" } ] }, @@ -93,8 +93,8 @@ "modifiedRange": "[26,38)", "innerChanges": [ { - "originalRange": "[28,1 -> 30,31]", - "modifiedRange": "[26,1 -> 37,9]" + "originalRange": "[28,1 -> 31,1]", + "modifiedRange": "[26,1 -> 38,1]" } ] } diff --git a/src/vs/editor/test/node/diffing/fixtures/ts-example1/advanced.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/ts-example1/advanced.expected.diff.json index d8eac8523e6..c7cd88a788b 100644 --- a/src/vs/editor/test/node/diffing/fixtures/ts-example1/advanced.expected.diff.json +++ b/src/vs/editor/test/node/diffing/fixtures/ts-example1/advanced.expected.diff.json @@ -27,8 +27,8 @@ "modifiedRange": "[17,80)", "innerChanges": [ { - "originalRange": "[13,10 -> 14,43]", - "modifiedRange": "[17,10 -> 79,2]" + "originalRange": "[13,10 -> 15,1]", + "modifiedRange": "[17,10 -> 80,1]" } ] } diff --git a/src/vs/editor/test/node/diffing/fixtures/ts-too-much-minimization/advanced.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/ts-too-much-minimization/advanced.expected.diff.json index aa65f823a85..7a52e0e84aa 100644 --- a/src/vs/editor/test/node/diffing/fixtures/ts-too-much-minimization/advanced.expected.diff.json +++ b/src/vs/editor/test/node/diffing/fixtures/ts-too-much-minimization/advanced.expected.diff.json @@ -13,8 +13,8 @@ "modifiedRange": "[9,15)", "innerChanges": [ { - "originalRange": "[9,124 -> 9,124]", - "modifiedRange": "[9,124 -> 14,143]" + "originalRange": "[9,124 -> 10,1]", + "modifiedRange": "[9,124 -> 15,1]" } ] } diff --git a/src/vs/editor/test/node/diffing/fixtures/ws-alignment/advanced.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/ws-alignment/advanced.expected.diff.json index 85a9a2fe928..66e738ab041 100644 --- a/src/vs/editor/test/node/diffing/fixtures/ws-alignment/advanced.expected.diff.json +++ b/src/vs/editor/test/node/diffing/fixtures/ws-alignment/advanced.expected.diff.json @@ -23,8 +23,8 @@ "modifiedRange": "[7,18)", "innerChanges": [ { - "originalRange": "[7,5 -> 12,17]", - "modifiedRange": "[7,5 -> 16,7]" + "originalRange": "[7,1 -> 13,1]", + "modifiedRange": "[7,1 -> 17,1]" }, { "originalRange": "[13,6 -> 13,11]", From 2cfdb5302a4e23cf51903b4e7df1875a5d611b6d Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 23 Aug 2023 11:50:46 +0200 Subject: [PATCH 219/223] fix #189138 (#191051) * fix #189138 * feedback --- .../services/configuration/browser/configuration.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/services/configuration/browser/configuration.ts b/src/vs/workbench/services/configuration/browser/configuration.ts index 8f942efdd0e..591bddbe576 100644 --- a/src/vs/workbench/services/configuration/browser/configuration.ts +++ b/src/vs/workbench/services/configuration/browser/configuration.ts @@ -259,8 +259,8 @@ class FileServiceBasedConfiguration extends Disposable { const resolveContents = async (resources: URI[]): Promise<(string | undefined)[]> => { return Promise.all(resources.map(async resource => { try { - const content = (await this.fileService.readFile(resource)).value.toString(); - return content; + const content = await this.fileService.readFile(resource, { atomic: true }); + return content.value.toString(); } catch (error) { this.logService.trace(`Error while resolving configuration file '${resource.toString()}': ${errors.getErrorMessage(error)}`); if ((error).fileOperationResult !== FileOperationResult.FILE_NOT_FOUND @@ -494,7 +494,7 @@ class FileServiceBasedRemoteUserConfiguration extends Disposable { } async resolveContent(): Promise { - const content = await this.fileService.readFile(this.configurationResource); + const content = await this.fileService.readFile(this.configurationResource, { atomic: true }); return content.value.toString(); } @@ -764,7 +764,7 @@ class FileServiceBasedWorkspaceConfiguration extends Disposable { } async resolveContent(workspaceIdentifier: IWorkspaceIdentifier): Promise { - const content = await this.fileService.readFile(workspaceIdentifier.configPath); + const content = await this.fileService.readFile(workspaceIdentifier.configPath, { atomic: true }); return content.value.toString(); } From 3e9a48f4888bd51b524e40de18efe255242bff09 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 23 Aug 2023 02:52:31 -0700 Subject: [PATCH 220/223] eng: reapply test leak fixes (#190890) * eng: reapply test leak fixes Re-applies #190623 which I merged before everyone had a chance to review. Alex and Ben, you were the two people whose code this touches and didn't review it in the original PR. * :lipstick: * fix merge * better handling of editor listeners --------- Co-authored-by: Benjamin Pasero --- src/vs/base/browser/ui/grid/gridview.ts | 1 + src/vs/base/browser/ui/splitview/splitview.ts | 4 +-- src/vs/base/browser/ui/toolbar/toolbar.ts | 2 +- .../diffEditorWidget2/diffEditorEditors.ts | 4 +-- .../suggest/browser/suggestController.ts | 2 +- src/vs/platform/actions/browser/buttonbar.ts | 2 +- src/vs/workbench/browser/dnd.ts | 5 ++-- .../browser/parts/editor/editorGroupView.ts | 2 +- .../parts/editor/editorGroupWatermark.ts | 2 +- .../browser/parts/editor/tabsTitleControl.ts | 10 +++---- .../common/editor/editorGroupModel.ts | 11 ++++++++ .../browser/editors/textFileEditorTracker.ts | 2 +- .../browser/textFileEditorTracker.test.ts | 3 +++ .../browser/inlineChatController.ts | 3 ++- .../inlineChat/browser/inlineChatSession.ts | 2 ++ .../inlineChat/browser/inlineChatWidget.ts | 8 +++--- .../test/browser/inlineChatController.test.ts | 26 +++++-------------- .../markers/test/browser/markersModel.test.ts | 3 +++ .../editor/browser/codeEditorService.ts | 4 +-- .../services/editor/browser/editorService.ts | 6 ++--- .../browser/browserTextFileService.ts | 2 +- .../textfile/browser/textFileService.ts | 4 ++- .../common/textFileSaveParticipant.ts | 6 ++++- .../test/browser/textFileEditorModel.test.ts | 6 ++--- .../storedFileWorkingCopySaveParticipant.ts | 2 ++ .../workingCopyFileOperationParticipant.ts | 2 ++ .../test/browser/workbenchTestServices.ts | 13 +++++----- 27 files changed, 78 insertions(+), 59 deletions(-) diff --git a/src/vs/base/browser/ui/grid/gridview.ts b/src/vs/base/browser/ui/grid/gridview.ts index c89c6a7a063..9445c64286a 100644 --- a/src/vs/base/browser/ui/grid/gridview.ts +++ b/src/vs/base/browser/ui/grid/gridview.ts @@ -705,6 +705,7 @@ class BranchNode implements ISplitView, IDisposable { this.splitviewSashResetDisposable.dispose(); this.childrenSashResetDisposable.dispose(); this.childrenChangeDisposable.dispose(); + this.onDidScrollDisposable.dispose(); this.splitview.dispose(); } } diff --git a/src/vs/base/browser/ui/splitview/splitview.ts b/src/vs/base/browser/ui/splitview/splitview.ts index b822db43751..28f18d42537 100644 --- a/src/vs/base/browser/ui/splitview/splitview.ts +++ b/src/vs/base/browser/ui/splitview/splitview.ts @@ -565,11 +565,11 @@ export class SplitView extends Disposable { this.sashContainer = append(this.el, $('.sash-container')); this.viewContainer = $('.split-view-container'); - this.scrollable = new Scrollable({ + this.scrollable = this._register(new Scrollable({ forceIntegerValues: true, smoothScrollDuration: 125, scheduleAtNextAnimationFrame - }); + })); this.scrollableElement = this._register(new SmoothScrollableElement(this.viewContainer, { vertical: this.orientation === Orientation.VERTICAL ? (options.scrollbarVisibility ?? ScrollbarVisibility.Auto) : ScrollbarVisibility.Hidden, horizontal: this.orientation === Orientation.HORIZONTAL ? (options.scrollbarVisibility ?? ScrollbarVisibility.Auto) : ScrollbarVisibility.Hidden diff --git a/src/vs/base/browser/ui/toolbar/toolbar.ts b/src/vs/base/browser/ui/toolbar/toolbar.ts index 1a6089b5958..bcb90361b41 100644 --- a/src/vs/base/browser/ui/toolbar/toolbar.ts +++ b/src/vs/base/browser/ui/toolbar/toolbar.ts @@ -52,7 +52,7 @@ export class ToolBar extends Disposable { private _onDidChangeDropdownVisibility = this._register(new EventMultiplexer()); readonly onDidChangeDropdownVisibility = this._onDidChangeDropdownVisibility.event; - private disposables = new DisposableStore(); + private disposables = this._register(new DisposableStore()); constructor(container: HTMLElement, contextMenuProvider: IContextMenuProvider, options: IToolBarOptions = { orientation: ActionsOrientation.HORIZONTAL }) { super(); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts index cce99a94271..ee764c81aed 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts @@ -35,8 +35,8 @@ export class DiffEditorEditors extends Disposable { ) { super(); - this.original = this._createLeftHandSideEditor(_options.editorOptions.get(), codeEditorWidgetOptions.originalEditor || {}); - this.modified = this._createRightHandSideEditor(_options.editorOptions.get(), codeEditorWidgetOptions.modifiedEditor || {}); + this.original = this._register(this._createLeftHandSideEditor(_options.editorOptions.get(), codeEditorWidgetOptions.originalEditor || {})); + this.modified = this._register(this._createRightHandSideEditor(_options.editorOptions.get(), codeEditorWidgetOptions.modifiedEditor || {})); this._register(autorunHandleChanges({ createEmptyChangeSummary: () => ({} as IDiffEditorConstructionOptions), diff --git a/src/vs/editor/contrib/suggest/browser/suggestController.ts b/src/vs/editor/contrib/suggest/browser/suggestController.ts index 887fb23cb2d..6450e989bce 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestController.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestController.ts @@ -143,7 +143,7 @@ export class SuggestController implements IEditorContribution { // context key: update insert/replace mode const ctxInsertMode = SuggestContext.InsertMode.bindTo(_contextKeyService); ctxInsertMode.set(editor.getOption(EditorOption.suggest).insertMode); - this.model.onDidTrigger(() => ctxInsertMode.set(editor.getOption(EditorOption.suggest).insertMode)); + this._toDispose.add(this.model.onDidTrigger(() => ctxInsertMode.set(editor.getOption(EditorOption.suggest).insertMode))); this.widget = this._toDispose.add(new IdleValue(() => { diff --git a/src/vs/platform/actions/browser/buttonbar.ts b/src/vs/platform/actions/browser/buttonbar.ts index cb6ab4c31df..4965d6bb5cd 100644 --- a/src/vs/platform/actions/browser/buttonbar.ts +++ b/src/vs/platform/actions/browser/buttonbar.ts @@ -55,7 +55,7 @@ export class MenuWorkbenchButtonBar extends ButtonBar { 'workbenchActionExecuted', { id: e.action.id, from: options.telemetrySource! } ); - }, this._store); + }, undefined, this._store); } const conifgProvider: IButtonConfigProvider = options?.buttonConfigProvider ?? (() => ({ showLabel: true })); diff --git a/src/vs/workbench/browser/dnd.ts b/src/vs/workbench/browser/dnd.ts index c196ac2132e..52afb8083d8 100644 --- a/src/vs/workbench/browser/dnd.ts +++ b/src/vs/workbench/browser/dnd.ts @@ -12,7 +12,7 @@ import { ITreeDragOverReaction } from 'vs/base/browser/ui/tree/tree'; import { coalesce } from 'vs/base/common/arrays'; import { UriList, VSDataTransfer } from 'vs/base/common/dataTransfer'; import { Emitter } from 'vs/base/common/event'; -import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, IDisposable, markAsSingleton } from 'vs/base/common/lifecycle'; import { stringify } from 'vs/base/common/marshalling'; import { Mimes } from 'vs/base/common/mime'; import { FileAccess, Schemas } from 'vs/base/common/network'; @@ -427,6 +427,7 @@ export class CompositeDragAndDropObserver extends Disposable { static get INSTANCE(): CompositeDragAndDropObserver { if (!CompositeDragAndDropObserver.instance) { CompositeDragAndDropObserver.instance = new CompositeDragAndDropObserver(); + markAsSingleton(CompositeDragAndDropObserver.instance); } return CompositeDragAndDropObserver.instance; @@ -523,7 +524,7 @@ export class CompositeDragAndDropObserver extends Disposable { if (callbacks.onDragEnd) { this.onDragEnd.event(e => { callbacks.onDragEnd!(e); - }); + }, this, disposableStore); } return this._register(disposableStore); diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index c80fa632d3e..01b92a4b7e2 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -244,7 +244,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { const groupEditorsCountContext = EditorGroupEditorsCountContext.bindTo(this.scopedContextKeyService); const groupLockedContext = ActiveEditorGroupLockedContext.bindTo(this.scopedContextKeyService); - const activeEditorListener = new MutableDisposable(); + const activeEditorListener = this._register(new MutableDisposable()); const observeActiveEditor = () => { activeEditorListener.clear(); diff --git a/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts b/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts index 55d6506abab..ff8a2778c7c 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts @@ -90,7 +90,7 @@ export class EditorGroupWatermark extends Disposable { } private registerListeners(): void { - this.lifecycleService.onDidShutdown(() => this.dispose()); + this._register(this.lifecycleService.onDidShutdown(() => this.dispose())); this._register(this.configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('workbench.tips.enabled')) { diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index d712b1a146b..3272b3e4839 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -184,7 +184,7 @@ export class TabsTitleControl extends TitleControl { this.updateTabSizing(false); // Tabs Scrollbar - this.tabsScrollbar = this._register(this.createTabsScrollbar(this.tabsContainer)); + this.tabsScrollbar = this.createTabsScrollbar(this.tabsContainer); this.tabsAndActionsContainer.appendChild(this.tabsScrollbar.getDomNode()); // Tabs Container listeners @@ -206,19 +206,19 @@ export class TabsTitleControl extends TitleControl { } private createTabsScrollbar(scrollable: HTMLElement): ScrollableElement { - const tabsScrollbar = new ScrollableElement(scrollable, { + const tabsScrollbar = this._register(new ScrollableElement(scrollable, { horizontal: ScrollbarVisibility.Auto, horizontalScrollbarSize: this.getTabsScrollbarSizing(), vertical: ScrollbarVisibility.Hidden, scrollYToX: true, useShadows: false - }); + })); - tabsScrollbar.onScroll(e => { + this._register(tabsScrollbar.onScroll(e => { if (e.scrollLeftChanged) { scrollable.scrollLeft = e.scrollLeft; } - }); + })); return tabsScrollbar; } diff --git a/src/vs/workbench/common/editor/editorGroupModel.ts b/src/vs/workbench/common/editor/editorGroupModel.ts index 7fc1ae6cbc0..8e3c7b7a811 100644 --- a/src/vs/workbench/common/editor/editorGroupModel.ts +++ b/src/vs/workbench/common/editor/editorGroupModel.ts @@ -180,6 +180,8 @@ export class EditorGroupModel extends Disposable { private editors: EditorInput[] = []; private mru: EditorInput[] = []; + private readonly editorListeners = new Set(); + private locked = false; private preview: EditorInput | null = null; // editor in preview state @@ -405,6 +407,7 @@ export class EditorGroupModel extends Disposable { private registerEditorListeners(editor: EditorInput): void { const listeners = new DisposableStore(); + this.editorListeners.add(listeners); // Re-emit disposal of editor input as our own event listeners.add(Event.once(editor.onWillDispose)(() => { @@ -453,6 +456,7 @@ export class EditorGroupModel extends Disposable { listeners.add(this.onDidModelChange(event => { if (event.kind === GroupModelChangeKind.EDITOR_CLOSE && event.editor?.matches(editor)) { dispose(listeners); + this.editorListeners.delete(listeners); } })); } @@ -1077,4 +1081,11 @@ export class EditorGroupModel extends Disposable { return this._id; } + + override dispose(): void { + dispose(Array.from(this.editorListeners)); + this.editorListeners.clear(); + + super.dispose(); + } } diff --git a/src/vs/workbench/contrib/files/browser/editors/textFileEditorTracker.ts b/src/vs/workbench/contrib/files/browser/editors/textFileEditorTracker.ts index fb876ba670a..afa94056cd5 100644 --- a/src/vs/workbench/contrib/files/browser/editors/textFileEditorTracker.ts +++ b/src/vs/workbench/contrib/files/browser/editors/textFileEditorTracker.ts @@ -47,7 +47,7 @@ export class TextFileEditorTracker extends Disposable implements IWorkbenchContr this._register(this.hostService.onDidChangeFocus(hasFocus => hasFocus ? this.reloadVisibleTextFileEditors() : undefined)); // Lifecycle - this.lifecycleService.onDidShutdown(() => this.dispose()); + this._register(this.lifecycleService.onDidShutdown(() => this.dispose())); } //#region Text File: Ensure every dirty text and untitled file is opened in an editor diff --git a/src/vs/workbench/contrib/files/test/browser/textFileEditorTracker.test.ts b/src/vs/workbench/contrib/files/test/browser/textFileEditorTracker.test.ts index bc69b29a381..b0afe7cc554 100644 --- a/src/vs/workbench/contrib/files/test/browser/textFileEditorTracker.test.ts +++ b/src/vs/workbench/contrib/files/test/browser/textFileEditorTracker.test.ts @@ -77,6 +77,7 @@ suite('Files - TextFileEditorTracker', () => { instantiationService.stub(IWorkspaceTrustRequestService, new TestWorkspaceTrustRequestService(false)); const editorService: EditorService = instantiationService.createInstance(EditorService); + disposables.add(editorService); instantiationService.stub(IEditorService, editorService); const accessor = instantiationService.createInstance(TestServiceAccessor); @@ -93,6 +94,7 @@ suite('Files - TextFileEditorTracker', () => { const resource = toResource.call(this, '/path/index.txt'); const model = await accessor.textFileService.files.resolve(resource) as IResolvedTextFileEditorModel; + disposables.add(model); model.textEditorModel.setValue('Super Good'); assert.strictEqual(snapshotToString(model.createSnapshot()!), 'Super Good'); @@ -141,6 +143,7 @@ suite('Files - TextFileEditorTracker', () => { } const model = await accessor.textFileService.files.resolve(resource) as IResolvedTextFileEditorModel; + disposables.add(model); model.textEditorModel.setValue('Super Good'); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index 104830cc852..f91de7e3ee8 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -100,7 +100,7 @@ export class InlineChatController implements IEditorContribution { private _messages = this._store.add(new Emitter()); - private readonly _sessionStore: DisposableStore = new DisposableStore(); + private readonly _sessionStore: DisposableStore = this._store.add(new DisposableStore()); private readonly _stashedSession: MutableDisposable = this._store.add(new MutableDisposable()); private _activeSession?: Session; private _strategy?: EditModeStrategy; @@ -146,6 +146,7 @@ export class InlineChatController implements IEditorContribution { } dispose(): void { + this._strategy?.dispose(); this._stashedSession.clear(); this.finishExistingSession(); this._store.dispose(); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts index 434aea1f4d2..fcbb998dc79 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts @@ -385,6 +385,8 @@ export interface IInlineChatSessionService { // recordings(): readonly Recording[]; + + dispose(): void; } type SessionData = { diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts index 31d9fa9c36b..343749ce5c6 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts @@ -230,7 +230,7 @@ export class InlineChatWidget { })); const uri = URI.from({ scheme: 'vscode', authority: 'inline-chat', path: `/inline-chat/model${InlineChatWidget._modelPool++}.txt` }); - this._inputModel = this._modelService.getModel(uri) ?? this._modelService.createModel('', null, uri); + this._inputModel = this._store.add(this._modelService.getModel(uri) ?? this._modelService.createModel('', null, uri)); this._inputEditor.setModel(this._inputModel); // --- context keys @@ -359,13 +359,13 @@ export class InlineChatWidget { this._store.add(feedbackToolbar); // preview editors - this._previewDiffEditor = new IdleValue(() => this._store.add(_instantiationService.createInstance(EmbeddedDiffEditorWidget2, this._elements.previewDiff, { + this._previewDiffEditor = this._store.add(new IdleValue(() => this._store.add(_instantiationService.createInstance(EmbeddedDiffEditorWidget2, this._elements.previewDiff, { ..._previewEditorEditorOptions, onlyShowAccessibleDiffViewer: this._accessibilityService.isScreenReaderOptimized(), - }, { modifiedEditor: codeEditorWidgetOptions, originalEditor: codeEditorWidgetOptions }, parentEditor))); + }, { modifiedEditor: codeEditorWidgetOptions, originalEditor: codeEditorWidgetOptions }, parentEditor)))); this._previewCreateTitle = this._store.add(_instantiationService.createInstance(ResourceLabel, this._elements.previewCreateTitle, { supportIcons: true })); - this._previewCreateEditor = new IdleValue(() => this._store.add(_instantiationService.createInstance(EmbeddedCodeEditorWidget, this._elements.previewCreate, _previewEditorEditorOptions, codeEditorWidgetOptions, parentEditor))); + this._previewCreateEditor = this._store.add(new IdleValue(() => this._store.add(_instantiationService.createInstance(EmbeddedCodeEditorWidget, this._elements.previewCreate, _previewEditorEditorOptions, codeEditorWidgetOptions, parentEditor)))); this._elements.message.tabIndex = 0; this._elements.message.ariaLabel = this._accessibleViewService.getOpenAriaHint(AccessibilityVerbositySettingId.InlineChat); diff --git a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts index ab5e4b27e1b..7a9beea7519 100644 --- a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts +++ b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts @@ -24,7 +24,6 @@ import { IEditorProgressService, IProgressRunner } from 'vs/platform/progress/co import { mock } from 'vs/base/test/common/mock'; import { Emitter, Event } from 'vs/base/common/event'; import { equals } from 'vs/base/common/arrays'; -import { timeout } from 'vs/base/common/async'; import { IChatAccessibilityService } from 'vs/workbench/contrib/chat/browser/chat'; import { IChatResponseViewModel } from 'vs/workbench/contrib/chat/common/chatViewModel'; import { IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; @@ -114,11 +113,11 @@ suite('InteractiveChatController', function () { }] ); - instaService = workbenchInstantiationService(undefined, store).createChild(serviceCollection); - inlineChatSessionService = instaService.get(IInlineChatSessionService); + instaService = store.add(workbenchInstantiationService(undefined, store).createChild(serviceCollection)); + inlineChatSessionService = store.add(instaService.get(IInlineChatSessionService)); - model = instaService.get(IModelService).createModel('Hello\nWorld\nHello Again\nHello World\n', null); - editor = instantiateTestCodeEditor(instaService, model); + model = store.add(instaService.get(IModelService).createModel('Hello\nWorld\nHello Again\nHello World\n', null)); + editor = store.add(instantiateTestCodeEditor(instaService, model)); store.add(inlineChatService.addProvider({ debugName: 'Unit Test', @@ -142,8 +141,6 @@ suite('InteractiveChatController', function () { }); teardown(function () { - editor.dispose(); - model.dispose(); store.clear(); ctrl?.dispose(); }); @@ -295,19 +292,8 @@ suite('InteractiveChatController', function () { wholeRange: new Range(3, 1, 3, 3) }; }, - async provideResponse(session, request) { - - // SLOW response - await timeout(50000); - - return { - type: InlineChatResponseType.EditorEdit, - id: Math.random(), - edits: [{ - range: new Range(1, 1, 1, 1), // EDIT happens outside of whole range - text: `${request.prompt}\n${request.prompt}` - }] - }; + provideResponse(session, request) { + return new Promise(() => { }); } }); store.add(d); diff --git a/src/vs/workbench/contrib/markers/test/browser/markersModel.test.ts b/src/vs/workbench/contrib/markers/test/browser/markersModel.test.ts index 9cb2c9dc650..b8334c948d6 100644 --- a/src/vs/workbench/contrib/markers/test/browser/markersModel.test.ts +++ b/src/vs/workbench/contrib/markers/test/browser/markersModel.test.ts @@ -8,6 +8,7 @@ import { URI } from 'vs/base/common/uri'; import { IMarker, MarkerSeverity, IRelatedInformation } from 'vs/platform/markers/common/markers'; import { MarkersModel, Marker, ResourceMarkers, RelatedInformation } from 'vs/workbench/contrib/markers/browser/markersModel'; import { groupBy } from 'vs/base/common/collections'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; class TestMarkersModel extends MarkersModel { @@ -27,6 +28,8 @@ class TestMarkersModel extends MarkersModel { suite('MarkersModel Test', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + test('marker ids are unique', function () { const marker1 = anErrorWithRange(3); const marker2 = anErrorWithRange(3); diff --git a/src/vs/workbench/services/editor/browser/codeEditorService.ts b/src/vs/workbench/services/editor/browser/codeEditorService.ts index 657f203312c..9930f85f58c 100644 --- a/src/vs/workbench/services/editor/browser/codeEditorService.ts +++ b/src/vs/workbench/services/editor/browser/codeEditorService.ts @@ -25,8 +25,8 @@ export class CodeEditorService extends AbstractCodeEditorService { ) { super(themeService); - this.registerCodeEditorOpenHandler(this.doOpenCodeEditor.bind(this)); - this.registerCodeEditorOpenHandler(this.doOpenCodeEditorFromDiff.bind(this)); + this._register(this.registerCodeEditorOpenHandler(this.doOpenCodeEditor.bind(this))); + this._register(this.registerCodeEditorOpenHandler(this.doOpenCodeEditorFromDiff.bind(this))); } getActiveCodeEditor(): ICodeEditor | null { diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index e241e493f65..f997b3ecfbd 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -83,9 +83,9 @@ export class EditorService extends Disposable implements EditorServiceImpl { // Editor & group changes this.editorGroupService.whenReady.then(() => this.onEditorGroupsReady()); - this.editorGroupService.onDidChangeActiveGroup(group => this.handleActiveEditorChange(group)); - this.editorGroupService.onDidAddGroup(group => this.registerGroupListeners(group as IEditorGroupView)); - this.editorsObserver.onDidMostRecentlyActiveEditorsChange(() => this._onDidMostRecentlyActiveEditorsChange.fire()); + this._register(this.editorGroupService.onDidChangeActiveGroup(group => this.handleActiveEditorChange(group))); + this._register(this.editorGroupService.onDidAddGroup(group => this.registerGroupListeners(group as IEditorGroupView))); + this._register(this.editorsObserver.onDidMostRecentlyActiveEditorsChange(() => this._onDidMostRecentlyActiveEditorsChange.fire())); // Out of workspace file watchers this._register(this.onDidVisibleEditorsChange(() => this.handleVisibleEditorsChange())); diff --git a/src/vs/workbench/services/textfile/browser/browserTextFileService.ts b/src/vs/workbench/services/textfile/browser/browserTextFileService.ts index 78957c01afd..188ca299d5f 100644 --- a/src/vs/workbench/services/textfile/browser/browserTextFileService.ts +++ b/src/vs/workbench/services/textfile/browser/browserTextFileService.ts @@ -54,7 +54,7 @@ export class BrowserTextFileService extends AbstractTextFileService { private registerListeners(): void { // Lifecycle - this.lifecycleService.onBeforeShutdown(event => event.veto(this.onBeforeShutdown(), 'veto.textFiles')); + this._register(this.lifecycleService.onBeforeShutdown(event => event.veto(this.onBeforeShutdown(), 'veto.textFiles'))); } private onBeforeShutdown(): boolean { diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index 45015baa458..f7a87fb1b4b 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -89,7 +89,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex private provideDecorations(): void { // Text file model decorations - this.decorationsService.registerDecorationsProvider(new class extends Disposable implements IDecorationsProvider { + const provider = this._register(new class extends Disposable implements IDecorationsProvider { readonly label = localize('textFileModelDecorations', "Text File Model Decorations"); @@ -161,6 +161,8 @@ export abstract class AbstractTextFileService extends Disposable implements ITex return undefined; } }(this.files)); + + this._register(this.decorationsService.registerDecorationsProvider(provider)); } //#endregin diff --git a/src/vs/workbench/services/textfile/common/textFileSaveParticipant.ts b/src/vs/workbench/services/textfile/common/textFileSaveParticipant.ts index ab3cb0a0e8a..3dc16d1a46e 100644 --- a/src/vs/workbench/services/textfile/common/textFileSaveParticipant.ts +++ b/src/vs/workbench/services/textfile/common/textFileSaveParticipant.ts @@ -60,11 +60,15 @@ export class TextFileSaveParticipant extends Disposable { model.textEditorModel?.pushStackElement(); }, () => { // user cancel - cts.dispose(true); + cts.cancel(); + }).finally(() => { + cts.dispose(); }); } override dispose(): void { this.saveParticipants.splice(0, this.saveParticipants.length); + + super.dispose(); } } diff --git a/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts b/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts index 7595d417a5f..7ca2a09bfb1 100644 --- a/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts +++ b/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts @@ -548,7 +548,7 @@ suite('Files - TextFileEditorModel', () => { test('Update Dirty', async function () { let eventCounter = 0; - const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); model.setDirty(true); assert.ok(!model.isDirty()); // needs to be resolved @@ -638,8 +638,8 @@ suite('Files - TextFileEditorModel', () => { }); test('save() and isDirty() - proper with check for mtimes', async function () { - const input1 = createFileEditorInput(instantiationService, toResource.call(this, '/path/index_async2.txt')); - const input2 = createFileEditorInput(instantiationService, toResource.call(this, '/path/index_async.txt')); + const input1 = disposables.add(createFileEditorInput(instantiationService, toResource.call(this, '/path/index_async2.txt'))); + const input2 = disposables.add(createFileEditorInput(instantiationService, toResource.call(this, '/path/index_async.txt'))); const model1 = await input1.resolve() as TextFileEditorModel; const model2 = await input2.resolve() as TextFileEditorModel; diff --git a/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopySaveParticipant.ts b/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopySaveParticipant.ts index 18be856eb47..11bddb67625 100644 --- a/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopySaveParticipant.ts +++ b/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopySaveParticipant.ts @@ -69,5 +69,7 @@ export class StoredFileWorkingCopySaveParticipant extends Disposable { override dispose(): void { this.saveParticipants.splice(0, this.saveParticipants.length); + + super.dispose(); } } diff --git a/src/vs/workbench/services/workingCopy/common/workingCopyFileOperationParticipant.ts b/src/vs/workbench/services/workingCopy/common/workingCopyFileOperationParticipant.ts index e75246b8b83..f3f4dd8f05e 100644 --- a/src/vs/workbench/services/workingCopy/common/workingCopyFileOperationParticipant.ts +++ b/src/vs/workbench/services/workingCopy/common/workingCopyFileOperationParticipant.ts @@ -46,5 +46,7 @@ export class WorkingCopyFileOperationParticipant extends Disposable { override dispose(): void { this.participants.clear(); + + super.dispose(); } } diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 0fffe75c46f..c566bc46401 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -283,15 +283,16 @@ export function workbenchInstantiationService( instantiationService.stub(IUndoRedoService, instantiationService.createInstance(UndoRedoService)); const themeService = new TestThemeService(); instantiationService.stub(IThemeService, themeService); - instantiationService.stub(ILanguageConfigurationService, new TestLanguageConfigurationService()); + instantiationService.stub(ILanguageConfigurationService, disposables.add(new TestLanguageConfigurationService())); instantiationService.stub(IModelService, disposables.add(instantiationService.createInstance(ModelService))); const fileService = overrides?.fileService ? overrides.fileService(instantiationService) : new TestFileService(); instantiationService.stub(IFileService, fileService); const uriIdentityService = new UriIdentityService(fileService); + disposables.add(uriIdentityService); instantiationService.stub(IFilesConfigurationService, disposables.add(new TestFilesConfigurationService(contextKeyService, configService, workspaceContextService, environmentService, uriIdentityService, fileService))); instantiationService.stub(IUriIdentityService, uriIdentityService); - const userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, fileService, uriIdentityService, new NullLogService())); - instantiationService.stub(IUserDataProfileService, new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService)); + const userDataProfilesService = instantiationService.stub(IUserDataProfilesService, disposables.add(new UserDataProfilesService(environmentService, fileService, uriIdentityService, new NullLogService()))); + instantiationService.stub(IUserDataProfileService, disposables.add(new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService))); instantiationService.stub(IWorkingCopyBackupService, overrides?.workingCopyBackupService ? overrides?.workingCopyBackupService(instantiationService) : new TestWorkingCopyBackupService()); instantiationService.stub(ITelemetryService, NullTelemetryService); instantiationService.stub(INotificationService, new TestNotificationService()); @@ -305,7 +306,7 @@ export function workbenchInstantiationService( instantiationService.stub(ITextFileService, overrides?.textFileService ? overrides.textFileService(instantiationService) : disposables.add(instantiationService.createInstance(TestTextFileService))); instantiationService.stub(IHostService, instantiationService.createInstance(TestHostService)); instantiationService.stub(ITextModelService, disposables.add(instantiationService.createInstance(TextModelResolverService))); - instantiationService.stub(ILoggerService, new TestLoggerService(TestEnvironmentService.logsHome)); + instantiationService.stub(ILoggerService, disposables.add(new TestLoggerService(TestEnvironmentService.logsHome))); instantiationService.stub(ILogService, new NullLogService()); const editorGroupService = new TestEditorGroupsService([new TestEditorGroupView(0)]); instantiationService.stub(IEditorGroupsService, editorGroupService); @@ -314,10 +315,10 @@ export function workbenchInstantiationService( instantiationService.stub(IEditorService, editorService); instantiationService.stub(IWorkingCopyEditorService, disposables.add(instantiationService.createInstance(WorkingCopyEditorService))); instantiationService.stub(IEditorResolverService, disposables.add(instantiationService.createInstance(EditorResolverService))); - const textEditorService = overrides?.textEditorService ? overrides.textEditorService(instantiationService) : instantiationService.createInstance(TextEditorService); + const textEditorService = overrides?.textEditorService ? overrides.textEditorService(instantiationService) : disposables.add(instantiationService.createInstance(TextEditorService)); instantiationService.stub(ITextEditorService, textEditorService); instantiationService.stub(ICodeEditorService, disposables.add(new CodeEditorService(editorService, themeService, configService))); - instantiationService.stub(IPaneCompositePartService, new TestPaneCompositeService()); + instantiationService.stub(IPaneCompositePartService, disposables.add(new TestPaneCompositeService())); instantiationService.stub(IListService, new TestListService()); const hoverService = instantiationService.stub(IHoverService, instantiationService.createInstance(TestHoverService)); instantiationService.stub(IQuickInputService, disposables.add(new QuickInputService(configService, instantiationService, keybindingService, contextKeyService, themeService, layoutService, hoverService))); From 6885d66040854a29aeee7383ca14efa181c51a74 Mon Sep 17 00:00:00 2001 From: Hans Date: Wed, 23 Aug 2023 20:19:42 +0800 Subject: [PATCH 221/223] adjust tabs and spaces consistent (#184861) --- extensions/html-language-features/.vscode/settings.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/html-language-features/.vscode/settings.json b/extensions/html-language-features/.vscode/settings.json index 569ac10cf8f..17b02728e8c 100644 --- a/extensions/html-language-features/.vscode/settings.json +++ b/extensions/html-language-features/.vscode/settings.json @@ -1,6 +1,6 @@ { "editor.insertSpaces": false, "prettier.semi": true, - "prettier.singleQuote": true, - "prettier.printWidth": 120, -} \ No newline at end of file + "prettier.singleQuote": true, + "prettier.printWidth": 120 +} From f712b1caba3ecc03f979f09ec8bfc623a0f312ac Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 23 Aug 2023 05:32:06 -0700 Subject: [PATCH 222/223] Hook up xterm.js trace log level --- .../workbench/contrib/terminal/browser/xterm/xtermTerminal.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index b5847637730..fcf7a078831 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -961,7 +961,7 @@ export function getXtermScaledDimensions(font: ITerminalFont, width: number, hei function vscodeToXtermLogLevel(logLevel: LogLevel): XtermLogLevel { switch (logLevel) { - case LogLevel.Trace: + case LogLevel.Trace: return 'trace'; case LogLevel.Debug: return 'debug'; case LogLevel.Info: return 'info'; case LogLevel.Warning: return 'warn'; From d141a1fccee14db6829359b9d8e6d0a153d10f4f Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 23 Aug 2023 05:47:07 -0700 Subject: [PATCH 223/223] xterm@5.3.0-beta.61 --- package.json | 16 +++++------ remote/package.json | 16 +++++------ remote/web/package.json | 12 ++++---- remote/web/yarn.lock | 48 +++++++++++++++---------------- remote/yarn.lock | 64 ++++++++++++++++++++--------------------- yarn.lock | 64 ++++++++++++++++++++--------------------- 6 files changed, 110 insertions(+), 110 deletions(-) diff --git a/package.json b/package.json index 425451a5559..584247e9fa4 100644 --- a/package.json +++ b/package.json @@ -95,14 +95,14 @@ "vscode-oniguruma": "1.7.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "9.0.0", - "xterm": "5.3.0-beta.58", - "xterm-addon-canvas": "0.5.0-beta.19", - "xterm-addon-image": "0.6.0-beta.11", - "xterm-addon-search": "0.13.0-beta.17", - "xterm-addon-serialize": "0.11.0-beta.17", - "xterm-addon-unicode11": "0.6.0-beta.9", - "xterm-addon-webgl": "0.16.0-beta.27", - "xterm-headless": "5.3.0-beta.58", + "xterm": "5.3.0-beta.61", + "xterm-addon-canvas": "0.5.0-beta.22", + "xterm-addon-image": "0.6.0-beta.14", + "xterm-addon-search": "0.13.0-beta.20", + "xterm-addon-serialize": "0.11.0-beta.20", + "xterm-addon-unicode11": "0.6.0-beta.12", + "xterm-addon-webgl": "0.16.0-beta.30", + "xterm-headless": "5.3.0-beta.61", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, diff --git a/remote/package.json b/remote/package.json index 6d6a0152a55..8fe0e55c1de 100644 --- a/remote/package.json +++ b/remote/package.json @@ -27,14 +27,14 @@ "vscode-oniguruma": "1.7.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "9.0.0", - "xterm": "5.3.0-beta.58", - "xterm-addon-canvas": "0.5.0-beta.19", - "xterm-addon-image": "0.6.0-beta.11", - "xterm-addon-search": "0.13.0-beta.17", - "xterm-addon-serialize": "0.11.0-beta.17", - "xterm-addon-unicode11": "0.6.0-beta.9", - "xterm-addon-webgl": "0.16.0-beta.27", - "xterm-headless": "5.3.0-beta.58", + "xterm": "5.3.0-beta.61", + "xterm-addon-canvas": "0.5.0-beta.22", + "xterm-addon-image": "0.6.0-beta.14", + "xterm-addon-search": "0.13.0-beta.20", + "xterm-addon-serialize": "0.11.0-beta.20", + "xterm-addon-unicode11": "0.6.0-beta.12", + "xterm-addon-webgl": "0.16.0-beta.30", + "xterm-headless": "5.3.0-beta.61", "yauzl": "^2.9.2", "yazl": "^2.4.3" } diff --git a/remote/web/package.json b/remote/web/package.json index 05686230a43..6075a563e34 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -11,11 +11,11 @@ "tas-client-umd": "0.1.8", "vscode-oniguruma": "1.7.0", "vscode-textmate": "9.0.0", - "xterm": "5.3.0-beta.58", - "xterm-addon-canvas": "0.5.0-beta.19", - "xterm-addon-image": "0.6.0-beta.11", - "xterm-addon-search": "0.13.0-beta.17", - "xterm-addon-unicode11": "0.6.0-beta.9", - "xterm-addon-webgl": "0.16.0-beta.27" + "xterm": "5.3.0-beta.61", + "xterm-addon-canvas": "0.5.0-beta.22", + "xterm-addon-image": "0.6.0-beta.14", + "xterm-addon-search": "0.13.0-beta.20", + "xterm-addon-unicode11": "0.6.0-beta.12", + "xterm-addon-webgl": "0.16.0-beta.30" } } diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index dc817e45bf1..b56fac51a45 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -68,32 +68,32 @@ vscode-textmate@9.0.0: resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-9.0.0.tgz#313c6c8792b0507aef35aeb81b6b370b37c44d6c" integrity sha512-Cl65diFGxz7gpwbav10HqiY/eVYTO1sjQpmRmV991Bj7wAoOAjGQ97PpQcXorDE2Uc4hnGWLY17xme+5t6MlSg== -xterm-addon-canvas@0.5.0-beta.19: - version "0.5.0-beta.19" - resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.5.0-beta.19.tgz#a2b67554191fae29c901c4a4b398fc28dc345afe" - integrity sha512-eF6b7SBZslmwqCLiWTGnjna4bdjd28cQ+ivZH8SVjE5xP3JGAHGWs/fZm5E3eR6pcHEOkX++/FxByuvhOVXIXQ== +xterm-addon-canvas@0.5.0-beta.22: + version "0.5.0-beta.22" + resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.5.0-beta.22.tgz#513f0c2b7cf96073f47627b27e8965c1b1a22431" + integrity sha512-9F6ZI0DMRgffVfHkLkDwl5n8VscvCaV10tWI3skXOX7Y7Aws6OEeglkOPoU3IllofCU792kHKM4pPoToUxTltg== -xterm-addon-image@0.6.0-beta.11: - version "0.6.0-beta.11" - resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.6.0-beta.11.tgz#17dffc5f38480a4fac231649ef6e313a8f46d614" - integrity sha512-KPsqJo8sSAawO4ze7xvjtS5evbPtlscy16NiSAteh/1AgOffmTnVhJTs3W84dnks2b13FaMEI/WOiCDIJjDV3A== +xterm-addon-image@0.6.0-beta.14: + version "0.6.0-beta.14" + resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.6.0-beta.14.tgz#75fc3f824123183a4bbb5306e22f8b2c6966b0a6" + integrity sha512-D5Gh5JTKhHaPt1KwQNf6diF37KA4eToJw3XId1wy62tWmSqfq+QflhOGTfd+SnSQYCktU05ETzM+0tncIU62pQ== -xterm-addon-search@0.13.0-beta.17: - version "0.13.0-beta.17" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.13.0-beta.17.tgz#5379cf3085370c55241d99e95b008c0cbf4e6e53" - integrity sha512-0OKwV9isk4OXHLlhRE8Ohqnmpcd0ExWKtorjeaFxqm4f6xc30qZcJTs0hSMzVZ83foedPJcd6hH0hW2pnxOz3Q== +xterm-addon-search@0.13.0-beta.20: + version "0.13.0-beta.20" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.13.0-beta.20.tgz#8ddd0513e2a70fcefa325722100d2e1bfaf3b9cb" + integrity sha512-wrx6187cJ1UenGL6ZeYv3jFvRPhhENTfbC+Hv1Fnww8LmsKhcj+0+Pm6yInNjX/9hNVsNzdqKyqNeEMoykyoyA== -xterm-addon-unicode11@0.6.0-beta.9: - version "0.6.0-beta.9" - resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.6.0-beta.9.tgz#1f476f77cf8c8e4e7ca1a1421b85ac45405db679" - integrity sha512-DH9OIH0EakIdCnzZcIH7NLgUqXwa8WT9fqmi+2CWzt4NN1AH5kSE/3Vk+/uurE5upxDloKw6dtLH9XHpfULV6g== +xterm-addon-unicode11@0.6.0-beta.12: + version "0.6.0-beta.12" + resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.6.0-beta.12.tgz#ac6df9d635325dc692e4c602e74a2fc27a09405c" + integrity sha512-9wWWf/5nFafYgq0pn9EgAWnXaXGleVxfjNOqavpLRYFv0nw42QbaYyGvnGcxyYHM5Aqx/8rYE/DDVWZBqQZdYA== -xterm-addon-webgl@0.16.0-beta.27: - version "0.16.0-beta.27" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.27.tgz#f4133a40044f0b6448ea87152abf6c9009729100" - integrity sha512-YiwCvTvgfcNGtQdpzcgxKINE6mG63cPfDxfoxCAbodNGYF66k9VzhIXNdCQtVtu+s6+pEc9YequENe78SSZloA== +xterm-addon-webgl@0.16.0-beta.30: + version "0.16.0-beta.30" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.30.tgz#820d5c65f868b14ec4177bfb8a294931a53616bf" + integrity sha512-39qPHPFmNENxcHf8/CzGHS6wzKMMegoRkHB1+scqtBhSxFaD8tX5Ye33HZIEdQ9nXe9xtr4FWVp77T+n9hdrew== -xterm@5.3.0-beta.58: - version "5.3.0-beta.58" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.58.tgz#4bae06639c90952b270e4714938c151ce339b5f0" - integrity sha512-EIOgp+7aqCToI0XjDqORXAe2lJS/XSZXWPzCpyTDmF+DJ56gyw+913gdPoIEWOw3mRylrJtEyIpFcdW7CDA6rQ== +xterm@5.3.0-beta.61: + version "5.3.0-beta.61" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.61.tgz#a6c27d90a5314da51d80deeb32f3bd77f1e1c8f6" + integrity sha512-rJHpCc48GSpHnu0SSERynQ80D5ikvFVsqhv6JdmeONTrnAFRr134OglJRIpbi2YK8UPbV6F6Dfqm/AQh+9GZzA== diff --git a/remote/yarn.lock b/remote/yarn.lock index 68c99905917..4716df355d5 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -877,45 +877,45 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -xterm-addon-canvas@0.5.0-beta.19: - version "0.5.0-beta.19" - resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.5.0-beta.19.tgz#a2b67554191fae29c901c4a4b398fc28dc345afe" - integrity sha512-eF6b7SBZslmwqCLiWTGnjna4bdjd28cQ+ivZH8SVjE5xP3JGAHGWs/fZm5E3eR6pcHEOkX++/FxByuvhOVXIXQ== +xterm-addon-canvas@0.5.0-beta.22: + version "0.5.0-beta.22" + resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.5.0-beta.22.tgz#513f0c2b7cf96073f47627b27e8965c1b1a22431" + integrity sha512-9F6ZI0DMRgffVfHkLkDwl5n8VscvCaV10tWI3skXOX7Y7Aws6OEeglkOPoU3IllofCU792kHKM4pPoToUxTltg== -xterm-addon-image@0.6.0-beta.11: - version "0.6.0-beta.11" - resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.6.0-beta.11.tgz#17dffc5f38480a4fac231649ef6e313a8f46d614" - integrity sha512-KPsqJo8sSAawO4ze7xvjtS5evbPtlscy16NiSAteh/1AgOffmTnVhJTs3W84dnks2b13FaMEI/WOiCDIJjDV3A== +xterm-addon-image@0.6.0-beta.14: + version "0.6.0-beta.14" + resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.6.0-beta.14.tgz#75fc3f824123183a4bbb5306e22f8b2c6966b0a6" + integrity sha512-D5Gh5JTKhHaPt1KwQNf6diF37KA4eToJw3XId1wy62tWmSqfq+QflhOGTfd+SnSQYCktU05ETzM+0tncIU62pQ== -xterm-addon-search@0.13.0-beta.17: - version "0.13.0-beta.17" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.13.0-beta.17.tgz#5379cf3085370c55241d99e95b008c0cbf4e6e53" - integrity sha512-0OKwV9isk4OXHLlhRE8Ohqnmpcd0ExWKtorjeaFxqm4f6xc30qZcJTs0hSMzVZ83foedPJcd6hH0hW2pnxOz3Q== +xterm-addon-search@0.13.0-beta.20: + version "0.13.0-beta.20" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.13.0-beta.20.tgz#8ddd0513e2a70fcefa325722100d2e1bfaf3b9cb" + integrity sha512-wrx6187cJ1UenGL6ZeYv3jFvRPhhENTfbC+Hv1Fnww8LmsKhcj+0+Pm6yInNjX/9hNVsNzdqKyqNeEMoykyoyA== -xterm-addon-serialize@0.11.0-beta.17: - version "0.11.0-beta.17" - resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.11.0-beta.17.tgz#170738ab615e2974c593d41531d41c02b8af1da7" - integrity sha512-zNsUQfIbRD8fL0gHGJJ14DXL22Kduv6pR+QnISReoQqhEEsWANPKpOduJLce7B4y+BJuiOGkT7R7qbMKbj9Ecw== +xterm-addon-serialize@0.11.0-beta.20: + version "0.11.0-beta.20" + resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.11.0-beta.20.tgz#e879b34d214761403f1081833f9221c6903bf0c3" + integrity sha512-OXnC1SATaz7kEFjFWhyv9MJaXi8yHdPjazpGLNi11h33CRTKtCQiqqPBHU87dztnXmpEX6Jw0/jr3zlyXuAmnw== -xterm-addon-unicode11@0.6.0-beta.9: - version "0.6.0-beta.9" - resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.6.0-beta.9.tgz#1f476f77cf8c8e4e7ca1a1421b85ac45405db679" - integrity sha512-DH9OIH0EakIdCnzZcIH7NLgUqXwa8WT9fqmi+2CWzt4NN1AH5kSE/3Vk+/uurE5upxDloKw6dtLH9XHpfULV6g== +xterm-addon-unicode11@0.6.0-beta.12: + version "0.6.0-beta.12" + resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.6.0-beta.12.tgz#ac6df9d635325dc692e4c602e74a2fc27a09405c" + integrity sha512-9wWWf/5nFafYgq0pn9EgAWnXaXGleVxfjNOqavpLRYFv0nw42QbaYyGvnGcxyYHM5Aqx/8rYE/DDVWZBqQZdYA== -xterm-addon-webgl@0.16.0-beta.27: - version "0.16.0-beta.27" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.27.tgz#f4133a40044f0b6448ea87152abf6c9009729100" - integrity sha512-YiwCvTvgfcNGtQdpzcgxKINE6mG63cPfDxfoxCAbodNGYF66k9VzhIXNdCQtVtu+s6+pEc9YequENe78SSZloA== +xterm-addon-webgl@0.16.0-beta.30: + version "0.16.0-beta.30" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.30.tgz#820d5c65f868b14ec4177bfb8a294931a53616bf" + integrity sha512-39qPHPFmNENxcHf8/CzGHS6wzKMMegoRkHB1+scqtBhSxFaD8tX5Ye33HZIEdQ9nXe9xtr4FWVp77T+n9hdrew== -xterm-headless@5.3.0-beta.58: - version "5.3.0-beta.58" - resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.3.0-beta.58.tgz#e6a3dbfafdb28435130ce3925ac3194c0ee2c281" - integrity sha512-ekzr3LX8p26qHDyVRs3lys08veHFv9wGOKaHd49kMgktICHsikJ48HNJq5Twp3ZnXyqlIP9CV34BZJ1SBfWmtQ== +xterm-headless@5.3.0-beta.61: + version "5.3.0-beta.61" + resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.3.0-beta.61.tgz#28654550cb572709b99ea3eb8672d4568ae141c9" + integrity sha512-yfkbPLUtKjE4K7DsZ204A1BuOKpu6Usqi6rIYWT4XRMi+LjnkTbBjGr2BSjyJ3Gmtm+cSgBD0SvRN+V3xNxbxA== -xterm@5.3.0-beta.58: - version "5.3.0-beta.58" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.58.tgz#4bae06639c90952b270e4714938c151ce339b5f0" - integrity sha512-EIOgp+7aqCToI0XjDqORXAe2lJS/XSZXWPzCpyTDmF+DJ56gyw+913gdPoIEWOw3mRylrJtEyIpFcdW7CDA6rQ== +xterm@5.3.0-beta.61: + version "5.3.0-beta.61" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.61.tgz#a6c27d90a5314da51d80deeb32f3bd77f1e1c8f6" + integrity sha512-rJHpCc48GSpHnu0SSERynQ80D5ikvFVsqhv6JdmeONTrnAFRr134OglJRIpbi2YK8UPbV6F6Dfqm/AQh+9GZzA== yallist@^4.0.0: version "4.0.0" diff --git a/yarn.lock b/yarn.lock index 96c77bcd45a..5595e14de37 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10759,45 +10759,45 @@ xtend@~2.1.1: dependencies: object-keys "~0.4.0" -xterm-addon-canvas@0.5.0-beta.19: - version "0.5.0-beta.19" - resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.5.0-beta.19.tgz#a2b67554191fae29c901c4a4b398fc28dc345afe" - integrity sha512-eF6b7SBZslmwqCLiWTGnjna4bdjd28cQ+ivZH8SVjE5xP3JGAHGWs/fZm5E3eR6pcHEOkX++/FxByuvhOVXIXQ== +xterm-addon-canvas@0.5.0-beta.22: + version "0.5.0-beta.22" + resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.5.0-beta.22.tgz#513f0c2b7cf96073f47627b27e8965c1b1a22431" + integrity sha512-9F6ZI0DMRgffVfHkLkDwl5n8VscvCaV10tWI3skXOX7Y7Aws6OEeglkOPoU3IllofCU792kHKM4pPoToUxTltg== -xterm-addon-image@0.6.0-beta.11: - version "0.6.0-beta.11" - resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.6.0-beta.11.tgz#17dffc5f38480a4fac231649ef6e313a8f46d614" - integrity sha512-KPsqJo8sSAawO4ze7xvjtS5evbPtlscy16NiSAteh/1AgOffmTnVhJTs3W84dnks2b13FaMEI/WOiCDIJjDV3A== +xterm-addon-image@0.6.0-beta.14: + version "0.6.0-beta.14" + resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.6.0-beta.14.tgz#75fc3f824123183a4bbb5306e22f8b2c6966b0a6" + integrity sha512-D5Gh5JTKhHaPt1KwQNf6diF37KA4eToJw3XId1wy62tWmSqfq+QflhOGTfd+SnSQYCktU05ETzM+0tncIU62pQ== -xterm-addon-search@0.13.0-beta.17: - version "0.13.0-beta.17" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.13.0-beta.17.tgz#5379cf3085370c55241d99e95b008c0cbf4e6e53" - integrity sha512-0OKwV9isk4OXHLlhRE8Ohqnmpcd0ExWKtorjeaFxqm4f6xc30qZcJTs0hSMzVZ83foedPJcd6hH0hW2pnxOz3Q== +xterm-addon-search@0.13.0-beta.20: + version "0.13.0-beta.20" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.13.0-beta.20.tgz#8ddd0513e2a70fcefa325722100d2e1bfaf3b9cb" + integrity sha512-wrx6187cJ1UenGL6ZeYv3jFvRPhhENTfbC+Hv1Fnww8LmsKhcj+0+Pm6yInNjX/9hNVsNzdqKyqNeEMoykyoyA== -xterm-addon-serialize@0.11.0-beta.17: - version "0.11.0-beta.17" - resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.11.0-beta.17.tgz#170738ab615e2974c593d41531d41c02b8af1da7" - integrity sha512-zNsUQfIbRD8fL0gHGJJ14DXL22Kduv6pR+QnISReoQqhEEsWANPKpOduJLce7B4y+BJuiOGkT7R7qbMKbj9Ecw== +xterm-addon-serialize@0.11.0-beta.20: + version "0.11.0-beta.20" + resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.11.0-beta.20.tgz#e879b34d214761403f1081833f9221c6903bf0c3" + integrity sha512-OXnC1SATaz7kEFjFWhyv9MJaXi8yHdPjazpGLNi11h33CRTKtCQiqqPBHU87dztnXmpEX6Jw0/jr3zlyXuAmnw== -xterm-addon-unicode11@0.6.0-beta.9: - version "0.6.0-beta.9" - resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.6.0-beta.9.tgz#1f476f77cf8c8e4e7ca1a1421b85ac45405db679" - integrity sha512-DH9OIH0EakIdCnzZcIH7NLgUqXwa8WT9fqmi+2CWzt4NN1AH5kSE/3Vk+/uurE5upxDloKw6dtLH9XHpfULV6g== +xterm-addon-unicode11@0.6.0-beta.12: + version "0.6.0-beta.12" + resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.6.0-beta.12.tgz#ac6df9d635325dc692e4c602e74a2fc27a09405c" + integrity sha512-9wWWf/5nFafYgq0pn9EgAWnXaXGleVxfjNOqavpLRYFv0nw42QbaYyGvnGcxyYHM5Aqx/8rYE/DDVWZBqQZdYA== -xterm-addon-webgl@0.16.0-beta.27: - version "0.16.0-beta.27" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.27.tgz#f4133a40044f0b6448ea87152abf6c9009729100" - integrity sha512-YiwCvTvgfcNGtQdpzcgxKINE6mG63cPfDxfoxCAbodNGYF66k9VzhIXNdCQtVtu+s6+pEc9YequENe78SSZloA== +xterm-addon-webgl@0.16.0-beta.30: + version "0.16.0-beta.30" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.30.tgz#820d5c65f868b14ec4177bfb8a294931a53616bf" + integrity sha512-39qPHPFmNENxcHf8/CzGHS6wzKMMegoRkHB1+scqtBhSxFaD8tX5Ye33HZIEdQ9nXe9xtr4FWVp77T+n9hdrew== -xterm-headless@5.3.0-beta.58: - version "5.3.0-beta.58" - resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.3.0-beta.58.tgz#e6a3dbfafdb28435130ce3925ac3194c0ee2c281" - integrity sha512-ekzr3LX8p26qHDyVRs3lys08veHFv9wGOKaHd49kMgktICHsikJ48HNJq5Twp3ZnXyqlIP9CV34BZJ1SBfWmtQ== +xterm-headless@5.3.0-beta.61: + version "5.3.0-beta.61" + resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.3.0-beta.61.tgz#28654550cb572709b99ea3eb8672d4568ae141c9" + integrity sha512-yfkbPLUtKjE4K7DsZ204A1BuOKpu6Usqi6rIYWT4XRMi+LjnkTbBjGr2BSjyJ3Gmtm+cSgBD0SvRN+V3xNxbxA== -xterm@5.3.0-beta.58: - version "5.3.0-beta.58" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.58.tgz#4bae06639c90952b270e4714938c151ce339b5f0" - integrity sha512-EIOgp+7aqCToI0XjDqORXAe2lJS/XSZXWPzCpyTDmF+DJ56gyw+913gdPoIEWOw3mRylrJtEyIpFcdW7CDA6rQ== +xterm@5.3.0-beta.61: + version "5.3.0-beta.61" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.61.tgz#a6c27d90a5314da51d80deeb32f3bd77f1e1c8f6" + integrity sha512-rJHpCc48GSpHnu0SSERynQ80D5ikvFVsqhv6JdmeONTrnAFRr134OglJRIpbi2YK8UPbV6F6Dfqm/AQh+9GZzA== y18n@^3.2.1: version "3.2.2"