From c8bf91604654baf7ecc1a716475bdc140c019e07 Mon Sep 17 00:00:00 2001 From: Wowol Date: Sun, 17 Nov 2019 14:47:59 +0100 Subject: [PATCH 01/71] Add cursor change after revert or stage changes --- extensions/git/src/commands.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index e58f8c42292..c6fc87a0290 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -6,7 +6,7 @@ import { lstat, Stats } from 'fs'; import * as os from 'os'; import * as path from 'path'; -import { commands, Disposable, LineChange, MessageOptions, OutputChannel, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder } from 'vscode'; +import { Selection, commands, Disposable, LineChange, MessageOptions, OutputChannel, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder } from 'vscode'; import TelemetryReporter from 'vscode-extension-telemetry'; import * as nls from 'vscode-nls'; import { Branch, GitErrorCodes, Ref, RefType, Status } from './api/git'; @@ -980,7 +980,10 @@ export class CommandCenter { return; } - await this._stageChanges(textEditor, [changes[index]]); + await this._stageChanges(textEditor, [changes[index]]).then(() => { + const firstStagedLine = changes[index].modifiedStartLineNumber - 1; + textEditor.selections = [new Selection(firstStagedLine, 0, firstStagedLine, 0)]; + }); } @command('git.stageSelectedRanges', { diff: true }) @@ -1027,7 +1030,10 @@ export class CommandCenter { return; } - await this._revertChanges(textEditor, [...changes.slice(0, index), ...changes.slice(index + 1)]); + await this._revertChanges(textEditor, [...changes.slice(0, index), ...changes.slice(index + 1)]).then(() => { + const firstStagedLine = changes[index].modifiedStartLineNumber - 1; + textEditor.selections = [new Selection(firstStagedLine, 0, firstStagedLine, 0)]; + }); } @command('git.revertSelectedRanges', { diff: true }) @@ -1049,7 +1055,10 @@ export class CommandCenter { return; } - await this._revertChanges(textEditor, selectedChanges); + const selectionsBeforeRevert = textEditor.selections; + await this._revertChanges(textEditor, selectedChanges).then(() => { + textEditor.selections = selectionsBeforeRevert; + }); } private async _revertChanges(textEditor: TextEditor, changes: LineChange[]): Promise { @@ -1062,7 +1071,6 @@ export class CommandCenter { const originalUri = toGitUri(modifiedUri, '~'); const originalDocument = await workspace.openTextDocument(originalUri); - const selectionsBeforeRevert = textEditor.selections; const visibleRangesBeforeRevert = textEditor.visibleRanges; const result = applyLineChanges(originalDocument, modifiedDocument, changes); @@ -1072,7 +1080,6 @@ export class CommandCenter { await modifiedDocument.save(); - textEditor.selections = selectionsBeforeRevert; textEditor.revealRange(visibleRangesBeforeRevert[0]); } From 828389d890157634dfb0b4e11f571bfe6da4043b Mon Sep 17 00:00:00 2001 From: Luqman Aden Date: Sun, 19 Apr 2020 04:57:06 -0700 Subject: [PATCH 02/71] Register mimetype for *.code-workspace --- build/gulpfile.vscode.linux.js | 14 ++++++++++++-- resources/linux/code-workspace.xml | 7 +++++++ resources/linux/code.desktop | 2 +- resources/linux/debian/postinst.template | 5 +++++ resources/linux/debian/postrm.template | 7 ++++++- resources/linux/rpm/code.spec.template | 8 ++++++++ 6 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 resources/linux/code-workspace.xml diff --git a/build/gulpfile.vscode.linux.js b/build/gulpfile.vscode.linux.js index bd8da32d7ad..8293cb36a7f 100644 --- a/build/gulpfile.vscode.linux.js +++ b/build/gulpfile.vscode.linux.js @@ -52,6 +52,11 @@ function prepareDebPackage(arch) { .pipe(replace('@@LICENSE@@', product.licenseName)) .pipe(rename('usr/share/appdata/' + product.applicationName + '.appdata.xml')); + const workspaceMime = gulp.src('resources/code-workspace.xml', { base: '.' }) + .pipe(replace('@@NAME_LONG@@', product.nameLong)) + .pipe(replace('@@NAME@@', product.applicationName)) + .pipe(rename('usr/share/mime/packages/' + product.applicationName + '-workspace.xml')); + const icon = gulp.src('resources/linux/code.png', { base: '.' }) .pipe(rename('usr/share/pixmaps/' + product.linuxIconName + '.png')); @@ -95,7 +100,7 @@ function prepareDebPackage(arch) { .pipe(replace('@@UPDATEURL@@', product.updateUrl || '@@UPDATEURL@@')) .pipe(rename('DEBIAN/postinst')); - const all = es.merge(control, postinst, postrm, prerm, desktops, appdata, icon, bash_completion, zsh_completion, code); + const all = es.merge(control, postinst, postrm, prerm, desktops, appdata, workspaceMime, icon, bash_completion, zsh_completion, code); return all.pipe(vfs.dest(destination)); }; @@ -143,6 +148,11 @@ function prepareRpmPackage(arch) { .pipe(replace('@@LICENSE@@', product.licenseName)) .pipe(rename('usr/share/appdata/' + product.applicationName + '.appdata.xml')); + const workspaceMime = gulp.src('resources/code-workspace.xml', { base: '.' }) + .pipe(replace('@@NAME_LONG@@', product.nameLong)) + .pipe(replace('@@NAME@@', product.applicationName)) + .pipe(rename('BUILD/usr/share/mime/packages/' + product.applicationName + '-workspace.xml')); + const icon = gulp.src('resources/linux/code.png', { base: '.' }) .pipe(rename('BUILD/usr/share/pixmaps/' + product.linuxIconName + '.png')); @@ -173,7 +183,7 @@ function prepareRpmPackage(arch) { const specIcon = gulp.src('resources/linux/rpm/code.xpm', { base: '.' }) .pipe(rename('SOURCES/' + product.applicationName + '.xpm')); - const all = es.merge(code, desktops, appdata, icon, bash_completion, zsh_completion, spec, specIcon); + const all = es.merge(code, desktops, appdata, workspaceMime, icon, bash_completion, zsh_completion, spec, specIcon); return all.pipe(vfs.dest(getRpmBuildPath(rpmArch))); }; diff --git a/resources/linux/code-workspace.xml b/resources/linux/code-workspace.xml new file mode 100644 index 00000000000..5a3e286ef43 --- /dev/null +++ b/resources/linux/code-workspace.xml @@ -0,0 +1,7 @@ + + + + @@NAME_LONG@@ Workspace + + + diff --git a/resources/linux/code.desktop b/resources/linux/code.desktop index b975e1094a2..62d6bfc47b4 100644 --- a/resources/linux/code.desktop +++ b/resources/linux/code.desktop @@ -8,7 +8,7 @@ Type=Application StartupNotify=false StartupWMClass=@@NAME_SHORT@@ Categories=Utility;TextEditor;Development;IDE; -MimeType=text/plain;inode/directory; +MimeType=text/plain;inode/directory;application/x-@@NAME@@-workspace; Actions=new-empty-window; Keywords=vscode; diff --git a/resources/linux/debian/postinst.template b/resources/linux/debian/postinst.template index 2c4a1fdd1d6..9f26b350999 100755 --- a/resources/linux/debian/postinst.template +++ b/resources/linux/debian/postinst.template @@ -18,6 +18,11 @@ if hash desktop-file-install 2>/dev/null; then desktop-file-install /usr/share/applications/@@NAME@@-url-handler.desktop fi +# Update mimetype database to pickup workspace mimetype +if hash update-mime-database 2>/dev/null; then + update-mime-database /usr/share/mime +fi + if [ "@@NAME@@" != "code-oss" ]; then # Remove the legacy bin command if this is the stable build if [ "@@NAME@@" = "code" ]; then diff --git a/resources/linux/debian/postrm.template b/resources/linux/debian/postrm.template index c43a2b16ae3..238a566c486 100755 --- a/resources/linux/debian/postrm.template +++ b/resources/linux/debian/postrm.template @@ -3,4 +3,9 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. -rm -f /usr/bin/@@NAME@@ \ No newline at end of file +rm -f /usr/bin/@@NAME@@ + +# Update mimetype database for removed workspace mimetype +if hash update-mime-database 2>/dev/null; then + update-mime-database /usr/share/mime +fi diff --git a/resources/linux/rpm/code.spec.template b/resources/linux/rpm/code.spec.template index 93e20cd4825..4c0b9db4718 100644 --- a/resources/linux/rpm/code.spec.template +++ b/resources/linux/rpm/code.spec.template @@ -23,6 +23,7 @@ mkdir -p %{buildroot}/usr/share/zsh/site-functions cp -r usr/share/@@NAME@@/* %{buildroot}/usr/share/@@NAME@@ cp -r usr/share/applications/@@NAME@@.desktop %{buildroot}/usr/share/applications cp -r usr/share/applications/@@NAME@@-url-handler.desktop %{buildroot}/usr/share/applications +cp -r usr/share/mime/packages/@@NAME@@-workspace.xml %{buildroot}/usr/share/mime/packages/@@NAME-workspace.xml cp -r usr/share/pixmaps/@@ICON@@.png %{buildroot}/usr/share/pixmaps cp usr/share/bash-completion/completions/@@NAME@@ %{buildroot}/usr/share/bash-completion/completions/@@NAME@@ cp usr/share/zsh/site-functions/_@@NAME@@ %{buildroot}/usr/share/zsh/site-functions/_@@NAME@@ @@ -46,17 +47,24 @@ ln -sf /usr/share/@@NAME@@/bin/@@NAME@@ %{_bindir}/@@NAME@@ # fi #fi +# Update mimetype database to pickup workspace mimetype +update-mime-database /usr/share/mime &> /dev/null || : + %postun if [ $1 = 0 ]; then rm -f /usr/bin/@@NAME@@ fi +# Update mimetype database for removed workspace mimetype +update-mime-database /usr/share/mime &> /dev/null || : + %files %defattr(-,root,root) /usr/share/@@NAME@@/ /usr/share/applications/@@NAME@@.desktop /usr/share/applications/@@NAME@@-url-handler.desktop +/usr/share/mime/packages/@@NAME-workspace.xml /usr/share/pixmaps/@@ICON@@.png /usr/share/bash-completion/completions/@@NAME@@ /usr/share/zsh/site-functions/_@@NAME@@ From 372d3f30a420f029d4b48b623a197edfd20bf99f Mon Sep 17 00:00:00 2001 From: "Dasharathi, Abhijith" Date: Wed, 5 Aug 2020 21:48:57 +0530 Subject: [PATCH 03/71] Fixing the open issue 101155 --- .../browser/links/terminalWordLinkProvider.ts | 17 +++++++++++++---- .../links/terminalWordLinkProvider.test.ts | 9 +++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts index 5b501192c49..5036850fd42 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts @@ -50,10 +50,10 @@ export class TerminalWordLinkProvider extends TerminalBaseLinkProvider { const chars = cellData.getChars(); const width = cellData.getWidth(); - // Add a link if this is a separator + // Add a link if this is a separator and also remove trailing colon if exists if (width !== 0 && wordSeparators.indexOf(chars) >= 0) { if (startX !== -1) { - result.push(new TerminalLink({ start: { x: startX + 1, y }, end: { x, y } }, text, this._xterm.buffer.active.viewportY, activateCallback, this._tooltipCallback, false, localize('searchWorkspace', 'Search workspace'), this._configurationService)); + result.push(new TerminalLink({ start: { x: startX + 1, y }, end: { x, y } }, this._remove_trailing_colon(text), this._xterm.buffer.active.viewportY, activateCallback, this._tooltipCallback, false, localize('searchWorkspace', 'Search workspace'), this._configurationService)); text = ''; startX = -1; } @@ -68,14 +68,23 @@ export class TerminalWordLinkProvider extends TerminalBaseLinkProvider { text += chars; } - // Add the final link if there is one + // Add the final link if there is one and also remove trailing colon if exists if (startX !== -1) { - result.push(new TerminalLink({ start: { x: startX + 1, y }, end: { x: line.length, y } }, text, this._xterm.buffer.active.viewportY, activateCallback, this._tooltipCallback, false, localize('searchWorkspace', 'Search workspace'), this._configurationService)); + result.push(new TerminalLink({ start: { x: startX + 1, y }, end: { x: line.length, y } }, this._remove_trailing_colon(text), this._xterm.buffer.active.viewportY, activateCallback, this._tooltipCallback, false, localize('searchWorkspace', 'Search workspace'), this._configurationService)); } return result; } + // Remove trailing colon in the provided text + private _remove_trailing_colon(text: string): string { + if (text.length > 0 && text[text.length - 1] === ':') { + return text.slice(0, text.length - 1); + } + return text; + } + + private async _activate(link: string) { const results = await this._searchService.fileSearch( this._fileQueryBuilder.file(this._workspaceContextService.getWorkspace().folders, { diff --git a/src/vs/workbench/contrib/terminal/test/browser/links/terminalWordLinkProvider.test.ts b/src/vs/workbench/contrib/terminal/test/browser/links/terminalWordLinkProvider.test.ts index 4927a1cbc37..589e8e94d42 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/links/terminalWordLinkProvider.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/links/terminalWordLinkProvider.test.ts @@ -79,4 +79,13 @@ suite('Workbench - TerminalWordLinkProvider', () => { { range: [[5, 1], [7, 1]], text: 'bar' } ]); }); + + test('should remove trailing colon in the link results', async () => { + await configurationService.setUserConfiguration('terminal', { integrated: { wordSeparators: ' ' } }); + await assertLink('foo:5:6: bar:0:32:', [ + { range: [[1, 1], [8, 1]], text: 'foo:5:6' }, + { range: [[10, 1], [18, 1]], text: 'bar:0:32' } + ]); + }); + }); From 4e51025355e5e728b7b87ed90d771ffa590dffcb Mon Sep 17 00:00:00 2001 From: "Dasharathi, Abhijith" Date: Thu, 13 Aug 2020 00:59:42 +0530 Subject: [PATCH 04/71] Removing any trailing : from the selected range and incorporating changes suggested --- .../browser/links/terminalWordLinkProvider.ts | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts index 5036850fd42..16a1850ac58 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts @@ -53,7 +53,12 @@ export class TerminalWordLinkProvider extends TerminalBaseLinkProvider { // Add a link if this is a separator and also remove trailing colon if exists if (width !== 0 && wordSeparators.indexOf(chars) >= 0) { if (startX !== -1) { - result.push(new TerminalLink({ start: { x: startX + 1, y }, end: { x, y } }, this._remove_trailing_colon(text), this._xterm.buffer.active.viewportY, activateCallback, this._tooltipCallback, false, localize('searchWorkspace', 'Search workspace'), this._configurationService)); + text = this._trimTrailingColon(text); + let endX = x; + if (x - startX > text.length) { + endX--; + } + result.push(new TerminalLink({ start: { x: startX + 1, y }, end: { x: endX, y } }, text, this._xterm.buffer.active.viewportY, activateCallback, this._tooltipCallback, false, localize('searchWorkspace', 'Search workspace'), this._configurationService)); text = ''; startX = -1; } @@ -70,18 +75,20 @@ export class TerminalWordLinkProvider extends TerminalBaseLinkProvider { // Add the final link if there is one and also remove trailing colon if exists if (startX !== -1) { - result.push(new TerminalLink({ start: { x: startX + 1, y }, end: { x: line.length, y } }, this._remove_trailing_colon(text), this._xterm.buffer.active.viewportY, activateCallback, this._tooltipCallback, false, localize('searchWorkspace', 'Search workspace'), this._configurationService)); + text = this._trimTrailingColon(text); + let endX = line.length; + if (line.length - startX > text.length) { + endX--; + } + result.push(new TerminalLink({ start: { x: startX + 1, y }, end: { x: endX, y } }, text, this._xterm.buffer.active.viewportY, activateCallback, this._tooltipCallback, false, localize('searchWorkspace', 'Search workspace'), this._configurationService)); } return result; } // Remove trailing colon in the provided text - private _remove_trailing_colon(text: string): string { - if (text.length > 0 && text[text.length - 1] === ':') { - return text.slice(0, text.length - 1); - } - return text; + private _trimTrailingColon(text: string): string { + return (text.slice(-1) === ':') ? text.slice(0, -1) : text; } From 9891b8e882f1eb5bf8cd24ff1ecf594377e5de87 Mon Sep 17 00:00:00 2001 From: "Dasharathi, Abhijith" Date: Thu, 13 Aug 2020 01:51:41 +0530 Subject: [PATCH 05/71] Updating the unit test with the code changes --- .../browser/links/terminalWordLinkProvider.ts | 14 ++++---------- .../browser/links/terminalWordLinkProvider.test.ts | 4 ++-- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts index 16a1850ac58..6c70e294bae 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts @@ -53,9 +53,9 @@ export class TerminalWordLinkProvider extends TerminalBaseLinkProvider { // Add a link if this is a separator and also remove trailing colon if exists if (width !== 0 && wordSeparators.indexOf(chars) >= 0) { if (startX !== -1) { - text = this._trimTrailingColon(text); let endX = x; - if (x - startX > text.length) { + if (text.slice(-1) === ':') { + text = text.slice(0, -1); endX--; } result.push(new TerminalLink({ start: { x: startX + 1, y }, end: { x: endX, y } }, text, this._xterm.buffer.active.viewportY, activateCallback, this._tooltipCallback, false, localize('searchWorkspace', 'Search workspace'), this._configurationService)); @@ -75,9 +75,9 @@ export class TerminalWordLinkProvider extends TerminalBaseLinkProvider { // Add the final link if there is one and also remove trailing colon if exists if (startX !== -1) { - text = this._trimTrailingColon(text); let endX = line.length; - if (line.length - startX > text.length) { + if (text.slice(-1) === ':') { + text = text.slice(0, -1); endX--; } result.push(new TerminalLink({ start: { x: startX + 1, y }, end: { x: endX, y } }, text, this._xterm.buffer.active.viewportY, activateCallback, this._tooltipCallback, false, localize('searchWorkspace', 'Search workspace'), this._configurationService)); @@ -86,12 +86,6 @@ export class TerminalWordLinkProvider extends TerminalBaseLinkProvider { return result; } - // Remove trailing colon in the provided text - private _trimTrailingColon(text: string): string { - return (text.slice(-1) === ':') ? text.slice(0, -1) : text; - } - - private async _activate(link: string) { const results = await this._searchService.fileSearch( this._fileQueryBuilder.file(this._workspaceContextService.getWorkspace().folders, { diff --git a/src/vs/workbench/contrib/terminal/test/browser/links/terminalWordLinkProvider.test.ts b/src/vs/workbench/contrib/terminal/test/browser/links/terminalWordLinkProvider.test.ts index 589e8e94d42..9a9bbab0b06 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/links/terminalWordLinkProvider.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/links/terminalWordLinkProvider.test.ts @@ -83,8 +83,8 @@ suite('Workbench - TerminalWordLinkProvider', () => { test('should remove trailing colon in the link results', async () => { await configurationService.setUserConfiguration('terminal', { integrated: { wordSeparators: ' ' } }); await assertLink('foo:5:6: bar:0:32:', [ - { range: [[1, 1], [8, 1]], text: 'foo:5:6' }, - { range: [[10, 1], [18, 1]], text: 'bar:0:32' } + { range: [[1, 1], [7, 1]], text: 'foo:5:6' }, + { range: [[10, 1], [17, 1]], text: 'bar:0:32' } ]); }); From c9e72c873711b8c1b7e7ea97b0d766d3fb2ea651 Mon Sep 17 00:00:00 2001 From: Brandon Date: Sat, 15 Aug 2020 17:40:32 -0600 Subject: [PATCH 06/71] Run Selected Text action should use the active code editor Fixes #104612 --- src/vs/workbench/contrib/terminal/browser/terminalActions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index dd4b2216ff2..6e5dbfc4e89 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -637,7 +637,7 @@ export function registerTerminalActions() { const codeEditorService = accessor.get(ICodeEditorService); const instance = terminalService.getActiveOrCreateInstance(); - let editor = codeEditorService.getFocusedCodeEditor(); + let editor = codeEditorService.getActiveCodeEditor(); if (!editor || !editor.hasModel()) { return; } From 6180194c5e2c840d59b6a2113f9b65a8b4b91b51 Mon Sep 17 00:00:00 2001 From: David Goldman Date: Thu, 20 Aug 2020 17:19:20 -0400 Subject: [PATCH 07/71] Terminal: Support linking local file paths containing '+' Previously the + character was excluded so file paths like: `some/folder/Foo+Extension.swift` and `some/folder/Object+Category.m` would not link properly in the terminal --- .../browser/links/terminalValidatedLocalLinkProvider.ts | 4 ++-- .../links/terminalValidatedLocalLinkProvider.test.ts | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalValidatedLocalLinkProvider.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalValidatedLocalLinkProvider.ts index 287afdf9e1a..67e0174bb13 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalValidatedLocalLinkProvider.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalValidatedLocalLinkProvider.ts @@ -20,14 +20,14 @@ const pathPrefix = '(\\.\\.?|\\~)'; const pathSeparatorClause = '\\/'; // '":; are allowed in paths but they are often separators so ignore them // Also disallow \\ to prevent a catastropic backtracking case #24798 -const excludedPathCharactersClause = '[^\\0\\s!$`&*()\\[\\]+\'":;\\\\]'; +const excludedPathCharactersClause = '[^\\0\\s!$`&*()\\[\\]\'":;\\\\]'; /** A regex that matches paths in the form /foo, ~/foo, ./foo, ../foo, foo/bar */ export const unixLocalLinkClause = '((' + pathPrefix + '|(' + excludedPathCharactersClause + ')+)?(' + pathSeparatorClause + '(' + excludedPathCharactersClause + ')+)+)'; export const winDrivePrefix = '(?:\\\\\\\\\\?\\\\)?[a-zA-Z]:'; const winPathPrefix = '(' + winDrivePrefix + '|\\.\\.?|\\~)'; const winPathSeparatorClause = '(\\\\|\\/)'; -const winExcludedPathCharactersClause = '[^\\0<>\\?\\|\\/\\s!$`&*()\\[\\]+\'":;]'; +const winExcludedPathCharactersClause = '[^\\0<>\\?\\|\\/\\s!$`&*()\\[\\]\'":;]'; /** A regex that matches paths in the form \\?\c:\foo c:\foo, ~\foo, .\foo, ..\foo, foo\bar */ export const winLocalLinkClause = '((' + winPathPrefix + '|(' + winExcludedPathCharactersClause + ')+)?(' + winPathSeparatorClause + '(' + winExcludedPathCharactersClause + ')+)+)'; diff --git a/src/vs/workbench/contrib/terminal/test/browser/links/terminalValidatedLocalLinkProvider.test.ts b/src/vs/workbench/contrib/terminal/test/browser/links/terminalValidatedLocalLinkProvider.test.ts index 23f81babf84..d6950b086ef 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/links/terminalValidatedLocalLinkProvider.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/links/terminalValidatedLocalLinkProvider.test.ts @@ -19,7 +19,9 @@ const unixLinks = [ './foo', '../foo', '/foo/bar', - 'foo/bar' + '/foo/bar+more', + 'foo/bar', + 'foo/bar+more', ]; const windowsLinks = [ @@ -33,10 +35,12 @@ const windowsLinks = [ '~/foo', 'c:/foo/bar', 'c:\\foo\\bar', + 'c:\\foo\\bar+more', 'c:\\foo/bar\\baz', 'foo/bar', 'foo/bar', - 'foo\\bar' + 'foo\\bar', + 'foo\\bar+more', ]; interface LinkFormatInfo { From cd3c52b9ca7bdf58012af0d8488ebb76b0f93382 Mon Sep 17 00:00:00 2001 From: IllusionMH Date: Fri, 4 Sep 2020 00:36:38 +0300 Subject: [PATCH 08/71] Add numeric values support for terminal.integrated.fontWeight --- .../terminal/browser/terminalConfigHelper.ts | 21 +++++++--- .../contrib/terminal/common/terminal.ts | 8 +++- .../terminal/common/terminalConfiguration.ts | 40 +++++++++++++++---- 3 files changed, 56 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts b/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts index e39451c4b69..6408853ea11 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts @@ -8,7 +8,7 @@ import * as platform from 'vs/base/common/platform'; import { EDITOR_FONT_DEFAULTS, IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { ITerminalConfiguration, ITerminalFont, IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, TERMINAL_CONFIG_SECTION, DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, MINIMUM_LETTER_SPACING, LinuxDistro, IShellLaunchConfig } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalConfiguration, ITerminalFont, IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, TERMINAL_CONFIG_SECTION, DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, MINIMUM_LETTER_SPACING, LinuxDistro, IShellLaunchConfig, MINIMUM_FONT_WEIGHT, MAXIMUM_FONT_WEIGHT, DEFAULT_FONT_WEIGHT, DEFAULT_BOLD_FONT_WEIGHT, FontWeight } from 'vs/workbench/contrib/terminal/common/terminal'; import Severity from 'vs/base/common/severity'; import { INotificationService, NeverShowAgainScope } from 'vs/platform/notification/common/notification'; import { IBrowserTerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminal'; @@ -67,7 +67,11 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper { } private _updateConfig(): void { - this.config = this._configurationService.getValue(TERMINAL_CONFIG_SECTION); + const configValues = this._configurationService.getValue(TERMINAL_CONFIG_SECTION); + configValues.fontWeight = this._normalizeFontWeight(configValues.fontWeight, DEFAULT_FONT_WEIGHT); + configValues.fontWeightBold = this._normalizeFontWeight(configValues.fontWeightBold, DEFAULT_BOLD_FONT_WEIGHT); + + this.config = configValues; } public configFontIsMonospace(): boolean { @@ -157,7 +161,7 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper { const editorConfig = this._configurationService.getValue('editor'); let fontFamily = this.config.fontFamily || editorConfig.fontFamily || EDITOR_FONT_DEFAULTS.fontFamily; - let fontSize = this._toInteger(this.config.fontSize, MINIMUM_FONT_SIZE, MAXIMUM_FONT_SIZE, EDITOR_FONT_DEFAULTS.fontSize); + let fontSize = this._clampInt(this.config.fontSize, MINIMUM_FONT_SIZE, MAXIMUM_FONT_SIZE, EDITOR_FONT_DEFAULTS.fontSize); // Work around bad font on Fedora/Ubuntu if (!this.config.fontFamily) { @@ -168,7 +172,7 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper { fontFamily = '\'Ubuntu Mono\', monospace'; // Ubuntu mono is somehow smaller, so set fontSize a bit larger to get the same perceived size. - fontSize = this._toInteger(fontSize + 2, MINIMUM_FONT_SIZE, MAXIMUM_FONT_SIZE, EDITOR_FONT_DEFAULTS.fontSize); + fontSize = this._clampInt(fontSize + 2, MINIMUM_FONT_SIZE, MAXIMUM_FONT_SIZE, EDITOR_FONT_DEFAULTS.fontSize); } } @@ -271,7 +275,7 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper { return !!isWorkspaceShellAllowed; } - private _toInteger(source: any, minimum: number, maximum: number, fallback: number): number { + private _clampInt(source: any, minimum: number, maximum: number, fallback: T): number | T { let r = parseInt(source, 10); if (isNaN(r)) { return fallback; @@ -340,4 +344,11 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper { const extensions = await this._extensionManagementService.getInstalled(ExtensionType.User); return extensions.some(e => e.identifier.id === id); } + + private _normalizeFontWeight(input: any, defaultWeight: FontWeight): FontWeight { + if (input === 'normal' || input === 'bold') { + return input; + } + return this._clampInt(input, MINIMUM_FONT_WEIGHT, MAXIMUM_FONT_WEIGHT, defaultWeight); + } } diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 9a41a37b16b..1af8a10e218 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -70,7 +70,13 @@ export const DEFAULT_LETTER_SPACING = 0; export const MINIMUM_LETTER_SPACING = -5; export const DEFAULT_LINE_HEIGHT = 1; -export type FontWeight = 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900'; +export const MINIMUM_FONT_WEIGHT = 1; +export const MAXIMUM_FONT_WEIGHT = 1000; +export const DEFAULT_FONT_WEIGHT = 'normal'; +export const DEFAULT_BOLD_FONT_WEIGHT = 'bold'; +export const SUGGESTIONS_FONT_WEIGHT = ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900']; + +export type FontWeight = 'normal' | 'bold' | number; export interface ITerminalConfiguration { shell: { diff --git a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts index f38353bcb8d..985fde8246b 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts @@ -6,7 +6,7 @@ import { IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry'; import { localize } from 'vs/nls'; import { EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions'; -import { DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, TerminalCursorStyle, DEFAULT_COMMANDS_TO_SKIP_SHELL } from 'vs/workbench/contrib/terminal/common/terminal'; +import { DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, TerminalCursorStyle, DEFAULT_COMMANDS_TO_SKIP_SHELL, SUGGESTIONS_FONT_WEIGHT, MINIMUM_FONT_WEIGHT, MAXIMUM_FONT_WEIGHT } from 'vs/workbench/contrib/terminal/common/terminal'; import { isMacintosh, isWindows, Platform } from 'vs/base/common/platform'; export const terminalConfiguration: IConfigurationNode = { @@ -136,15 +136,41 @@ export const terminalConfiguration: IConfigurationNode = { default: 1 }, 'terminal.integrated.fontWeight': { - type: 'string', - enum: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'], - description: localize('terminal.integrated.fontWeight', "The font weight to use within the terminal for non-bold text."), + 'anyOf': [ + { + type: 'number', + minimum: MINIMUM_FONT_WEIGHT, + maximum: MAXIMUM_FONT_WEIGHT, + errorMessage: localize('terminal.integrated.fontWeightError', "Only \"normal\" and \"bold\" keywords or numbers between 1 and 1000 are allowed.") + }, + { + type: 'string', + pattern: '^(normal|bold|1000|[1-9][0-9]{0,2})$' + }, + { + enum: SUGGESTIONS_FONT_WEIGHT, + } + ], + description: localize('terminal.integrated.fontWeight', "The font weight to use within the terminal for non-bold text. Accepts \"normal\" and \"bold\" keywords or numbers between 1 and 1000."), default: 'normal' }, 'terminal.integrated.fontWeightBold': { - type: 'string', - enum: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'], - description: localize('terminal.integrated.fontWeightBold', "The font weight to use within the terminal for bold text."), + 'anyOf': [ + { + type: 'number', + minimum: MINIMUM_FONT_WEIGHT, + maximum: MAXIMUM_FONT_WEIGHT, + errorMessage: localize('terminal.integrated.fontWeightError', "Only \"normal\" and \"bold\" keywords or numbers between 1 and 1000 are allowed.") + }, + { + type: 'string', + pattern: '^(normal|bold|1000|[1-9][0-9]{0,2})$' + }, + { + enum: SUGGESTIONS_FONT_WEIGHT, + } + ], + description: localize('terminal.integrated.fontWeightBold', "The font weight to use within the terminal for bold text. Accepts \"normal\" and \"bold\" keywords or numbers between 1 and 1000."), default: 'bold' }, 'terminal.integrated.cursorBlinking': { From d2da7e966eeaa497a990697e82e10cb8e31125c6 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 9 Sep 2020 09:26:56 +0200 Subject: [PATCH 09/71] fix https://github.com/microsoft/vscode/issues/106308 --- src/vs/editor/contrib/links/getLinks.ts | 16 ++++++++++--- .../api/common/extHostApiCommands.ts | 20 +++++----------- .../browser/api/extHostApiCommands.test.ts | 23 +++++++++++++++++++ 3 files changed, 42 insertions(+), 17 deletions(-) diff --git a/src/vs/editor/contrib/links/getLinks.ts b/src/vs/editor/contrib/links/getLinks.ts index 0ee40d5166e..be5c7416457 100644 --- a/src/vs/editor/contrib/links/getLinks.ts +++ b/src/vs/editor/contrib/links/getLinks.ts @@ -13,6 +13,7 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { isDisposable, Disposable } from 'vs/base/common/lifecycle'; import { coalesce } from 'vs/base/common/arrays'; +import { assertType } from 'vs/base/common/types'; export class Link implements ILink { @@ -152,10 +153,13 @@ export function getLinks(model: ITextModel, token: CancellationToken): Promise => { - const [uri] = args; - if (!(uri instanceof URI)) { - return []; + let [uri, resolveCount] = args; + assertType(uri instanceof URI); + + if (typeof resolveCount !== 'number') { + resolveCount = 0; } + const model = accessor.get(IModelService).getModel(uri); if (!model) { return []; @@ -164,6 +168,12 @@ CommandsRegistry.registerCommand('_executeLinkProvider', async (accessor, ...arg if (!list) { return []; } + + // resolve links + for (let i = 0; i < Math.min(resolveCount, list.links.length); i++) { + await list.links[i].resolve(CancellationToken.None); + } + const result = list.links.slice(0); list.dispose(); return result; diff --git a/src/vs/workbench/api/common/extHostApiCommands.ts b/src/vs/workbench/api/common/extHostApiCommands.ts index 1f60c7f5ad0..e7cac07ee3b 100644 --- a/src/vs/workbench/api/common/extHostApiCommands.ts +++ b/src/vs/workbench/api/common/extHostApiCommands.ts @@ -228,10 +228,15 @@ const newCommands: ApiCommand[] = [ } return typeConverters.WorkspaceEdit.to(value); }) + ), + // --- links + new ApiCommand( + 'vscode.executeLinkProvider', '_executeLinkProvider', 'Execute document link provider.', + [ApiCommandArgument.Uri, new ApiCommandArgument('linkResolveCount', '(optional) Number of links that should be resolved, only when links are unresolved.', v => typeof v === 'number' || typeof v === 'undefined', v => v)], + new ApiCommandResult('A promise that resolves to an array of DocumentLink-instances.', value => value.map(typeConverters.DocumentLink.to)) ) ]; - //#endregion @@ -289,13 +294,6 @@ export class ExtHostApiCommands { returns: 'A promise that resolves to an array of CodeLens-instances.' }); - this._register('vscode.executeLinkProvider', this._executeDocumentLinkProvider, { - description: 'Execute document link provider.', - args: [ - { name: 'uri', description: 'Uri of a text document', constraint: URI } - ], - returns: 'A promise that resolves to an array of DocumentLink-instances.' - }); this._register('vscode.executeDocumentColorProvider', this._executeDocumentColorProvider, { description: 'Execute document color provider.', args: [ @@ -478,12 +476,6 @@ export class ExtHostApiCommands { typeConverters.Range.to(item.range), item.command ? this._commands.converter.fromInternal(item.command) : undefined); })); - - } - - private _executeDocumentLinkProvider(resource: URI): Promise { - return this._commands.executeCommand('_executeLinkProvider', resource) - .then(tryMapWith(typeConverters.DocumentLink.to)); } } diff --git a/src/vs/workbench/test/browser/api/extHostApiCommands.test.ts b/src/vs/workbench/test/browser/api/extHostApiCommands.test.ts index 6a7252b386d..f5290dd2f74 100644 --- a/src/vs/workbench/test/browser/api/extHostApiCommands.test.ts +++ b/src/vs/workbench/test/browser/api/extHostApiCommands.test.ts @@ -1011,6 +1011,29 @@ suite('ExtHostLanguageFeatureCommands', function () { }); }); + test('What\'s the condition for DocumentLink target to be undefined? #106308', async function () { + disposables.push(extHost.registerDocumentLinkProvider(nullExtensionDescription, defaultSelector, { + provideDocumentLinks(): any { + return [new types.DocumentLink(new types.Range(0, 0, 0, 20), undefined)]; + }, + resolveDocumentLink(link) { + link.target = URI.parse('foo:bar'); + return link; + } + })); + + await rpcProtocol.sync(); + + const links1 = await commands.executeCommand('vscode.executeLinkProvider', model.uri); + assert.equal(links1.length, 1); + assert.equal(links1[0].target, undefined); + + const links2 = await commands.executeCommand('vscode.executeLinkProvider', model.uri, 1000); + assert.equal(links2.length, 1); + assert.equal(links2[0].target!.toString(), URI.parse('foo:bar').toString()); + + }); + test('Color provider', function () { From 7792c61393fff3c7dbff904f93baa0a2dff59266 Mon Sep 17 00:00:00 2001 From: Vyacheslav Pukhanov Date: Wed, 9 Sep 2020 10:37:56 +0300 Subject: [PATCH 10/71] Update gitignore decorations when .git/info/exclude file is edited (#106270) * detect local `exclude` file edits * use `uri.path` to detect exclude file edits `uri.path` uses forward slash as a path separator indepentent of the host system, which makes it easier to use with regex --- extensions/git/src/decorationProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/git/src/decorationProvider.ts b/extensions/git/src/decorationProvider.ts index 2ed893ed766..ab6e5713e11 100644 --- a/extensions/git/src/decorationProvider.ts +++ b/extensions/git/src/decorationProvider.ts @@ -21,7 +21,7 @@ class GitIgnoreDecorationProvider implements DecorationProvider { constructor(private model: Model) { this.onDidChangeDecorations = fireEvent(anyEvent( - filterEvent(workspace.onDidSaveTextDocument, e => e.fileName.endsWith('.gitignore')), + filterEvent(workspace.onDidSaveTextDocument, e => /\.gitignore$|\.git\/info\/exclude$/.test(e.uri.path)), model.onDidOpenRepository, model.onDidCloseRepository )); From e516bf3251172374707269feeeb7f98811872726 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 9 Sep 2020 09:33:46 +0200 Subject: [PATCH 11/71] updated searches --- .vscode/searches/TrustedTypes.code-search | 165 ++++++++-------------- .vscode/searches/es6.code-search | 30 ++-- 2 files changed, 75 insertions(+), 120 deletions(-) diff --git a/.vscode/searches/TrustedTypes.code-search b/.vscode/searches/TrustedTypes.code-search index 2112eb183c8..9710c95777e 100644 --- a/.vscode/searches/TrustedTypes.code-search +++ b/.vscode/searches/TrustedTypes.code-search @@ -4,7 +4,7 @@ # Excluding: *.test.ts # ContextLines: 3 -57 results - 39 files +52 results - 34 files src/vs/base/browser/markdownRenderer.ts: 156 const strValue = values[0]; @@ -92,13 +92,13 @@ src/vs/base/browser/ui/selectBox/selectBoxCustom.ts: 372 this.applyStyles(); 373 } - 705 }); - 706 - 707 - 708: container.innerHTML = this.options[longest].text + (!!this.options[longest].decoratorRight ? (this.options[longest].decoratorRight + ' ') : ''); - 709 elementWidth = dom.getTotalWidth(container); - 710 } - 711 + 680 }); + 681 + 682 + 683: container.innerHTML = this.options[longest].text + (!!this.options[longest].decoratorRight ? (this.options[longest].decoratorRight + ' ') : ''); + 684 elementWidth = dom.getTotalWidth(container); + 685 } + 686 src/vs/base/browser/ui/tree/abstractTree.ts: 1476 @@ -227,29 +227,29 @@ src/vs/editor/standalone/browser/colorizer.ts: 46 } src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts: - 211 if (!this._globalStyleElement) { - 212 this._globalStyleElement = dom.createStyleSheet(); - 213 this._globalStyleElement.className = 'monaco-colors'; - 214: this._globalStyleElement.innerHTML = this._css; - 215 this._styleElements.push(this._globalStyleElement); - 216 } - 217 return Disposable.None; + 212 if (!this._globalStyleElement) { + 213 this._globalStyleElement = dom.createStyleSheet(); + 214 this._globalStyleElement.className = 'monaco-colors'; + 215: this._globalStyleElement.innerHTML = this._css; + 216 this._styleElements.push(this._globalStyleElement); + 217 } + 218 return Disposable.None; - 220 private _registerShadowDomContainer(domNode: HTMLElement): IDisposable { - 221 const styleElement = dom.createStyleSheet(domNode); - 222 styleElement.className = 'monaco-colors'; - 223: styleElement.innerHTML = this._css; - 224 this._styleElements.push(styleElement); - 225 return { - 226 dispose: () => { + 221 private _registerShadowDomContainer(domNode: HTMLElement): IDisposable { + 222 const styleElement = dom.createStyleSheet(domNode); + 223 styleElement.className = 'monaco-colors'; + 224: styleElement.innerHTML = this._css; + 225 this._styleElements.push(styleElement); + 226 return { + 227 dispose: () => { - 290 ruleCollector.addRule(generateTokensCSSForColorMap(colorMap)); - 291 - 292 this._css = cssRules.join('\n'); - 293: this._styleElements.forEach(styleElement => styleElement.innerHTML = this._css); - 294 - 295 TokenizationRegistry.setColorMap(colorMap); - 296 this._onColorThemeChange.fire(theme); + 291 ruleCollector.addRule(generateTokensCSSForColorMap(colorMap)); + 292 + 293 this._css = cssRules.join('\n'); + 294: this._styleElements.forEach(styleElement => styleElement.innerHTML = this._css); + 295 + 296 TokenizationRegistry.setColorMap(colorMap); + 297 this._onColorThemeChange.fire(theme); src/vs/editor/test/browser/controller/imeTester.ts: 55 let content = this._model.getModelLineContent(i); @@ -268,24 +268,6 @@ src/vs/editor/test/browser/controller/imeTester.ts: 74 75 let startBtn = document.createElement('button'); -src/vs/workbench/browser/parts/compositePart.ts: - 414 const $this = this; - 415 return { - 416 updateTitle: (id, title, keybinding) => { - 417: titleLabel.innerHTML = strings.escape(title); - 418 titleLabel.title = keybinding ? nls.localize('titleTooltip', "{0} ({1})", title, keybinding) : title; - 419 }, - 420 - -src/vs/workbench/browser/parts/statusbar/statusbarPart.ts: - 672 this.styleElement = createStyleSheet(container); - 673 } - 674 - 675: this.styleElement.innerHTML = `.monaco-workbench .part.statusbar > .items-container > .statusbar-item.has-beak:before { border-bottom-color: ${backgroundColor}; }`; - 676 } - 677 - 678 private doCreateStatusItem(id: string, alignment: StatusbarAlignment, ...extraClasses: string[]): HTMLElement { - src/vs/workbench/contrib/comments/browser/commentsView.ts: 109 content.push(`.comments-panel .comments-panel-container .text code { color: ${codeTextForegroundColor}; }`); 110 } @@ -321,24 +303,6 @@ src/vs/workbench/contrib/debug/browser/repl.ts: 311 font-size: ${fontSize}px; 312 font-family: ${fontFamily}; -src/vs/workbench/contrib/debug/browser/statusbarColorProvider.ts: - 84 this.styleElement = createStyleSheet(container); - 85 } - 86 - 87: this.styleElement.innerHTML = `.monaco-workbench .part.statusbar > .items-container > .statusbar-item.has-beak:before { border-bottom-color: ${backgroundColor} !important; }`; - 88 } - 89 - 90 private getColorKey(noFolderColor: string, debuggingColor: string, normalColor: string): string { - -src/vs/workbench/contrib/files/browser/views/explorerView.ts: - 812 - 813 const newStyles = content.join('\n'); - 814 if (newStyles !== this.styleElement.innerHTML) { - 815: this.styleElement.innerHTML = newStyles; - 816 } - 817 } - 818 - src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts: 222 223 const newStyles = content.join('\n'); @@ -358,29 +322,29 @@ src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts: 1039 src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts: - 443 - 444 private getMarkdownDragImage(templateData: MarkdownCellRenderTemplate): HTMLElement { - 445 const dragImageContainer = DOM.$('.cell-drag-image.monaco-list-row.focused.markdown-cell-row'); - 446: dragImageContainer.innerHTML = templateData.container.outerHTML; - 447 - 448 // Remove all rendered content nodes after the - 449 const markdownContent = dragImageContainer.querySelector('.cell.markdown')!; + 454 + 455 private getMarkdownDragImage(templateData: MarkdownCellRenderTemplate): HTMLElement { + 456 const dragImageContainer = DOM.$('.cell-drag-image.monaco-list-row.focused.markdown-cell-row'); + 457: dragImageContainer.innerHTML = templateData.container.outerHTML; + 458 + 459 // Remove all rendered content nodes after the + 460 const markdownContent = dragImageContainer.querySelector('.cell.markdown')!; - 587 - 588 private getDragImageImpl(templateData: BaseCellRenderTemplate, editor: ICodeEditor, type: 'code' | 'markdown'): HTMLElement | null { - 589 const dragImageContainer = DOM.$(`.cell-drag-image.monaco-list-row.focused.${type}-cell-row`); - 590: dragImageContainer.innerHTML = templateData.container.innerHTML; - 591 - 592 const editorContainer = dragImageContainer.querySelector('.cell-editor-container'); - 593 if (!editorContainer) { + 598 + 599 private getDragImageImpl(templateData: BaseCellRenderTemplate, editor: ICodeEditor, type: 'code' | 'markdown'): HTMLElement | null { + 600 const dragImageContainer = DOM.$(`.cell-drag-image.monaco-list-row.focused.${type}-cell-row`); + 601: dragImageContainer.innerHTML = templateData.container.innerHTML; + 602 + 603 const editorContainer = dragImageContainer.querySelector('.cell-editor-container'); + 604 if (!editorContainer) { - 599 return null; - 600 } - 601 - 602: editorContainer.innerHTML = richEditorText; - 603 - 604 return dragImageContainer; - 605 } + 610 return null; + 611 } + 612 + 613: editorContainer.innerHTML = richEditorText; + 614 + 615 return dragImageContainer; + 616 } src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts: 375 addMouseoverListeners(outputNode, outputId); @@ -409,15 +373,6 @@ src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts: 1306 .monaco-editor .dirty-diff-modified, .monaco-editor .dirty-diff-added, .monaco-editor .dirty-diff-deleted { 1307 opacity: ${state.visibility === 'always' ? 1 : 0}; -src/vs/workbench/contrib/watermark/browser/watermark.ts: - 141 const dd = dom.append(dl, $('dd')); - 142 const keybinding = new KeybindingLabel(dd, OS, { renderUnboundKeybindings: true }); - 143 keybinding.set(this.keybindingService.lookupKeybinding(entry.id)); - 144: dd.innerHTML = keybinding.element.outerHTML; - 145 }); - 146 }; - 147 - src/vs/workbench/contrib/webview/browser/webviewIconManager.ts: 61 } 62 } @@ -471,13 +426,13 @@ src/vs/workbench/services/textMate/browser/abstractTextMateService.ts: 318 src/vs/workbench/services/themes/browser/workbenchThemeService.ts: - 692 const elStyle = document.createElement('style'); - 693 elStyle.type = 'text/css'; - 694 elStyle.className = rulesClassName; - 695: elStyle.innerHTML = styleSheetContent; - 696 document.head.appendChild(elStyle); - 697 } else { - 698: (themeStyles[0]).innerHTML = styleSheetContent; - 699 } - 700 } - 701 + 685 const elStyle = document.createElement('style'); + 686 elStyle.type = 'text/css'; + 687 elStyle.className = rulesClassName; + 688: elStyle.innerHTML = styleSheetContent; + 689 document.head.appendChild(elStyle); + 690 } else { + 691: (themeStyles[0]).innerHTML = styleSheetContent; + 692 } + 693 } + 694 diff --git a/.vscode/searches/es6.code-search b/.vscode/searches/es6.code-search index 6ab0d14c5ac..54d4f0ea795 100644 --- a/.vscode/searches/es6.code-search +++ b/.vscode/searches/es6.code-search @@ -5,21 +5,21 @@ 14 results - 4 files src/vs/base/browser/dom.ts: - 81 }; - 82 - 83: /** @deprecated ES6 - use classList*/ - 84 export const hasClass: (node: HTMLElement | SVGElement, className: string) => boolean = _classList.hasClass.bind(_classList); + 83 }; + 84 85: /** @deprecated ES6 - use classList*/ - 86 export const addClass: (node: HTMLElement | SVGElement, className: string) => void = _classList.addClass.bind(_classList); + 86 export const hasClass: (node: HTMLElement | SVGElement, className: string) => boolean = _classList.hasClass.bind(_classList); 87: /** @deprecated ES6 - use classList*/ - 88 export const addClasses: (node: HTMLElement | SVGElement, ...classNames: string[]) => void = _classList.addClasses.bind(_classList); + 88 export const addClass: (node: HTMLElement | SVGElement, className: string) => void = _classList.addClass.bind(_classList); 89: /** @deprecated ES6 - use classList*/ - 90 export const removeClass: (node: HTMLElement | SVGElement, className: string) => void = _classList.removeClass.bind(_classList); + 90 export const addClasses: (node: HTMLElement | SVGElement, ...classNames: string[]) => void = _classList.addClasses.bind(_classList); 91: /** @deprecated ES6 - use classList*/ - 92 export const removeClasses: (node: HTMLElement | SVGElement, ...classNames: string[]) => void = _classList.removeClasses.bind(_classList); + 92 export const removeClass: (node: HTMLElement | SVGElement, className: string) => void = _classList.removeClass.bind(_classList); 93: /** @deprecated ES6 - use classList*/ - 94 export const toggleClass: (node: HTMLElement | SVGElement, className: string, shouldHaveIt?: boolean) => void = _classList.toggleClass.bind(_classList); - 95 + 94 export const removeClasses: (node: HTMLElement | SVGElement, ...classNames: string[]) => void = _classList.removeClasses.bind(_classList); + 95: /** @deprecated ES6 - use classList*/ + 96 export const toggleClass: (node: HTMLElement | SVGElement, className: string, shouldHaveIt?: boolean) => void = _classList.toggleClass.bind(_classList); + 97 src/vs/base/common/arrays.ts: 401 @@ -66,8 +66,8 @@ src/vs/base/common/strings.ts: 170 */ 171 export function endsWith(haystack: string, needle: string): boolean { - 861 - 862 /** - 863: * @deprecated ES6 - 864 */ - 865 export function repeat(s: string, count: number): string { + 857 + 858 /** + 859: * @deprecated ES6 + 860 */ + 861 export function repeat(s: string, count: number): string { From b1b2e09317712218c35b9268d240d9d44114a3fb Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 9 Sep 2020 10:00:40 +0200 Subject: [PATCH 12/71] fix https://github.com/microsoft/vscode/issues/105999 --- src/vs/vscode.d.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 680d2921f0f..5eef883dee0 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -3820,6 +3820,13 @@ declare module 'vscode' { * A string that should be used when comparing this item * with other items. When `falsy` the [label](#CompletionItem.label) * is used. + * + * Note that `sortText` is only used for the initial ordering of completion + * items. When having a leading word (prefix) ordering is based on how + * well completion match that prefix and the initial ordering is only used + * when completions match equal. The prefix is defined by the + * [`range`](#CompletionItem.range)-property and can therefore be different + * for each completion. */ sortText?: string; @@ -3827,6 +3834,10 @@ declare module 'vscode' { * A string that should be used when filtering a set of * completion items. When `falsy` the [label](#CompletionItem.label) * is used. + * + * Note that the filter text is matched against the leading word (prefix) which is defined + * by the [`range`](#CompletionItem.range)-property. + * prefix. */ filterText?: string; From 0b770073a4e48ef90c458f4d60dad07518c2f92c Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 9 Sep 2020 10:30:02 +0200 Subject: [PATCH 13/71] Reduce usage of `.innerHTML` (#106285) --- .../services/textMate/browser/abstractTextMateService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/textMate/browser/abstractTextMateService.ts b/src/vs/workbench/services/textMate/browser/abstractTextMateService.ts index 6567c19b739..d718890fc2b 100644 --- a/src/vs/workbench/services/textMate/browser/abstractTextMateService.ts +++ b/src/vs/workbench/services/textMate/browser/abstractTextMateService.ts @@ -312,7 +312,7 @@ export abstract class AbstractTextMateService extends Disposable implements ITex grammarFactory.setTheme(theme, tokenColorMap); let colorMap = AbstractTextMateService._toColorMap(tokenColorMap); let cssRules = generateTokensCSSForColorMap(colorMap); - this._styleElement.innerHTML = cssRules; + this._styleElement.textContent = cssRules; TokenizationRegistry.setColorMap(colorMap); } From a08267123a5cb8836fc43ac33c45475ef7cadfb6 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 9 Sep 2020 10:27:41 +0200 Subject: [PATCH 14/71] fix uninstalling extension --- .../extensionManagement/node/extensionManagementService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 9b0fd4db47e..fac9a19f475 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -678,8 +678,8 @@ export class ExtensionManagementService extends Disposable implements IExtension private setUninstalled(...extensions: ILocalExtension[]): Promise<{ [id: string]: boolean }> { const ids: ExtensionIdentifierWithVersion[] = extensions.map(e => new ExtensionIdentifierWithVersion(e.identifier, e.manifest.version)); return this.extensionsScanner.withUninstalledExtensions(uninstalled => { - const newUninstalled = (ids.reduce>((result, id) => { result[id.key()] = true; return result; }, {})); - return { ...uninstalled, ...newUninstalled }; + ids.forEach(id => uninstalled[id.key()] = true); + return uninstalled; }); } From 52a7a9ecfbf9c3bf62d41f669ccbbfadabbd7129 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 9 Sep 2020 10:39:46 +0200 Subject: [PATCH 15/71] remove unused import --- .../extensionManagement/node/extensionManagementService.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index fac9a19f475..5c1480c6bb3 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -45,7 +45,6 @@ import { IExtensionManifest, ExtensionType } from 'vs/platform/extensions/common import { ExtensionsDownloader } from 'vs/platform/extensionManagement/node/extensionDownloader'; import { ExtensionsScanner, IMetadata } from 'vs/platform/extensionManagement/node/extensionsScanner'; import { ExtensionsLifecycle } from 'vs/platform/extensionManagement/node/extensionLifecycle'; -import { IStringDictionary } from 'vs/base/common/collections'; const INSTALL_ERROR_UNSET_UNINSTALLED = 'unsetUninstalled'; const INSTALL_ERROR_DOWNLOADING = 'downloading'; From 951dd140b88b253f52a484c35909e36ef058be2b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 9 Sep 2020 10:27:59 +0200 Subject: [PATCH 16/71] add `replaceNotebookMetadata` (should become `replaceMetadata`) to NotebookEditorEdit, https://github.com/microsoft/vscode/issues/105283 --- src/vs/vscode.proposed.d.ts | 14 ++++-- .../api/browser/mainThreadNotebook.ts | 15 +++--- .../workbench/api/common/extHost.protocol.ts | 2 +- .../workbench/api/common/extHostNotebook.ts | 49 +++++++++++++------ .../contrib/notebook/common/notebookCommon.ts | 3 +- 5 files changed, 56 insertions(+), 27 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index b732e88f1ec..1a0d137e32b 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1355,12 +1355,18 @@ declare module 'vscode' { replaceCellMetadata(uri: Uri, index: number, cellMetadata: NotebookCellMetadata, metadata?: WorkspaceEditEntryMetadata): void; } - export interface NotebookEditorCellEdit { + export interface NotebookEditorEdit { + + replaceNotebookMetadata(value: NotebookDocumentMetadata): void; replaceCells(start: number, end: number, cells: NotebookCellData[]): void; - replaceOutput(index: number, outputs: CellOutput[]): void; - replaceMetadata(index: number, metadata: NotebookCellMetadata): void; + replaceCellOutput(index: number, outputs: CellOutput[]): void; + replaceCellMetadata(index: number, metadata: NotebookCellMetadata): void; + /** @deprecated */ + replaceOutput(index: number, outputs: CellOutput[]): void; + /** @deprecated */ + replaceMetadata(index: number, metadata: NotebookCellMetadata): void; /** @deprecated */ insert(index: number, content: string | string[], language: string, type: CellKind, outputs: CellOutput[], metadata: NotebookCellMetadata | undefined): void; /** @deprecated */ @@ -1448,7 +1454,7 @@ declare module 'vscode' { */ asWebviewUri(localResource: Uri): Uri; - edit(callback: (editBuilder: NotebookEditorCellEdit) => void): Thenable; + edit(callback: (editBuilder: NotebookEditorEdit) => void): Thenable; revealRange(range: NotebookCellRange, revealType?: NotebookEditorRevealType): void; } diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index ef92249a326..8bfadac04d7 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -153,14 +153,17 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo this.registerListeners(); } - async $tryApplyEdits(viewType: string, resource: UriComponents, modelVersionId: number, edits: ICellEditOperation[]): Promise { + async $tryApplyEdits(_viewType: string, resource: UriComponents, modelVersionId: number, cellEdits: ICellEditOperation[], newMetadata: NotebookDocumentMetadata | undefined): Promise { const textModel = this._notebookService.getNotebookTextModel(URI.from(resource)); - if (textModel) { - this._notebookService.transformEditsOutputs(textModel, edits); - return textModel.applyEdit(modelVersionId, edits, true); + if (!textModel) { + return false; } - - return false; + this._notebookService.transformEditsOutputs(textModel, cellEdits); + //TODO@rebornix should this go into applyEdit? + if (newMetadata) { + textModel.updateNotebookMetadata(newMetadata); + } + return textModel.applyEdit(modelVersionId, cellEdits, true); } async removeNotebookTextModel(uri: URI): Promise { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index da717e7f0e1..94cbcab2a90 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -738,7 +738,7 @@ export interface MainThreadNotebookShape extends IDisposable { $registerNotebookKernelProvider(extension: NotebookExtensionDescription, handle: number, documentFilter: INotebookDocumentFilter): Promise; $unregisterNotebookKernelProvider(handle: number): Promise; $onNotebookKernelChange(handle: number, uri: UriComponents | undefined): void; - $tryApplyEdits(viewType: string, resource: UriComponents, modelVersionId: number, edits: ICellEditOperation[]): Promise; + $tryApplyEdits(viewType: string, resource: UriComponents, modelVersionId: number, edits: ICellEditOperation[], metadata: NotebookDocumentMetadata | undefined): Promise; $updateNotebookLanguages(viewType: string, resource: UriComponents, languages: string[]): Promise; $updateNotebookMetadata(viewType: string, resource: UriComponents, metadata: NotebookDocumentMetadata): Promise; $updateNotebookCellMetadata(viewType: string, resource: UriComponents, handle: number, metadata: NotebookCellMetadata | undefined): Promise; diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 03e7cedad80..c1fe2a39f11 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -22,7 +22,7 @@ import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePa import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview'; -import { addIdToOutput, CellEditType, CellOutputKind, CellStatusbarAlignment, CellUri, diff, ICellEditOperation, ICellReplaceEdit, IMainCellDto, INotebookCellStatusBarEntry, INotebookDisplayOrder, INotebookEditData, INotebookKernelInfoDto2, IProcessedOutput, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookCellsSplice2, NotebookDataDto, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { addIdToOutput, CellEditType, CellOutputKind, CellStatusbarAlignment, CellUri, diff, ICellEditOperation, ICellReplaceEdit, IMainCellDto, INotebookCellStatusBarEntry, INotebookDisplayOrder, INotebookEditData, INotebookKernelInfoDto2, IProcessedOutput, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookCellsSplice2, NotebookDataDto, NotebookDocumentMetadata, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import * as vscode from 'vscode'; import { Cache } from './cache'; import { ResourceMap } from 'vs/base/common/map'; @@ -486,11 +486,13 @@ export class ExtHostNotebookDocument extends Disposable { } } -export class NotebookEditorCellEditBuilder implements vscode.NotebookEditorCellEdit { +export class NotebookEditorCellEditBuilder implements vscode.NotebookEditorEdit { private readonly _documentVersionId: number; - private readonly _collectedEdits: ICellEditOperation[] = []; + private _finalized: boolean = false; + private _collectedEdits: ICellEditOperation[] = []; + private _newNotebookDocumentMetadata?: NotebookDocumentMetadata; constructor(documentVersionId: number) { this._documentVersionId = documentVersionId; @@ -500,7 +502,8 @@ export class NotebookEditorCellEditBuilder implements vscode.NotebookEditorCellE this._finalized = true; return { documentVersionId: this._documentVersionId, - edits: this._collectedEdits, + cellEdits: this._collectedEdits, + newMetadata: this._newNotebookDocumentMetadata }; } @@ -510,7 +513,12 @@ export class NotebookEditorCellEditBuilder implements vscode.NotebookEditorCellE } } - replaceMetadata(index: number, metadata: vscode.NotebookCellMetadata): void { + replaceNotebookMetadata(value: vscode.NotebookDocumentMetadata): void { + this._throwIfFinalized(); + this._newNotebookDocumentMetadata = { ...notebookDocumentMetadataDefaults, ...value }; + } + + replaceCellMetadata(index: number, metadata: vscode.NotebookCellMetadata): void { this._throwIfFinalized(); this._collectedEdits.push({ editType: CellEditType.Metadata, @@ -519,7 +527,13 @@ export class NotebookEditorCellEditBuilder implements vscode.NotebookEditorCellE }); } - replaceOutput(index: number, outputs: vscode.CellOutput[]): void { + replaceMetadata(index: number, metadata: vscode.NotebookCellMetadata): void { + console.warn('DEPRECATED use "replaceCellMetadata" instead'); + this.replaceCellMetadata(index, metadata); + } + + + replaceCellOutput(index: number, outputs: vscode.CellOutput[]): void { this._throwIfFinalized(); this._collectedEdits.push({ editType: CellEditType.Output, @@ -528,6 +542,11 @@ export class NotebookEditorCellEditBuilder implements vscode.NotebookEditorCellE }); } + replaceOutput(index: number, outputs: vscode.CellOutput[]): void { + console.warn('DEPRECATED use "replaceCellOutput" instead'); + this.replaceCellOutput(index, outputs); + } + replaceCells(from: number, to: number, cells: vscode.NotebookCellData[]): void { this._throwIfFinalized(); @@ -693,16 +712,16 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook private _applyEdit(editData: INotebookEditData): Promise { // return when there is nothing to do - if (editData.edits.length === 0) { + if (editData.cellEdits.length === 0) { return Promise.resolve(true); } const compressedEdits: ICellEditOperation[] = []; let compressedEditsIndex = -1; - for (let i = 0; i < editData.edits.length; i++) { + for (let i = 0; i < editData.cellEdits.length; i++) { if (compressedEditsIndex < 0) { - compressedEdits.push(editData.edits[i]); + compressedEdits.push(editData.cellEdits[i]); compressedEditsIndex++; continue; } @@ -710,19 +729,19 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook const prevIndex = compressedEditsIndex; const prev = compressedEdits[prevIndex]; - if (prev.editType === CellEditType.Replace && editData.edits[i].editType === CellEditType.Replace) { - if (prev.index === editData.edits[i].index) { - prev.cells.push(...(editData.edits[i] as ICellReplaceEdit).cells); - prev.count += (editData.edits[i] as ICellReplaceEdit).count; + if (prev.editType === CellEditType.Replace && editData.cellEdits[i].editType === CellEditType.Replace) { + if (prev.index === editData.cellEdits[i].index) { + prev.cells.push(...(editData.cellEdits[i] as ICellReplaceEdit).cells); + prev.count += (editData.cellEdits[i] as ICellReplaceEdit).count; continue; } } - compressedEdits.push(editData.edits[i]); + compressedEdits.push(editData.cellEdits[i]); compressedEditsIndex++; } - return this._proxy.$tryApplyEdits(this.viewType, this.uri, editData.documentVersionId, compressedEdits); + return this._proxy.$tryApplyEdits(this.viewType, this.uri, editData.documentVersionId, compressedEdits, editData.newMetadata); } revealRange(range: vscode.NotebookCellRange, revealType?: extHostTypes.NotebookEditorRevealType) { diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 75b8abd0a88..66443e9d3ac 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -445,7 +445,8 @@ export type ICellEditOperation = ICellReplaceEdit | ICellOutputEdit | ICellMetad export interface INotebookEditData { documentVersionId: number; - edits: ICellEditOperation[]; + cellEdits: ICellEditOperation[]; + newMetadata?: NotebookDocumentMetadata; } export interface NotebookDataDto { From 420aa32929e2087969a9e4d005fe0137fd49a913 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 9 Sep 2020 10:38:11 +0200 Subject: [PATCH 17/71] add `replaceNotebookMetadata`, https://github.com/microsoft/vscode/issues/105283 --- src/vs/vscode.proposed.d.ts | 1 + .../api/browser/mainThreadEditors.ts | 2 +- .../workbench/api/common/extHost.protocol.ts | 5 ++-- .../api/common/extHostTypeConverters.ts | 5 ++-- src/vs/workbench/api/common/extHostTypes.ts | 9 ++++++-- .../contrib/bulkEdit/browser/bulkCellEdits.ts | 23 +++++++++++++++---- 6 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 1a0d137e32b..5741ff5aa85 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1350,6 +1350,7 @@ declare module 'vscode' { } export interface WorkspaceEdit { + replaceNotebookMetadata(uri: Uri, value: NotebookDocumentMetadata): void; replaceCells(uri: Uri, start: number, end: number, cells: NotebookCellData[], metadata?: WorkspaceEditEntryMetadata): void; replaceCellOutput(uri: Uri, index: number, outputs: CellOutput[], metadata?: WorkspaceEditEntryMetadata): void; replaceCellMetadata(uri: Uri, index: number, cellMetadata: NotebookCellMetadata, metadata?: WorkspaceEditEntryMetadata): void; diff --git a/src/vs/workbench/api/browser/mainThreadEditors.ts b/src/vs/workbench/api/browser/mainThreadEditors.ts index d8e199ac0ad..71d1e940085 100644 --- a/src/vs/workbench/api/browser/mainThreadEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadEditors.ts @@ -44,7 +44,7 @@ function reviveWorkspaceEditDto2(data: IWorkspaceEditDto | undefined): ResourceE } else if (edit._type === WorkspaceEditType.Text) { result.push(new ResourceTextEdit(edit.resource, edit.edit, edit.modelVersionId, edit.metadata)); } else if (edit._type === WorkspaceEditType.Cell) { - result.push(new ResourceNotebookCellEdit(edit.resource, edit.edit, edit.modelVersionId, edit.metadata)); + result.push(new ResourceNotebookCellEdit(edit.resource, edit.edit, edit.notebookMetadata, edit.notebookVersionId, edit.metadata)); } } return result; diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 94cbcab2a90..172c3111ff7 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1274,8 +1274,9 @@ export interface IWorkspaceTextEditDto { export interface IWorkspaceCellEditDto { _type: WorkspaceEditType.Cell; resource: UriComponents; - edit: ICellEditOperation; - modelVersionId?: number; + edit?: ICellEditOperation; + notebookMetadata?: NotebookDocumentMetadata; + notebookVersionId?: number; metadata?: IWorkspaceEditEntryMetadataDto; } diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index e5df10e69f8..da9bd09b20a 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -537,10 +537,11 @@ export namespace WorkspaceEdit { } else if (entry._type === types.FileEditType.Cell) { result.edits.push({ _type: extHostProtocol.WorkspaceEditType.Cell, + metadata: entry.metadata, resource: entry.uri, edit: entry.edit, - metadata: entry.metadata, - modelVersionId: notebooks?.lookupNotebookDocument(entry.uri)?.notebookDocument.version + notebookMetadata: entry.notebookMetadata, + notebookVersionId: notebooks?.lookupNotebookDocument(entry.uri)?.notebookDocument.version }); } } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index d832779db22..94638701e4f 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -601,7 +601,8 @@ export interface IFileTextEdit { export interface IFileCellEdit { _type: FileEditType.Cell; uri: URI; - edit: ICellEditOperation; + edit?: ICellEditOperation; + notebookMetadata?: vscode.NotebookDocumentMetadata; metadata?: vscode.WorkspaceEditEntryMetadata; } @@ -629,7 +630,11 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit { this._edits.push({ _type: FileEditType.File, from: uri, to: undefined, options, metadata }); } - // --- cell + // --- notebook + + replaceNotebookMetadata(uri: URI, value: vscode.NotebookDocumentMetadata, metadata?: vscode.WorkspaceEditEntryMetadata): void { + this._edits.push({ _type: FileEditType.Cell, metadata, uri, notebookMetadata: value }); + } replaceCells(uri: URI, start: number, end: number, cells: vscode.NotebookCellData[], metadata?: vscode.WorkspaceEditEntryMetadata): void { this._edits.push({ _type: FileEditType.Cell, metadata, uri, edit: { editType: CellEditType.Replace, index: start, count: end - start, cells: cells.map(cell => ({ ...cell, outputs: cell.outputs.map(output => addIdToOutput(output)) })) } }); diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkCellEdits.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkCellEdits.ts index 5def1d910c2..41ab95fd25e 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkCellEdits.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkCellEdits.ts @@ -9,7 +9,7 @@ import { URI } from 'vs/base/common/uri'; import { ResourceEdit } from 'vs/editor/browser/services/bulkEditService'; import { WorkspaceEditMetadata } from 'vs/editor/common/modes'; import { IProgress } from 'vs/platform/progress/common/progress'; -import { ICellEditOperation } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ICellEditOperation, NotebookDocumentMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; @@ -17,7 +17,8 @@ export class ResourceNotebookCellEdit extends ResourceEdit { constructor( readonly resource: URI, - readonly cellEdit: ICellEditOperation, + readonly cellEdit?: ICellEditOperation, + readonly notebookMetadata?: NotebookDocumentMetadata, readonly versionId?: number, readonly metadata?: WorkspaceEditMetadata ) { @@ -48,10 +49,22 @@ export class BulkCellEdits { // throw new Error(`Notebook '${first.resource}' has changed in the meantime`); // } + const edits: ICellEditOperation[] = []; + let newMetadata: NotebookDocumentMetadata | undefined; + for (let edit of group) { + if (edit.cellEdit) { + edits.push(edit.cellEdit); + } + newMetadata = edit.notebookMetadata ?? newMetadata; + } + + // set metadata + if (newMetadata) { + ref.object.notebook.updateNotebookMetadata(newMetadata); + } // apply edits - const cellEdits = group.map(edit => edit.cellEdit); - this._notebookService.transformEditsOutputs(ref.object.notebook, cellEdits); - ref.object.notebook.applyEdit(ref.object.notebook.versionId, cellEdits, true); + this._notebookService.transformEditsOutputs(ref.object.notebook, edits); + ref.object.notebook.applyEdit(ref.object.notebook.versionId, edits, true); ref.dispose(); this._progress.report(undefined); From f0dd2d26ed4778443263f4073fc1a658b0707788 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 9 Sep 2020 10:45:59 +0200 Subject: [PATCH 18/71] use textContent instead of innerHTML (for #106285) --- src/vs/base/browser/ui/codicons/codiconStyles.ts | 2 +- .../services/themes/browser/workbenchThemeService.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/base/browser/ui/codicons/codiconStyles.ts b/src/vs/base/browser/ui/codicons/codiconStyles.ts index b3dc12fb3f3..59dbdbe873a 100644 --- a/src/vs/base/browser/ui/codicons/codiconStyles.ts +++ b/src/vs/base/browser/ui/codicons/codiconStyles.ts @@ -20,7 +20,7 @@ function initialize() { for (let c of iconRegistry.all) { rules.push(formatRule(c)); } - codiconStyleSheet.innerHTML = rules.join('\n'); + codiconStyleSheet.textContent = rules.join('\n'); } const delayer = new RunOnceScheduler(updateAll, 0); diff --git a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts index 39c18b0c7d3..cbce827b1db 100644 --- a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts @@ -685,10 +685,10 @@ function _applyRules(styleSheetContent: string, rulesClassName: string) { const elStyle = document.createElement('style'); elStyle.type = 'text/css'; elStyle.className = rulesClassName; - elStyle.innerHTML = styleSheetContent; + elStyle.textContent = styleSheetContent; document.head.appendChild(elStyle); } else { - (themeStyles[0]).innerHTML = styleSheetContent; + (themeStyles[0]).textContent = styleSheetContent; } } From dcef262f4e41a5fd3e730652d908e56195714d84 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 9 Sep 2020 11:18:14 +0200 Subject: [PATCH 19/71] chore - move appyWorkspaceEdit from extHostTextEditors to (new) extHostBulkEdits and mainThreadBulkEdits --- .../api/browser/extensionHost.contribution.ts | 1 + .../api/browser/mainThreadBulkEdits.ts | 44 +++++++++++++++++++ .../workbench/api/common/extHost.api.impl.ts | 8 ++-- .../workbench/api/common/extHost.protocol.ts | 6 ++- .../workbench/api/common/extHostBulkEdits.ts | 29 ++++++++++++ .../common/extHostDocumentSaveParticipant.ts | 6 +-- .../common/extHostFileSystemEventService.ts | 6 +-- .../api/common/extHostTextEditors.ts | 7 --- ...itors.test.ts => extHostBulkEdits.test.ts} | 16 +++---- .../extHostDocumentSaveParticipant.test.ts | 30 ++++++------- .../browser/api/extHostTextEditor.test.ts | 1 - 11 files changed, 113 insertions(+), 41 deletions(-) create mode 100644 src/vs/workbench/api/browser/mainThreadBulkEdits.ts create mode 100644 src/vs/workbench/api/common/extHostBulkEdits.ts rename src/vs/workbench/test/browser/api/{extHostTextEditors.test.ts => extHostBulkEdits.test.ts} (80%) diff --git a/src/vs/workbench/api/browser/extensionHost.contribution.ts b/src/vs/workbench/api/browser/extensionHost.contribution.ts index bfabf000891..9e264fb33b9 100644 --- a/src/vs/workbench/api/browser/extensionHost.contribution.ts +++ b/src/vs/workbench/api/browser/extensionHost.contribution.ts @@ -15,6 +15,7 @@ import { TokenClassificationExtensionPoints } from 'vs/workbench/services/themes import { LanguageConfigurationFileHandler } from 'vs/workbench/contrib/codeEditor/browser/languageConfigurationExtensionPoint'; // --- mainThread participants +import './mainThreadBulkEdits'; import './mainThreadCodeInsets'; import './mainThreadClipboard'; import './mainThreadCommands'; diff --git a/src/vs/workbench/api/browser/mainThreadBulkEdits.ts b/src/vs/workbench/api/browser/mainThreadBulkEdits.ts new file mode 100644 index 00000000000..2c10477e0d1 --- /dev/null +++ b/src/vs/workbench/api/browser/mainThreadBulkEdits.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 { IBulkEditService, ResourceEdit, ResourceFileEdit, ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService'; +import { IExtHostContext, IWorkspaceEditDto, WorkspaceEditType, MainThreadBulkEditsShape, MainContext } from 'vs/workbench/api/common/extHost.protocol'; +import { revive } from 'vs/base/common/marshalling'; +import { ResourceNotebookCellEdit } from 'vs/workbench/contrib/bulkEdit/browser/bulkCellEdits'; +import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; + +function reviveWorkspaceEditDto2(data: IWorkspaceEditDto | undefined): ResourceEdit[] { + if (!data?.edits) { + return []; + } + + const result: ResourceEdit[] = []; + for (let edit of revive(data).edits) { + if (edit._type === WorkspaceEditType.File) { + result.push(new ResourceFileEdit(edit.oldUri, edit.newUri, edit.options, edit.metadata)); + } else if (edit._type === WorkspaceEditType.Text) { + result.push(new ResourceTextEdit(edit.resource, edit.edit, edit.modelVersionId, edit.metadata)); + } else if (edit._type === WorkspaceEditType.Cell) { + result.push(new ResourceNotebookCellEdit(edit.resource, edit.edit, edit.notebookMetadata, edit.notebookVersionId, edit.metadata)); + } + } + return result; +} + +@extHostNamedCustomer(MainContext.MainThreadBulkEdits) +export class MainThreadBulkEdits implements MainThreadBulkEditsShape { + + constructor( + _extHostContext: IExtHostContext, + @IBulkEditService private readonly _bulkEditService: IBulkEditService, + ) { } + + dispose(): void { } + + $tryApplyWorkspaceEdit(dto: IWorkspaceEditDto): Promise { + const edits = reviveWorkspaceEditDto2(dto); + return this._bulkEditService.apply(edits).then(() => true, _err => false); + } +} diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 3595cd3e381..e5e5b3cc88d 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -79,6 +79,7 @@ import { IExtHostConsumerFileSystem } from 'vs/workbench/api/common/extHostFileS import { ExtHostWebviewViews } from 'vs/workbench/api/common/extHostWebviewView'; import { ExtHostCustomEditors } from 'vs/workbench/api/common/extHostCustomEditors'; import { ExtHostWebviewPanels } from 'vs/workbench/api/common/extHostWebviewPanels'; +import { ExtHostBulkEdits } from 'vs/workbench/api/common/extHostBulkEdits'; export interface IExtensionApiFactory { (extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode; @@ -127,9 +128,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostUrls = rpcProtocol.set(ExtHostContext.ExtHostUrls, new ExtHostUrls(rpcProtocol)); const extHostDocuments = rpcProtocol.set(ExtHostContext.ExtHostDocuments, new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors)); const extHostDocumentContentProviders = rpcProtocol.set(ExtHostContext.ExtHostDocumentContentProviders, new ExtHostDocumentContentProvider(rpcProtocol, extHostDocumentsAndEditors, extHostLogService)); - const extHostDocumentSaveParticipant = rpcProtocol.set(ExtHostContext.ExtHostDocumentSaveParticipant, new ExtHostDocumentSaveParticipant(extHostLogService, extHostDocuments, rpcProtocol.getProxy(MainContext.MainThreadTextEditors))); + const extHostDocumentSaveParticipant = rpcProtocol.set(ExtHostContext.ExtHostDocumentSaveParticipant, new ExtHostDocumentSaveParticipant(extHostLogService, extHostDocuments, rpcProtocol.getProxy(MainContext.MainThreadBulkEdits))); const extHostNotebook = rpcProtocol.set(ExtHostContext.ExtHostNotebook, new ExtHostNotebookController(rpcProtocol, extHostCommands, extHostDocumentsAndEditors, initData.environment, extHostLogService, extensionStoragePaths)); - const extHostEditors = rpcProtocol.set(ExtHostContext.ExtHostEditors, new ExtHostEditors(rpcProtocol, extHostDocumentsAndEditors, extHostNotebook)); + const extHostEditors = rpcProtocol.set(ExtHostContext.ExtHostEditors, new ExtHostEditors(rpcProtocol, extHostDocumentsAndEditors)); const extHostTreeViews = rpcProtocol.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(rpcProtocol.getProxy(MainContext.MainThreadTreeViews), extHostCommands, extHostLogService)); const extHostEditorInsets = rpcProtocol.set(ExtHostContext.ExtHostEditorInsets, new ExtHostEditorInsets(rpcProtocol.getProxy(MainContext.MainThreadEditorInsets), extHostEditors, initData.environment)); const extHostDiagnostics = rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, new ExtHostDiagnostics(rpcProtocol, extHostLogService)); @@ -154,6 +155,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I rpcProtocol.assertRegistered(expected); // Other instances + const extHostBulkEdits = new ExtHostBulkEdits(rpcProtocol, extHostDocumentsAndEditors, extHostNotebook); const extHostClipboard = new ExtHostClipboard(rpcProtocol); const extHostMessageService = new ExtHostMessageService(rpcProtocol, extHostLogService); const extHostDialogs = new ExtHostDialogs(rpcProtocol); @@ -691,7 +693,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostWorkspace.saveAll(includeUntitled); }, applyEdit(edit: vscode.WorkspaceEdit): Thenable { - return extHostEditors.applyWorkspaceEdit(edit); + return extHostBulkEdits.applyWorkspaceEdit(edit); }, createFileSystemWatcher: (pattern, ignoreCreate, ignoreChange, ignoreDelete): vscode.FileSystemWatcher => { return extHostFileSystemEvent.createFileSystemWatcher(typeConverters.GlobPattern.from(pattern), ignoreCreate, ignoreChange, ignoreDelete); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 172c3111ff7..a6387986941 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -263,6 +263,10 @@ export interface ITextDocumentShowOptions { selection?: IRange; } +export interface MainThreadBulkEditsShape extends IDisposable { + $tryApplyWorkspaceEdit(workspaceEditDto: IWorkspaceEditDto): Promise; +} + export interface MainThreadTextEditorsShape extends IDisposable { $tryShowTextDocument(resource: UriComponents, options: ITextDocumentShowOptions): Promise; $registerTextEditorDecorationType(key: string, options: editorCommon.IDecorationRenderOptions): void; @@ -275,7 +279,6 @@ export interface MainThreadTextEditorsShape extends IDisposable { $tryRevealRange(id: string, range: IRange, revealType: TextEditorRevealType): Promise; $trySetSelections(id: string, selections: ISelection[]): Promise; $tryApplyEdits(id: string, modelVersionId: number, edits: ISingleEditOperation[], opts: IApplyEditsOptions): Promise; - $tryApplyWorkspaceEdit(workspaceEditDto: IWorkspaceEditDto): Promise; $tryInsertSnippet(id: string, template: string, selections: readonly IRange[], opts: IUndoStopOptions): Promise; $getDiffInformation(id: string): Promise; } @@ -1726,6 +1729,7 @@ export interface ExtHostTimelineShape { export const MainContext = { MainThreadAuthentication: createMainId('MainThreadAuthentication'), + MainThreadBulkEdits: createMainId('MainThreadBulkEdits'), MainThreadClipboard: createMainId('MainThreadClipboard'), MainThreadCommands: createMainId('MainThreadCommands'), MainThreadComments: createMainId('MainThreadComments'), diff --git a/src/vs/workbench/api/common/extHostBulkEdits.ts b/src/vs/workbench/api/common/extHostBulkEdits.ts new file mode 100644 index 00000000000..c521b1e6974 --- /dev/null +++ b/src/vs/workbench/api/common/extHostBulkEdits.ts @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { MainContext, MainThreadBulkEditsShape } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; +import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook'; +import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; +import { WorkspaceEdit } from 'vs/workbench/api/common/extHostTypeConverters'; +import type * as vscode from 'vscode'; + +export class ExtHostBulkEdits { + + private readonly _proxy: MainThreadBulkEditsShape; + + constructor( + @IExtHostRpcService extHostRpc: IExtHostRpcService, + private readonly _extHostDocumentsAndEditors: ExtHostDocumentsAndEditors, + private readonly _extHostNotebooks: ExtHostNotebookController, + ) { + this._proxy = extHostRpc.getProxy(MainContext.MainThreadBulkEdits); + } + + applyWorkspaceEdit(edit: vscode.WorkspaceEdit): Promise { + const dto = WorkspaceEdit.from(edit, this._extHostDocumentsAndEditors, this._extHostNotebooks); + return this._proxy.$tryApplyWorkspaceEdit(dto); + } +} diff --git a/src/vs/workbench/api/common/extHostDocumentSaveParticipant.ts b/src/vs/workbench/api/common/extHostDocumentSaveParticipant.ts index 5613e66db18..fe6fd330378 100644 --- a/src/vs/workbench/api/common/extHostDocumentSaveParticipant.ts +++ b/src/vs/workbench/api/common/extHostDocumentSaveParticipant.ts @@ -6,7 +6,7 @@ import { Event } from 'vs/base/common/event'; import { URI, UriComponents } from 'vs/base/common/uri'; import { illegalState } from 'vs/base/common/errors'; -import { ExtHostDocumentSaveParticipantShape, MainThreadTextEditorsShape, IWorkspaceEditDto, WorkspaceEditType } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostDocumentSaveParticipantShape, IWorkspaceEditDto, WorkspaceEditType, MainThreadBulkEditsShape } from 'vs/workbench/api/common/extHost.protocol'; import { TextEdit } from 'vs/workbench/api/common/extHostTypes'; import { Range, TextDocumentSaveReason, EndOfLine } from 'vs/workbench/api/common/extHostTypeConverters'; import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; @@ -26,7 +26,7 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic constructor( private readonly _logService: ILogService, private readonly _documents: ExtHostDocuments, - private readonly _mainThreadEditors: MainThreadTextEditorsShape, + private readonly _mainThreadBulkEdits: MainThreadBulkEditsShape, private readonly _thresholds: { timeout: number; errors: number; } = { timeout: 1500, errors: 3 } ) { // @@ -165,7 +165,7 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic } if (version === document.version) { - return this._mainThreadEditors.$tryApplyWorkspaceEdit(dto); + return this._mainThreadBulkEdits.$tryApplyWorkspaceEdit(dto); } return Promise.reject(new Error('concurrent_edits')); diff --git a/src/vs/workbench/api/common/extHostFileSystemEventService.ts b/src/vs/workbench/api/common/extHostFileSystemEventService.ts index 76a5f2f81e9..c576f8f5e4c 100644 --- a/src/vs/workbench/api/common/extHostFileSystemEventService.ts +++ b/src/vs/workbench/api/common/extHostFileSystemEventService.ts @@ -8,7 +8,7 @@ import { IRelativePattern, parse } from 'vs/base/common/glob'; import { URI } from 'vs/base/common/uri'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import type * as vscode from 'vscode'; -import { ExtHostFileSystemEventServiceShape, FileSystemEvents, IMainContext, MainContext, MainThreadTextEditorsShape, SourceTargetPair, IWorkspaceEditDto } from './extHost.protocol'; +import { ExtHostFileSystemEventServiceShape, FileSystemEvents, IMainContext, MainContext, SourceTargetPair, IWorkspaceEditDto, MainThreadBulkEditsShape } from './extHost.protocol'; import * as typeConverter from './extHostTypeConverters'; import { Disposable, WorkspaceEdit } from './extHostTypes'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; @@ -123,7 +123,7 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ mainContext: IMainContext, private readonly _logService: ILogService, private readonly _extHostDocumentsAndEditors: ExtHostDocumentsAndEditors, - private readonly _mainThreadTextEditors: MainThreadTextEditorsShape = mainContext.getProxy(MainContext.MainThreadTextEditors) + private readonly _mainThreadBulkEdits: MainThreadBulkEditsShape = mainContext.getProxy(MainContext.MainThreadBulkEdits) ) { // } @@ -222,7 +222,7 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ let { edits } = typeConverter.WorkspaceEdit.from(edit, this._extHostDocumentsAndEditors); dto.edits = dto.edits.concat(edits); } - return this._mainThreadTextEditors.$tryApplyWorkspaceEdit(dto); + return this._mainThreadBulkEdits.$tryApplyWorkspaceEdit(dto); } } } diff --git a/src/vs/workbench/api/common/extHostTextEditors.ts b/src/vs/workbench/api/common/extHostTextEditors.ts index a2ba800bf5c..3e95b2f382e 100644 --- a/src/vs/workbench/api/common/extHostTextEditors.ts +++ b/src/vs/workbench/api/common/extHostTextEditors.ts @@ -7,7 +7,6 @@ import { Emitter, Event } from 'vs/base/common/event'; import * as arrays from 'vs/base/common/arrays'; import { ExtHostEditorsShape, IEditorPropertiesChangeData, IMainContext, ITextDocumentShowOptions, ITextEditorPositionData, MainContext, MainThreadTextEditorsShape } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; -import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook'; import { ExtHostTextEditor, TextEditorDecorationType } from 'vs/workbench/api/common/extHostTextEditor'; import * as TypeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import { TextEditorSelectionChangeKind } from 'vs/workbench/api/common/extHostTypes'; @@ -34,7 +33,6 @@ export class ExtHostEditors implements ExtHostEditorsShape { constructor( mainContext: IMainContext, private readonly _extHostDocumentsAndEditors: ExtHostDocumentsAndEditors, - private readonly _extHostNotebooks: ExtHostNotebookController, ) { this._proxy = mainContext.getProxy(MainContext.MainThreadTextEditors); @@ -92,11 +90,6 @@ export class ExtHostEditors implements ExtHostEditorsShape { return new TextEditorDecorationType(this._proxy, options); } - applyWorkspaceEdit(edit: vscode.WorkspaceEdit): Promise { - const dto = TypeConverters.WorkspaceEdit.from(edit, this._extHostDocumentsAndEditors, this._extHostNotebooks); - return this._proxy.$tryApplyWorkspaceEdit(dto); - } - // --- called from main thread $acceptEditorPropertiesChanged(id: string, data: IEditorPropertiesChangeData): void { diff --git a/src/vs/workbench/test/browser/api/extHostTextEditors.test.ts b/src/vs/workbench/test/browser/api/extHostBulkEdits.test.ts similarity index 80% rename from src/vs/workbench/test/browser/api/extHostTextEditors.test.ts rename to src/vs/workbench/test/browser/api/extHostBulkEdits.test.ts index a1778a00358..f6adea9dd43 100644 --- a/src/vs/workbench/test/browser/api/extHostTextEditors.test.ts +++ b/src/vs/workbench/test/browser/api/extHostBulkEdits.test.ts @@ -4,26 +4,26 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; -import { MainContext, MainThreadTextEditorsShape, IWorkspaceEditDto, WorkspaceEditType } from 'vs/workbench/api/common/extHost.protocol'; +import { MainContext, IWorkspaceEditDto, WorkspaceEditType, MainThreadBulkEditsShape } from 'vs/workbench/api/common/extHost.protocol'; import { URI } from 'vs/base/common/uri'; import { mock } from 'vs/base/test/common/mock'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import { SingleProxyRPCProtocol, TestRPCProtocol } from 'vs/workbench/test/browser/api/testRPCProtocol'; -import { ExtHostEditors } from 'vs/workbench/api/common/extHostTextEditors'; import { NullLogService } from 'vs/platform/log/common/log'; import { assertType } from 'vs/base/common/types'; +import { ExtHostBulkEdits } from 'vs/workbench/api/common/extHostBulkEdits'; -suite('ExtHostTextEditors.applyWorkspaceEdit', () => { +suite('ExtHostBulkEdits.applyWorkspaceEdit', () => { const resource = URI.parse('foo:bar'); - let editors: ExtHostEditors; + let bulkEdits: ExtHostBulkEdits; let workspaceResourceEdits: IWorkspaceEditDto; setup(() => { workspaceResourceEdits = null!; let rpcProtocol = new TestRPCProtocol(); - rpcProtocol.set(MainContext.MainThreadTextEditors, new class extends mock() { + rpcProtocol.set(MainContext.MainThreadBulkEdits, new class extends mock() { $tryApplyWorkspaceEdit(_workspaceResourceEdits: IWorkspaceEditDto): Promise { workspaceResourceEdits = _workspaceResourceEdits; return Promise.resolve(true); @@ -40,13 +40,13 @@ suite('ExtHostTextEditors.applyWorkspaceEdit', () => { EOL: '\n', }] }); - editors = new ExtHostEditors(rpcProtocol, documentsAndEditors, null!); + bulkEdits = new ExtHostBulkEdits(rpcProtocol, documentsAndEditors, null!); }); test('uses version id if document available', async () => { let edit = new extHostTypes.WorkspaceEdit(); edit.replace(resource, new extHostTypes.Range(0, 0, 0, 0), 'hello'); - await editors.applyWorkspaceEdit(edit); + await bulkEdits.applyWorkspaceEdit(edit); assert.equal(workspaceResourceEdits.edits.length, 1); const [first] = workspaceResourceEdits.edits; assertType(first._type === WorkspaceEditType.Text); @@ -56,7 +56,7 @@ suite('ExtHostTextEditors.applyWorkspaceEdit', () => { test('does not use version id if document is not available', async () => { let edit = new extHostTypes.WorkspaceEdit(); edit.replace(URI.parse('foo:bar2'), new extHostTypes.Range(0, 0, 0, 0), 'hello'); - await editors.applyWorkspaceEdit(edit); + await bulkEdits.applyWorkspaceEdit(edit); assert.equal(workspaceResourceEdits.edits.length, 1); const [first] = workspaceResourceEdits.edits; assertType(first._type === WorkspaceEditType.Text); diff --git a/src/vs/workbench/test/browser/api/extHostDocumentSaveParticipant.test.ts b/src/vs/workbench/test/browser/api/extHostDocumentSaveParticipant.test.ts index 855f92b34de..7a9a6ddc45b 100644 --- a/src/vs/workbench/test/browser/api/extHostDocumentSaveParticipant.test.ts +++ b/src/vs/workbench/test/browser/api/extHostDocumentSaveParticipant.test.ts @@ -7,7 +7,7 @@ import { URI } from 'vs/base/common/uri'; import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import { TextDocumentSaveReason, TextEdit, Position, EndOfLine } from 'vs/workbench/api/common/extHostTypes'; -import { MainThreadTextEditorsShape, IWorkspaceEditDto, IWorkspaceTextEditDto } from 'vs/workbench/api/common/extHost.protocol'; +import { MainThreadTextEditorsShape, IWorkspaceEditDto, IWorkspaceTextEditDto, MainThreadBulkEditsShape } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostDocumentSaveParticipant } from 'vs/workbench/api/common/extHostDocumentSaveParticipant'; import { SingleProxyRPCProtocol } from './testRPCProtocol'; import { SaveReason } from 'vs/workbench/common/editor'; @@ -20,7 +20,7 @@ import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensio suite('ExtHostDocumentSaveParticipant', () => { let resource = URI.parse('foo:bar'); - let mainThreadEditors = new class extends mock() { }; + let mainThreadBulkEdits = new class extends mock() { }; let documents: ExtHostDocuments; let nullLogService = new NullLogService(); let nullExtensionDescription: IExtensionDescription = { @@ -51,12 +51,12 @@ suite('ExtHostDocumentSaveParticipant', () => { }); test('no listeners, no problem', () => { - const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadEditors); + const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadBulkEdits); return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(() => assert.ok(true)); }); test('event delivery', () => { - const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadEditors); + const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadBulkEdits); let event: vscode.TextDocumentWillSaveEvent; let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) { @@ -73,7 +73,7 @@ suite('ExtHostDocumentSaveParticipant', () => { }); test('event delivery, immutable', () => { - const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadEditors); + const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadBulkEdits); let event: vscode.TextDocumentWillSaveEvent; let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) { @@ -89,7 +89,7 @@ suite('ExtHostDocumentSaveParticipant', () => { }); test('event delivery, bad listener', () => { - const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadEditors); + const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadBulkEdits); let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) { throw new Error('💀'); @@ -104,7 +104,7 @@ suite('ExtHostDocumentSaveParticipant', () => { }); test('event delivery, bad listener doesn\'t prevent more events', () => { - const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadEditors); + const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadBulkEdits); let sub1 = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) { throw new Error('💀'); @@ -123,7 +123,7 @@ suite('ExtHostDocumentSaveParticipant', () => { }); test('event delivery, in subscriber order', () => { - const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadEditors); + const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadBulkEdits); let counter = 0; let sub1 = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (event) { @@ -141,7 +141,7 @@ suite('ExtHostDocumentSaveParticipant', () => { }); test('event delivery, ignore bad listeners', async () => { - const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadEditors, { timeout: 5, errors: 1 }); + const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadBulkEdits, { timeout: 5, errors: 1 }); let callCount = 0; let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (event) { @@ -159,7 +159,7 @@ suite('ExtHostDocumentSaveParticipant', () => { }); test('event delivery, overall timeout', () => { - const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadEditors, { timeout: 20, errors: 5 }); + const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadBulkEdits, { timeout: 20, errors: 5 }); let callCount = 0; let sub1 = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (event) { @@ -187,7 +187,7 @@ suite('ExtHostDocumentSaveParticipant', () => { }); test('event delivery, waitUntil', () => { - const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadEditors); + const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadBulkEdits); let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (event) { @@ -203,7 +203,7 @@ suite('ExtHostDocumentSaveParticipant', () => { }); test('event delivery, waitUntil must be called sync', () => { - const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadEditors); + const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadBulkEdits); let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (event) { @@ -226,7 +226,7 @@ suite('ExtHostDocumentSaveParticipant', () => { }); test('event delivery, waitUntil will timeout', () => { - const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadEditors, { timeout: 5, errors: 3 }); + const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadBulkEdits, { timeout: 5, errors: 3 }); let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (event) { event.waitUntil(timeout(15)); @@ -241,7 +241,7 @@ suite('ExtHostDocumentSaveParticipant', () => { }); test('event delivery, waitUntil failure handling', () => { - const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadEditors); + const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadBulkEdits); let sub1 = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) { e.waitUntil(Promise.reject(new Error('dddd'))); @@ -380,7 +380,7 @@ suite('ExtHostDocumentSaveParticipant', () => { error(message: string | Error, ...args: any[]): void { didLogSomething = true; } - }, documents, mainThreadEditors); + }, documents, mainThreadBulkEdits); let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) { diff --git a/src/vs/workbench/test/browser/api/extHostTextEditor.test.ts b/src/vs/workbench/test/browser/api/extHostTextEditor.test.ts index dc844a5273a..931313c55e9 100644 --- a/src/vs/workbench/test/browser/api/extHostTextEditor.test.ts +++ b/src/vs/workbench/test/browser/api/extHostTextEditor.test.ts @@ -84,7 +84,6 @@ suite('ExtHostTextEditorOptions', () => { $tryRevealRange: undefined!, $trySetSelections: undefined!, $tryApplyEdits: undefined!, - $tryApplyWorkspaceEdit: undefined!, $tryInsertSnippet: undefined!, $getDiffInformation: undefined! }; From 6e35aa3cebf0713d5945574530dea49a49124059 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 9 Sep 2020 11:38:20 +0200 Subject: [PATCH 20/71] chore - extract extHostNotebookDocument for the NotebookDocument and NotebookCell types and friends --- .../workbench/api/common/extHostNotebook.ts | 480 +---------------- .../api/common/extHostNotebookDocument.ts | 482 ++++++++++++++++++ .../test/browser/api/extHostNotebook.test.ts | 3 +- .../api/extHostNotebookConcatDocument.test.ts | 3 +- 4 files changed, 492 insertions(+), 476 deletions(-) create mode 100644 src/vs/workbench/api/common/extHostNotebookDocument.ts diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index c1fe2a39f11..5025eefc64a 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -6,485 +6,22 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { readonly } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; -import { hash } from 'vs/base/common/hash'; -import { Disposable, DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle'; -import { Schemas } from 'vs/base/common/network'; -import { joinPath } from 'vs/base/common/resources'; -import { ISplice } from 'vs/base/common/sequence'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; import * as UUID from 'vs/base/common/uuid'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { CellKind, ExtHostNotebookShape, ICommandDto, IMainContext, IModelAddedData, INotebookDocumentPropertiesChangeData, INotebookDocumentsAndEditorsDelta, INotebookEditorPropertiesChangeData, MainContext, MainThreadNotebookShape, NotebookCellOutputsSplice } from 'vs/workbench/api/common/extHost.protocol'; +import { CellKind, ExtHostNotebookShape, ICommandDto, IMainContext, IModelAddedData, INotebookDocumentPropertiesChangeData, INotebookDocumentsAndEditorsDelta, INotebookEditorPropertiesChangeData, MainContext, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol'; import { ILogService } from 'vs/platform/log/common/log'; import { CommandsConverter, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; -import { ExtHostDocumentsAndEditors, IExtHostModelAddedData } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; +import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview'; -import { addIdToOutput, CellEditType, CellOutputKind, CellStatusbarAlignment, CellUri, diff, ICellEditOperation, ICellReplaceEdit, IMainCellDto, INotebookCellStatusBarEntry, INotebookDisplayOrder, INotebookEditData, INotebookKernelInfoDto2, IProcessedOutput, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookCellsSplice2, NotebookDataDto, NotebookDocumentMetadata, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { addIdToOutput, CellEditType, CellStatusbarAlignment, CellUri, ICellEditOperation, ICellReplaceEdit, INotebookCellStatusBarEntry, INotebookDisplayOrder, INotebookEditData, INotebookKernelInfoDto2, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookDataDto, NotebookDocumentMetadata, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import * as vscode from 'vscode'; -import { Cache } from './cache'; import { ResourceMap } from 'vs/base/common/map'; - -interface IObservable { - proxy: T; - onDidChange: Event; -} - -function getObservable(obj: T): IObservable { - const onDidChange = new Emitter(); - const proxy = new Proxy(obj, { - set(target: T, p: PropertyKey, value: any, _receiver: any): boolean { - target[p as keyof T] = value; - onDidChange.fire(); - return true; - } - }); - - return { - proxy, - onDidChange: onDidChange.event - }; -} - -interface INotebookEventEmitter { - emitModelChange(events: vscode.NotebookCellsChangeEvent): void; - emitCellOutputsChange(event: vscode.NotebookCellOutputsChangeEvent): void; - emitCellLanguageChange(event: vscode.NotebookCellLanguageChangeEvent): void; - emitCellMetadataChange(event: vscode.NotebookCellMetadataChangeEvent): void; -} - -export class ExtHostCell extends Disposable { - - public static asModelAddData(notebook: vscode.NotebookDocument, cell: IMainCellDto): IExtHostModelAddedData { - return { - EOL: cell.eol, - lines: cell.source, - modeId: cell.language, - uri: cell.uri, - isDirty: false, - versionId: 1, - notebook - }; - } - - private _onDidDispose = new Emitter(); - readonly onDidDispose: Event = this._onDidDispose.event; - - private _onDidChangeOutputs = new Emitter[]>(); - readonly onDidChangeOutputs: Event[]> = this._onDidChangeOutputs.event; - - private _outputs: any[]; - private _outputMapping = new WeakMap(); - - private _metadata: vscode.NotebookCellMetadata; - private _metadataChangeListener: IDisposable; - - readonly handle: number; - readonly uri: URI; - readonly cellKind: CellKind; - - private _cell: vscode.NotebookCell | undefined; - - constructor( - private readonly _proxy: MainThreadNotebookShape, - private readonly _notebook: ExtHostNotebookDocument, - private readonly _extHostDocument: ExtHostDocumentsAndEditors, - private readonly _cellData: IMainCellDto, - ) { - super(); - - this.handle = _cellData.handle; - this.uri = URI.revive(_cellData.uri); - this.cellKind = _cellData.cellKind; - - this._outputs = _cellData.outputs; - for (const output of this._outputs) { - this._outputMapping.set(output, output.outputId); - delete output.outputId; - } - - const observableMetadata = getObservable(_cellData.metadata ?? {}); - this._metadata = observableMetadata.proxy; - this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => { - this._updateMetadata(); - })); - } - - get cell(): vscode.NotebookCell { - if (!this._cell) { - const that = this; - const document = this._extHostDocument.getDocument(this.uri)!.document; - this._cell = Object.freeze({ - notebook: that._notebook.notebookDocument, - uri: that.uri, - cellKind: this._cellData.cellKind, - document, - language: document.languageId, - get outputs() { return that._outputs; }, - set outputs(value) { that._updateOutputs(value); }, - get metadata() { return that._metadata; }, - set metadata(value) { - that.setMetadata(value); - that._updateMetadata(); - }, - }); - } - return this._cell; - } - - dispose() { - super.dispose(); - this._onDidDispose.fire(); - } - - setOutputs(newOutputs: vscode.CellOutput[]): void { - this._outputs = newOutputs; - } - - private _updateOutputs(newOutputs: vscode.CellOutput[]) { - const rawDiffs = diff(this._outputs || [], newOutputs || [], (a) => { - return this._outputMapping.has(a); - }); - - const transformedDiffs: ISplice[] = rawDiffs.map(diff => { - for (let i = diff.start; i < diff.start + diff.deleteCount; i++) { - this._outputMapping.delete(this._outputs[i]); - } - - return { - deleteCount: diff.deleteCount, - start: diff.start, - toInsert: diff.toInsert.map((output): IProcessedOutput => { - if (output.outputKind === CellOutputKind.Rich) { - const uuid = UUID.generateUuid(); - this._outputMapping.set(output, uuid); - return { ...output, outputId: uuid }; - } - - this._outputMapping.set(output, undefined); - return output; - }) - }; - }); - - this._outputs = newOutputs; - this._onDidChangeOutputs.fire(transformedDiffs); - } - - setMetadata(newMetadata: vscode.NotebookCellMetadata): void { - // Don't apply metadata defaults here, 'undefined' means 'inherit from document metadata' - this._metadataChangeListener.dispose(); - const observableMetadata = getObservable(newMetadata); - this._metadata = observableMetadata.proxy; - this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => { - this._updateMetadata(); - })); - } - - private _updateMetadata(): Promise { - return this._proxy.$updateNotebookCellMetadata(this._notebook.notebookDocument.viewType, this._notebook.uri, this.handle, this._metadata); - } -} - -class RawContentChangeEvent { - - constructor(readonly start: number, readonly deletedCount: number, readonly deletedItems: ExtHostCell[], readonly items: ExtHostCell[]) { } - - static asApiEvent(event: RawContentChangeEvent): vscode.NotebookCellsChangeData { - return Object.freeze({ - start: event.start, - deletedCount: event.deletedCount, - deletedItems: event.deletedItems.map(data => data.cell), - items: event.items.map(data => data.cell) - }); - } -} - -export class ExtHostNotebookDocument extends Disposable { - - private static _handlePool: number = 0; - readonly handle = ExtHostNotebookDocument._handlePool++; - - private _cells: ExtHostCell[] = []; - - private _cellDisposableMapping = new Map(); - - private _notebook: vscode.NotebookDocument | undefined; - private _metadata: Required; - private _metadataChangeListener: IDisposable; - private _versionId = 0; - private _isDirty: boolean = false; - private _backupCounter = 1; - private _backup?: vscode.NotebookDocumentBackup; - private _disposed = false; - private _languages: string[] = []; - - private readonly _edits = new Cache('notebook documents'); - - constructor( - private readonly _proxy: MainThreadNotebookShape, - private readonly _documentsAndEditors: ExtHostDocumentsAndEditors, - private readonly _emitter: INotebookEventEmitter, - private readonly _viewType: string, - metadata: Required, - public readonly uri: URI, - public readonly renderingHandler: ExtHostNotebookOutputRenderingHandler, - private readonly _storagePath: URI | undefined - ) { - super(); - - const observableMetadata = getObservable(metadata); - this._metadata = observableMetadata.proxy; - this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => { - this._tryUpdateMetadata(); - })); - } - - dispose() { - this._disposed = true; - super.dispose(); - dispose(this._cellDisposableMapping.values()); - } - - private _updateMetadata(newMetadata: Required) { - this._metadataChangeListener.dispose(); - newMetadata = { - ...notebookDocumentMetadataDefaults, - ...newMetadata - }; - if (this._metadataChangeListener) { - this._metadataChangeListener.dispose(); - } - - const observableMetadata = getObservable(newMetadata); - this._metadata = observableMetadata.proxy; - this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => { - this._tryUpdateMetadata(); - })); - - this._tryUpdateMetadata(); - } - - private _tryUpdateMetadata() { - this._proxy.$updateNotebookMetadata(this._viewType, this.uri, this._metadata); - } - get notebookDocument(): vscode.NotebookDocument { - if (!this._notebook) { - const that = this; - this._notebook = Object.freeze({ - get uri() { return that.uri; }, - get version() { return that._versionId; }, - get fileName() { return that.uri.fsPath; }, - get viewType() { return that._viewType; }, - get isDirty() { return that._isDirty; }, - get isUntitled() { return that.uri.scheme === Schemas.untitled; }, - get cells(): ReadonlyArray { return that._cells.map(cell => cell.cell); }, - get languages() { return that._languages; }, - set languages(value: string[]) { that._trySetLanguages(value); }, - get metadata() { return that._metadata; }, - set metadata(value: Required) { that._updateMetadata(value); }, - }); - } - return this._notebook; - } - - private _trySetLanguages(newLanguages: string[]) { - this._languages = newLanguages; - this._proxy.$updateNotebookLanguages(this._viewType, this.uri, this._languages); - } - - getNewBackupUri(): URI { - if (!this._storagePath) { - throw new Error('Backup requires a valid storage path'); - } - const fileName = hashPath(this.uri) + (this._backupCounter++); - return joinPath(this._storagePath, fileName); - } - - updateBackup(backup: vscode.NotebookDocumentBackup): void { - this._backup?.delete(); - this._backup = backup; - } - - disposeBackup(): void { - this._backup?.delete(); - this._backup = undefined; - } - - acceptModelChanged(event: NotebookCellsChangedEventDto, isDirty: boolean): void { - this._versionId = event.versionId; - this._isDirty = isDirty; - if (event.kind === NotebookCellsChangeType.Initialize) { - this._spliceNotebookCells(event.changes, true); - } if (event.kind === NotebookCellsChangeType.ModelChange) { - this._spliceNotebookCells(event.changes, false); - } else if (event.kind === NotebookCellsChangeType.Move) { - this._moveCell(event.index, event.newIdx); - } else if (event.kind === NotebookCellsChangeType.Output) { - this._setCellOutputs(event.index, event.outputs); - } else if (event.kind === NotebookCellsChangeType.ChangeLanguage) { - this._changeCellLanguage(event.index, event.language); - } else if (event.kind === NotebookCellsChangeType.ChangeCellMetadata) { - this._changeCellMetadata(event.index, event.metadata); - } - } - - private _spliceNotebookCells(splices: NotebookCellsSplice2[], initialization: boolean): void { - if (this._disposed) { - return; - } - - const contentChangeEvents: RawContentChangeEvent[] = []; - const addedCellDocuments: IExtHostModelAddedData[] = []; - const removedCellDocuments: URI[] = []; - - splices.reverse().forEach(splice => { - const cellDtos = splice[2]; - const newCells = cellDtos.map(cell => { - - const extCell = new ExtHostCell(this._proxy, this, this._documentsAndEditors, cell); - - if (!initialization) { - addedCellDocuments.push(ExtHostCell.asModelAddData(this.notebookDocument, cell)); - } - - if (!this._cellDisposableMapping.has(extCell.handle)) { - const store = new DisposableStore(); - store.add(extCell); - this._cellDisposableMapping.set(extCell.handle, store); - } - - const store = this._cellDisposableMapping.get(extCell.handle)!; - - store.add(extCell.onDidChangeOutputs((diffs) => { - this.eventuallyUpdateCellOutputs(extCell, diffs); - })); - - return extCell; - }); - - for (let j = splice[0]; j < splice[0] + splice[1]; j++) { - this._cellDisposableMapping.get(this._cells[j].handle)?.dispose(); - this._cellDisposableMapping.delete(this._cells[j].handle); - } - - const deletedItems = this._cells.splice(splice[0], splice[1], ...newCells); - for (let cell of deletedItems) { - removedCellDocuments.push(cell.uri); - } - - contentChangeEvents.push(new RawContentChangeEvent(splice[0], splice[1], deletedItems, newCells)); - }); - - this._documentsAndEditors.acceptDocumentsAndEditorsDelta({ - addedDocuments: addedCellDocuments, - removedDocuments: removedCellDocuments - }); - - if (!initialization) { - this._emitter.emitModelChange({ - document: this.notebookDocument, - changes: contentChangeEvents.map(RawContentChangeEvent.asApiEvent) - }); - } - } - - private _moveCell(index: number, newIdx: number): void { - const cells = this._cells.splice(index, 1); - this._cells.splice(newIdx, 0, ...cells); - const changes: vscode.NotebookCellsChangeData[] = [{ - start: index, - deletedCount: 1, - deletedItems: cells.map(data => data.cell), - items: [] - }, { - start: newIdx, - deletedCount: 0, - deletedItems: [], - items: cells.map(data => data.cell) - }]; - this._emitter.emitModelChange({ - document: this.notebookDocument, - changes - }); - } - - private _setCellOutputs(index: number, outputs: IProcessedOutput[]): void { - const cell = this._cells[index]; - cell.setOutputs(outputs); - this._emitter.emitCellOutputsChange({ document: this.notebookDocument, cells: [cell.cell] }); - } - - private _changeCellLanguage(index: number, language: string): void { - const cell = this._cells[index]; - const event: vscode.NotebookCellLanguageChangeEvent = { document: this.notebookDocument, cell: cell.cell, language }; - this._emitter.emitCellLanguageChange(event); - } - - private _changeCellMetadata(index: number, newMetadata: NotebookCellMetadata | undefined): void { - const cell = this._cells[index]; - cell.setMetadata(newMetadata || {}); - const event: vscode.NotebookCellMetadataChangeEvent = { document: this.notebookDocument, cell: cell.cell }; - this._emitter.emitCellMetadataChange(event); - } - - async eventuallyUpdateCellOutputs(cell: ExtHostCell, diffs: ISplice[]) { - const outputDtos: NotebookCellOutputsSplice[] = diffs.map(diff => { - const outputs = diff.toInsert; - return [diff.start, diff.deleteCount, outputs]; - }); - - if (!outputDtos.length) { - return; - } - - await this._proxy.$spliceNotebookCellOutputs(this._viewType, this.uri, cell.handle, outputDtos); - this._emitter.emitCellOutputsChange({ - document: this.notebookDocument, - cells: [cell.cell] - }); - } - - getCell(cellHandle: number): ExtHostCell | undefined { - return this._cells.find(cell => cell.handle === cellHandle); - } - - - addEdit(item: vscode.NotebookDocumentEditEvent): number { - return this._edits.add([item]); - } - - async undo(editId: number, isDirty: boolean): Promise { - await this.getEdit(editId).undo(); - // if (!isDirty) { - // this.disposeBackup(); - // } - } - - async redo(editId: number, isDirty: boolean): Promise { - await this.getEdit(editId).redo(); - // if (!isDirty) { - // this.disposeBackup(); - // } - } - - private getEdit(editId: number): vscode.NotebookDocumentEditEvent { - const edit = this._edits.get(editId, 0); - if (!edit) { - throw new Error('No edit found'); - } - - return edit; - } - - disposeEdits(editIds: number[]): void { - for (const id of editIds) { - this._edits.delete(id); - } - } -} +import { ExtHostCell, ExtHostNotebookDocument } from './extHostNotebookDocument'; export class NotebookEditorCellEditBuilder implements vscode.NotebookEditorEdit { @@ -1395,7 +932,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN emitCellMetadataChange(event: vscode.NotebookCellMetadataChangeEvent): void { that._onDidChangeCellMetadata.fire(event); } - }, viewType, { ...notebookDocumentMetadataDefaults, ...modelData.metadata }, uri, this, storageRoot); + }, viewType, { ...notebookDocumentMetadataDefaults, ...modelData.metadata }, uri, storageRoot); document.acceptModelChanged({ kind: NotebookCellsChangeType.Initialize, @@ -1520,11 +1057,6 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } } -function hashPath(resource: URI): string { - const str = resource.scheme === Schemas.file || resource.scheme === Schemas.untitled ? resource.fsPath : resource.toString(); - return hash(str) + ''; -} - function isEditEvent(e: vscode.NotebookDocumentEditEvent | vscode.NotebookDocumentContentChangeEvent): e is vscode.NotebookDocumentEditEvent { return typeof (e as vscode.NotebookDocumentEditEvent).undo === 'function' && typeof (e as vscode.NotebookDocumentEditEvent).redo === 'function'; diff --git a/src/vs/workbench/api/common/extHostNotebookDocument.ts b/src/vs/workbench/api/common/extHostNotebookDocument.ts new file mode 100644 index 00000000000..363d0eb98df --- /dev/null +++ b/src/vs/workbench/api/common/extHostNotebookDocument.ts @@ -0,0 +1,482 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Emitter, Event } from 'vs/base/common/event'; +import { hash } from 'vs/base/common/hash'; +import { Disposable, DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { Schemas } from 'vs/base/common/network'; +import { joinPath } from 'vs/base/common/resources'; +import { ISplice } from 'vs/base/common/sequence'; +import { URI } from 'vs/base/common/uri'; +import * as UUID from 'vs/base/common/uuid'; +import { CellKind, MainThreadNotebookShape, NotebookCellOutputsSplice } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostDocumentsAndEditors, IExtHostModelAddedData } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; +import { CellOutputKind, diff, IMainCellDto, IProcessedOutput, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookCellsSplice2, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import * as vscode from 'vscode'; +import { Cache } from './cache'; + + +interface IObservable { + proxy: T; + onDidChange: Event; +} + +function getObservable(obj: T): IObservable { + const onDidChange = new Emitter(); + const proxy = new Proxy(obj, { + set(target: T, p: PropertyKey, value: any, _receiver: any): boolean { + target[p as keyof T] = value; + onDidChange.fire(); + return true; + } + }); + + return { + proxy, + onDidChange: onDidChange.event + }; +} + +class RawContentChangeEvent { + + constructor(readonly start: number, readonly deletedCount: number, readonly deletedItems: ExtHostCell[], readonly items: ExtHostCell[]) { } + + static asApiEvent(event: RawContentChangeEvent): vscode.NotebookCellsChangeData { + return Object.freeze({ + start: event.start, + deletedCount: event.deletedCount, + deletedItems: event.deletedItems.map(data => data.cell), + items: event.items.map(data => data.cell) + }); + } +} + +export class ExtHostCell extends Disposable { + + static asModelAddData(notebook: vscode.NotebookDocument, cell: IMainCellDto): IExtHostModelAddedData { + return { + EOL: cell.eol, + lines: cell.source, + modeId: cell.language, + uri: cell.uri, + isDirty: false, + versionId: 1, + notebook + }; + } + + private _onDidDispose = new Emitter(); + readonly onDidDispose: Event = this._onDidDispose.event; + + private _onDidChangeOutputs = new Emitter[]>(); + readonly onDidChangeOutputs: Event[]> = this._onDidChangeOutputs.event; + + private _outputs: any[]; + private _outputMapping = new WeakMap(); + + private _metadata: vscode.NotebookCellMetadata; + private _metadataChangeListener: IDisposable; + + readonly handle: number; + readonly uri: URI; + readonly cellKind: CellKind; + + private _cell: vscode.NotebookCell | undefined; + + constructor( + private readonly _proxy: MainThreadNotebookShape, + private readonly _notebook: ExtHostNotebookDocument, + private readonly _extHostDocument: ExtHostDocumentsAndEditors, + private readonly _cellData: IMainCellDto, + ) { + super(); + + this.handle = _cellData.handle; + this.uri = URI.revive(_cellData.uri); + this.cellKind = _cellData.cellKind; + + this._outputs = _cellData.outputs; + for (const output of this._outputs) { + this._outputMapping.set(output, output.outputId); + delete output.outputId; + } + + const observableMetadata = getObservable(_cellData.metadata ?? {}); + this._metadata = observableMetadata.proxy; + this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => { + this._updateMetadata(); + })); + } + + get cell(): vscode.NotebookCell { + if (!this._cell) { + const that = this; + const document = this._extHostDocument.getDocument(this.uri)!.document; + this._cell = Object.freeze({ + notebook: that._notebook.notebookDocument, + uri: that.uri, + cellKind: this._cellData.cellKind, + document, + language: document.languageId, + get outputs() { return that._outputs; }, + set outputs(value) { that._updateOutputs(value); }, + get metadata() { return that._metadata; }, + set metadata(value) { + that.setMetadata(value); + that._updateMetadata(); + }, + }); + } + return this._cell; + } + + dispose() { + super.dispose(); + this._onDidDispose.fire(); + } + + setOutputs(newOutputs: vscode.CellOutput[]): void { + this._outputs = newOutputs; + } + + private _updateOutputs(newOutputs: vscode.CellOutput[]) { + const rawDiffs = diff(this._outputs || [], newOutputs || [], (a) => { + return this._outputMapping.has(a); + }); + + const transformedDiffs: ISplice[] = rawDiffs.map(diff => { + for (let i = diff.start; i < diff.start + diff.deleteCount; i++) { + this._outputMapping.delete(this._outputs[i]); + } + + return { + deleteCount: diff.deleteCount, + start: diff.start, + toInsert: diff.toInsert.map((output): IProcessedOutput => { + if (output.outputKind === CellOutputKind.Rich) { + const uuid = UUID.generateUuid(); + this._outputMapping.set(output, uuid); + return { ...output, outputId: uuid }; + } + + this._outputMapping.set(output, undefined); + return output; + }) + }; + }); + + this._outputs = newOutputs; + this._onDidChangeOutputs.fire(transformedDiffs); + } + + setMetadata(newMetadata: vscode.NotebookCellMetadata): void { + // Don't apply metadata defaults here, 'undefined' means 'inherit from document metadata' + this._metadataChangeListener.dispose(); + const observableMetadata = getObservable(newMetadata); + this._metadata = observableMetadata.proxy; + this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => { + this._updateMetadata(); + })); + } + + private _updateMetadata(): Promise { + return this._proxy.$updateNotebookCellMetadata(this._notebook.notebookDocument.viewType, this._notebook.uri, this.handle, this._metadata); + } +} + +export interface INotebookEventEmitter { + emitModelChange(events: vscode.NotebookCellsChangeEvent): void; + emitCellOutputsChange(event: vscode.NotebookCellOutputsChangeEvent): void; + emitCellLanguageChange(event: vscode.NotebookCellLanguageChangeEvent): void; + emitCellMetadataChange(event: vscode.NotebookCellMetadataChangeEvent): void; +} + +function hashPath(resource: URI): string { + const str = resource.scheme === Schemas.file || resource.scheme === Schemas.untitled ? resource.fsPath : resource.toString(); + return hash(str) + ''; +} + +export class ExtHostNotebookDocument extends Disposable { + + private static _handlePool: number = 0; + readonly handle = ExtHostNotebookDocument._handlePool++; + + private _cells: ExtHostCell[] = []; + + private _cellDisposableMapping = new Map(); + + private _notebook: vscode.NotebookDocument | undefined; + private _metadata: Required; + private _metadataChangeListener: IDisposable; + private _versionId = 0; + private _isDirty: boolean = false; + private _backupCounter = 1; + private _backup?: vscode.NotebookDocumentBackup; + private _disposed = false; + private _languages: string[] = []; + + private readonly _edits = new Cache('notebook documents'); + + constructor( + private readonly _proxy: MainThreadNotebookShape, + private readonly _documentsAndEditors: ExtHostDocumentsAndEditors, + private readonly _emitter: INotebookEventEmitter, + private readonly _viewType: string, + metadata: Required, + public readonly uri: URI, + private readonly _storagePath: URI | undefined + ) { + super(); + + const observableMetadata = getObservable(metadata); + this._metadata = observableMetadata.proxy; + this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => { + this._tryUpdateMetadata(); + })); + } + + dispose() { + this._disposed = true; + super.dispose(); + dispose(this._cellDisposableMapping.values()); + } + + private _updateMetadata(newMetadata: Required) { + this._metadataChangeListener.dispose(); + newMetadata = { + ...notebookDocumentMetadataDefaults, + ...newMetadata + }; + if (this._metadataChangeListener) { + this._metadataChangeListener.dispose(); + } + + const observableMetadata = getObservable(newMetadata); + this._metadata = observableMetadata.proxy; + this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => { + this._tryUpdateMetadata(); + })); + + this._tryUpdateMetadata(); + } + + private _tryUpdateMetadata() { + this._proxy.$updateNotebookMetadata(this._viewType, this.uri, this._metadata); + } + get notebookDocument(): vscode.NotebookDocument { + if (!this._notebook) { + const that = this; + this._notebook = Object.freeze({ + get uri() { return that.uri; }, + get version() { return that._versionId; }, + get fileName() { return that.uri.fsPath; }, + get viewType() { return that._viewType; }, + get isDirty() { return that._isDirty; }, + get isUntitled() { return that.uri.scheme === Schemas.untitled; }, + get cells(): ReadonlyArray { return that._cells.map(cell => cell.cell); }, + get languages() { return that._languages; }, + set languages(value: string[]) { that._trySetLanguages(value); }, + get metadata() { return that._metadata; }, + set metadata(value: Required) { that._updateMetadata(value); }, + }); + } + return this._notebook; + } + + private _trySetLanguages(newLanguages: string[]) { + this._languages = newLanguages; + this._proxy.$updateNotebookLanguages(this._viewType, this.uri, this._languages); + } + + getNewBackupUri(): URI { + if (!this._storagePath) { + throw new Error('Backup requires a valid storage path'); + } + const fileName = hashPath(this.uri) + (this._backupCounter++); + return joinPath(this._storagePath, fileName); + } + + updateBackup(backup: vscode.NotebookDocumentBackup): void { + this._backup?.delete(); + this._backup = backup; + } + + disposeBackup(): void { + this._backup?.delete(); + this._backup = undefined; + } + + acceptModelChanged(event: NotebookCellsChangedEventDto, isDirty: boolean): void { + this._versionId = event.versionId; + this._isDirty = isDirty; + if (event.kind === NotebookCellsChangeType.Initialize) { + this._spliceNotebookCells(event.changes, true); + } if (event.kind === NotebookCellsChangeType.ModelChange) { + this._spliceNotebookCells(event.changes, false); + } else if (event.kind === NotebookCellsChangeType.Move) { + this._moveCell(event.index, event.newIdx); + } else if (event.kind === NotebookCellsChangeType.Output) { + this._setCellOutputs(event.index, event.outputs); + } else if (event.kind === NotebookCellsChangeType.ChangeLanguage) { + this._changeCellLanguage(event.index, event.language); + } else if (event.kind === NotebookCellsChangeType.ChangeCellMetadata) { + this._changeCellMetadata(event.index, event.metadata); + } + } + + private _spliceNotebookCells(splices: NotebookCellsSplice2[], initialization: boolean): void { + if (this._disposed) { + return; + } + + const contentChangeEvents: RawContentChangeEvent[] = []; + const addedCellDocuments: IExtHostModelAddedData[] = []; + const removedCellDocuments: URI[] = []; + + splices.reverse().forEach(splice => { + const cellDtos = splice[2]; + const newCells = cellDtos.map(cell => { + + const extCell = new ExtHostCell(this._proxy, this, this._documentsAndEditors, cell); + + if (!initialization) { + addedCellDocuments.push(ExtHostCell.asModelAddData(this.notebookDocument, cell)); + } + + if (!this._cellDisposableMapping.has(extCell.handle)) { + const store = new DisposableStore(); + store.add(extCell); + this._cellDisposableMapping.set(extCell.handle, store); + } + + const store = this._cellDisposableMapping.get(extCell.handle)!; + + store.add(extCell.onDidChangeOutputs((diffs) => { + this.eventuallyUpdateCellOutputs(extCell, diffs); + })); + + return extCell; + }); + + for (let j = splice[0]; j < splice[0] + splice[1]; j++) { + this._cellDisposableMapping.get(this._cells[j].handle)?.dispose(); + this._cellDisposableMapping.delete(this._cells[j].handle); + } + + const deletedItems = this._cells.splice(splice[0], splice[1], ...newCells); + for (let cell of deletedItems) { + removedCellDocuments.push(cell.uri); + } + + contentChangeEvents.push(new RawContentChangeEvent(splice[0], splice[1], deletedItems, newCells)); + }); + + this._documentsAndEditors.acceptDocumentsAndEditorsDelta({ + addedDocuments: addedCellDocuments, + removedDocuments: removedCellDocuments + }); + + if (!initialization) { + this._emitter.emitModelChange({ + document: this.notebookDocument, + changes: contentChangeEvents.map(RawContentChangeEvent.asApiEvent) + }); + } + } + + private _moveCell(index: number, newIdx: number): void { + const cells = this._cells.splice(index, 1); + this._cells.splice(newIdx, 0, ...cells); + const changes: vscode.NotebookCellsChangeData[] = [{ + start: index, + deletedCount: 1, + deletedItems: cells.map(data => data.cell), + items: [] + }, { + start: newIdx, + deletedCount: 0, + deletedItems: [], + items: cells.map(data => data.cell) + }]; + this._emitter.emitModelChange({ + document: this.notebookDocument, + changes + }); + } + + private _setCellOutputs(index: number, outputs: IProcessedOutput[]): void { + const cell = this._cells[index]; + cell.setOutputs(outputs); + this._emitter.emitCellOutputsChange({ document: this.notebookDocument, cells: [cell.cell] }); + } + + private _changeCellLanguage(index: number, language: string): void { + const cell = this._cells[index]; + const event: vscode.NotebookCellLanguageChangeEvent = { document: this.notebookDocument, cell: cell.cell, language }; + this._emitter.emitCellLanguageChange(event); + } + + private _changeCellMetadata(index: number, newMetadata: NotebookCellMetadata | undefined): void { + const cell = this._cells[index]; + cell.setMetadata(newMetadata || {}); + const event: vscode.NotebookCellMetadataChangeEvent = { document: this.notebookDocument, cell: cell.cell }; + this._emitter.emitCellMetadataChange(event); + } + + async eventuallyUpdateCellOutputs(cell: ExtHostCell, diffs: ISplice[]) { + const outputDtos: NotebookCellOutputsSplice[] = diffs.map(diff => { + const outputs = diff.toInsert; + return [diff.start, diff.deleteCount, outputs]; + }); + + if (!outputDtos.length) { + return; + } + + await this._proxy.$spliceNotebookCellOutputs(this._viewType, this.uri, cell.handle, outputDtos); + this._emitter.emitCellOutputsChange({ + document: this.notebookDocument, + cells: [cell.cell] + }); + } + + getCell(cellHandle: number): ExtHostCell | undefined { + return this._cells.find(cell => cell.handle === cellHandle); + } + + + addEdit(item: vscode.NotebookDocumentEditEvent): number { + return this._edits.add([item]); + } + + async undo(editId: number, isDirty: boolean): Promise { + await this.getEdit(editId).undo(); + // if (!isDirty) { + // this.disposeBackup(); + // } + } + + async redo(editId: number, isDirty: boolean): Promise { + await this.getEdit(editId).redo(); + // if (!isDirty) { + // this.disposeBackup(); + // } + } + + private getEdit(editId: number): vscode.NotebookDocumentEditEvent { + const edit = this._edits.get(editId, 0); + if (!edit) { + throw new Error('No edit found'); + } + + return edit; + } + + disposeEdits(editIds: number[]): void { + for (const id of editIds) { + this._edits.delete(id); + } + } +} diff --git a/src/vs/workbench/test/browser/api/extHostNotebook.test.ts b/src/vs/workbench/test/browser/api/extHostNotebook.test.ts index 2f6156164f1..c707cc3e276 100644 --- a/src/vs/workbench/test/browser/api/extHostNotebook.test.ts +++ b/src/vs/workbench/test/browser/api/extHostNotebook.test.ts @@ -11,7 +11,8 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { NullLogService } from 'vs/platform/log/common/log'; import { mock } from 'vs/base/test/common/mock'; import { IModelAddedData, MainContext, MainThreadCommandsShape, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol'; -import { ExtHostNotebookDocument, ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook'; +import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook'; +import { ExtHostNotebookDocument } from 'vs/workbench/api/common/extHostNotebookDocument'; import { CellKind, CellUri, NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { URI } from 'vs/base/common/uri'; import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; diff --git a/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts b/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts index c9f18c332a9..71e9d8216c8 100644 --- a/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts +++ b/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts @@ -9,7 +9,8 @@ import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import { NullLogService } from 'vs/platform/log/common/log'; import { ExtHostNotebookConcatDocument } from 'vs/workbench/api/common/extHostNotebookConcatDocument'; -import { ExtHostNotebookDocument, ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook'; +import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook'; +import { ExtHostNotebookDocument } from 'vs/workbench/api/common/extHostNotebookDocument'; import { URI } from 'vs/base/common/uri'; import { CellKind, CellUri, NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { Position, Location, Range } from 'vs/workbench/api/common/extHostTypes'; From 82ac7bacf31c5935fb33cc84fbd0a9963b48a1d5 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 9 Sep 2020 11:56:54 +0200 Subject: [PATCH 21/71] chore - extract ExtHostNotebookEditor into its own file --- .../workbench/api/common/extHostNotebook.ts | 247 +---------------- .../api/common/extHostNotebookEditor.ts | 258 ++++++++++++++++++ 2 files changed, 262 insertions(+), 243 deletions(-) create mode 100644 src/vs/workbench/api/common/extHostNotebookEditor.ts diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 5025eefc64a..d621d311119 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -4,13 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; -import { readonly } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; import * as UUID from 'vs/base/common/uuid'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { CellKind, ExtHostNotebookShape, ICommandDto, IMainContext, IModelAddedData, INotebookDocumentPropertiesChangeData, INotebookDocumentsAndEditorsDelta, INotebookEditorPropertiesChangeData, MainContext, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostNotebookShape, ICommandDto, IMainContext, IModelAddedData, INotebookDocumentPropertiesChangeData, INotebookDocumentsAndEditorsDelta, INotebookEditorPropertiesChangeData, MainContext, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol'; import { ILogService } from 'vs/platform/log/common/log'; import { CommandsConverter, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; @@ -18,104 +17,11 @@ import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePa import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview'; -import { addIdToOutput, CellEditType, CellStatusbarAlignment, CellUri, ICellEditOperation, ICellReplaceEdit, INotebookCellStatusBarEntry, INotebookDisplayOrder, INotebookEditData, INotebookKernelInfoDto2, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookDataDto, NotebookDocumentMetadata, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { addIdToOutput, CellStatusbarAlignment, CellUri, INotebookCellStatusBarEntry, INotebookDisplayOrder, INotebookKernelInfoDto2, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookDataDto, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import * as vscode from 'vscode'; import { ResourceMap } from 'vs/base/common/map'; import { ExtHostCell, ExtHostNotebookDocument } from './extHostNotebookDocument'; - -export class NotebookEditorCellEditBuilder implements vscode.NotebookEditorEdit { - - private readonly _documentVersionId: number; - - private _finalized: boolean = false; - private _collectedEdits: ICellEditOperation[] = []; - private _newNotebookDocumentMetadata?: NotebookDocumentMetadata; - - constructor(documentVersionId: number) { - this._documentVersionId = documentVersionId; - } - - finalize(): INotebookEditData { - this._finalized = true; - return { - documentVersionId: this._documentVersionId, - cellEdits: this._collectedEdits, - newMetadata: this._newNotebookDocumentMetadata - }; - } - - private _throwIfFinalized() { - if (this._finalized) { - throw new Error('Edit is only valid while callback runs'); - } - } - - replaceNotebookMetadata(value: vscode.NotebookDocumentMetadata): void { - this._throwIfFinalized(); - this._newNotebookDocumentMetadata = { ...notebookDocumentMetadataDefaults, ...value }; - } - - replaceCellMetadata(index: number, metadata: vscode.NotebookCellMetadata): void { - this._throwIfFinalized(); - this._collectedEdits.push({ - editType: CellEditType.Metadata, - index, - metadata - }); - } - - replaceMetadata(index: number, metadata: vscode.NotebookCellMetadata): void { - console.warn('DEPRECATED use "replaceCellMetadata" instead'); - this.replaceCellMetadata(index, metadata); - } - - - replaceCellOutput(index: number, outputs: vscode.CellOutput[]): void { - this._throwIfFinalized(); - this._collectedEdits.push({ - editType: CellEditType.Output, - index, - outputs: outputs.map(output => addIdToOutput(output)) - }); - } - - replaceOutput(index: number, outputs: vscode.CellOutput[]): void { - console.warn('DEPRECATED use "replaceCellOutput" instead'); - this.replaceCellOutput(index, outputs); - } - - replaceCells(from: number, to: number, cells: vscode.NotebookCellData[]): void { - this._throwIfFinalized(); - - this._collectedEdits.push({ - editType: CellEditType.Replace, - index: from, - count: to - from, - cells: cells.map(data => { - return { - ...data, - outputs: data.outputs.map(output => addIdToOutput(output)), - }; - }) - }); - } - - insert(index: number, content: string | string[], language: string, type: CellKind, outputs: vscode.CellOutput[], metadata: vscode.NotebookCellMetadata | undefined): void { - this._throwIfFinalized(); - this.replaceCells(index, index, [{ - language, - outputs, - metadata, - cellKind: type, - source: Array.isArray(content) ? content.join('\n') : content, - }]); - } - - delete(index: number): void { - this._throwIfFinalized(); - this.replaceCells(index, 1, []); - } -} +import { ExtHostNotebookEditor } from './extHostNotebookEditor'; class ExtHostWebviewCommWrapper extends Disposable { private readonly _onDidReceiveDocumentMessage = new Emitter(); @@ -162,152 +68,7 @@ class ExtHostWebviewCommWrapper extends Disposable { } } -export class ExtHostNotebookEditor extends Disposable implements vscode.NotebookEditor { - private _viewColumn: vscode.ViewColumn | undefined; - selection?: vscode.NotebookCell; - - private _visibleRanges: vscode.NotebookCellRange[] = []; - - get visibleRanges() { - return this._visibleRanges; - } - - set visibleRanges(_range: vscode.NotebookCellRange[]) { - throw readonly('visibleRanges'); - } - - _acceptVisibleRanges(value: vscode.NotebookCellRange[]): void { - this._visibleRanges = value; - } - - private _active: boolean = false; - get active(): boolean { - return this._active; - } - - set active(_state: boolean) { - throw readonly('active'); - } - - private _visible: boolean = false; - get visible(): boolean { - return this._visible; - } - - set visible(_state: boolean) { - throw readonly('visible'); - } - - _acceptVisibility(value: boolean) { - this._visible = value; - } - - _acceptActive(value: boolean) { - this._active = value; - } - - private _kernel?: vscode.NotebookKernel; - - get kernel() { - return this._kernel; - } - - set kernel(_kernel: vscode.NotebookKernel | undefined) { - throw readonly('kernel'); - } - - private _onDidDispose = new Emitter(); - readonly onDidDispose: Event = this._onDidDispose.event; - private _onDidReceiveMessage = new Emitter(); - onDidReceiveMessage: vscode.Event = this._onDidReceiveMessage.event; - - constructor( - private readonly viewType: string, - readonly id: string, - public uri: URI, - private _proxy: MainThreadNotebookShape, - private _webComm: vscode.NotebookCommunication, - public readonly notebookData: ExtHostNotebookDocument, - ) { - super(); - this._register(this._webComm.onDidReceiveMessage(e => { - this._onDidReceiveMessage.fire(e); - })); - } - - get document(): vscode.NotebookDocument { - return this.notebookData.notebookDocument; - } - - edit(callback: (editBuilder: NotebookEditorCellEditBuilder) => void): Thenable { - const edit = new NotebookEditorCellEditBuilder(this.document.version); - callback(edit); - return this._applyEdit(edit.finalize()); - } - - private _applyEdit(editData: INotebookEditData): Promise { - - // return when there is nothing to do - if (editData.cellEdits.length === 0) { - return Promise.resolve(true); - } - - const compressedEdits: ICellEditOperation[] = []; - let compressedEditsIndex = -1; - - for (let i = 0; i < editData.cellEdits.length; i++) { - if (compressedEditsIndex < 0) { - compressedEdits.push(editData.cellEdits[i]); - compressedEditsIndex++; - continue; - } - - const prevIndex = compressedEditsIndex; - const prev = compressedEdits[prevIndex]; - - if (prev.editType === CellEditType.Replace && editData.cellEdits[i].editType === CellEditType.Replace) { - if (prev.index === editData.cellEdits[i].index) { - prev.cells.push(...(editData.cellEdits[i] as ICellReplaceEdit).cells); - prev.count += (editData.cellEdits[i] as ICellReplaceEdit).count; - continue; - } - } - - compressedEdits.push(editData.cellEdits[i]); - compressedEditsIndex++; - } - - return this._proxy.$tryApplyEdits(this.viewType, this.uri, editData.documentVersionId, compressedEdits, editData.newMetadata); - } - - revealRange(range: vscode.NotebookCellRange, revealType?: extHostTypes.NotebookEditorRevealType) { - this._proxy.$tryRevealRange(this.id, range, revealType || extHostTypes.NotebookEditorRevealType.Default); - } - - get viewColumn(): vscode.ViewColumn | undefined { - return this._viewColumn; - } - - set viewColumn(value) { - throw readonly('viewColumn'); - } - - updateActiveKernel(kernel?: vscode.NotebookKernel) { - this._kernel = kernel; - } - async postMessage(message: any): Promise { - return this._webComm.postMessage(message); - } - - asWebviewUri(localResource: vscode.Uri): vscode.Uri { - return this._webComm.asWebviewUri(localResource); - } - dispose() { - this._onDidDispose.fire(); - super.dispose(); - } -} export interface ExtHostNotebookOutputRenderingHandler { outputDisplayOrder: INotebookDisplayOrder | undefined; @@ -755,7 +516,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN const kernel = event.kernelId ? adapter.getKernel(event.kernelId) : undefined; this._editors.forEach(editor => { if (editor.editor.notebookData === document) { - editor.editor.updateActiveKernel(kernel); + editor.editor._acceptKernel(kernel); } }); this._onDidChangeActiveNotebookKernel.fire({ document: document.notebookDocument, kernel }); diff --git a/src/vs/workbench/api/common/extHostNotebookEditor.ts b/src/vs/workbench/api/common/extHostNotebookEditor.ts new file mode 100644 index 00000000000..2822cb39e30 --- /dev/null +++ b/src/vs/workbench/api/common/extHostNotebookEditor.ts @@ -0,0 +1,258 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { readonly } from 'vs/base/common/errors'; +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; +import { CellKind, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol'; +import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; +import { addIdToOutput, CellEditType, ICellEditOperation, ICellReplaceEdit, INotebookEditData, NotebookDocumentMetadata, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import * as vscode from 'vscode'; +import { ExtHostNotebookDocument } from './extHostNotebookDocument'; + +class NotebookEditorCellEditBuilder implements vscode.NotebookEditorEdit { + + private readonly _documentVersionId: number; + + private _finalized: boolean = false; + private _collectedEdits: ICellEditOperation[] = []; + private _newNotebookDocumentMetadata?: NotebookDocumentMetadata; + + constructor(documentVersionId: number) { + this._documentVersionId = documentVersionId; + } + + finalize(): INotebookEditData { + this._finalized = true; + return { + documentVersionId: this._documentVersionId, + cellEdits: this._collectedEdits, + newMetadata: this._newNotebookDocumentMetadata + }; + } + + private _throwIfFinalized() { + if (this._finalized) { + throw new Error('Edit is only valid while callback runs'); + } + } + + replaceNotebookMetadata(value: vscode.NotebookDocumentMetadata): void { + this._throwIfFinalized(); + this._newNotebookDocumentMetadata = { ...notebookDocumentMetadataDefaults, ...value }; + } + + replaceCellMetadata(index: number, metadata: vscode.NotebookCellMetadata): void { + this._throwIfFinalized(); + this._collectedEdits.push({ + editType: CellEditType.Metadata, + index, + metadata + }); + } + + replaceMetadata(index: number, metadata: vscode.NotebookCellMetadata): void { + console.warn('DEPRECATED use "replaceCellMetadata" instead'); + this.replaceCellMetadata(index, metadata); + } + + + replaceCellOutput(index: number, outputs: vscode.CellOutput[]): void { + this._throwIfFinalized(); + this._collectedEdits.push({ + editType: CellEditType.Output, + index, + outputs: outputs.map(output => addIdToOutput(output)) + }); + } + + replaceOutput(index: number, outputs: vscode.CellOutput[]): void { + console.warn('DEPRECATED use "replaceCellOutput" instead'); + this.replaceCellOutput(index, outputs); + } + + replaceCells(from: number, to: number, cells: vscode.NotebookCellData[]): void { + this._throwIfFinalized(); + + this._collectedEdits.push({ + editType: CellEditType.Replace, + index: from, + count: to - from, + cells: cells.map(data => { + return { + ...data, + outputs: data.outputs.map(output => addIdToOutput(output)), + }; + }) + }); + } + + insert(index: number, content: string | string[], language: string, type: CellKind, outputs: vscode.CellOutput[], metadata: vscode.NotebookCellMetadata | undefined): void { + this._throwIfFinalized(); + this.replaceCells(index, index, [{ + language, + outputs, + metadata, + cellKind: type, + source: Array.isArray(content) ? content.join('\n') : content, + }]); + } + + delete(index: number): void { + this._throwIfFinalized(); + this.replaceCells(index, 1, []); + } +} + +export class ExtHostNotebookEditor extends Disposable implements vscode.NotebookEditor { + + //TODO@rebornix noop setter? + selection?: vscode.NotebookCell; + + private _visibleRanges: vscode.NotebookCellRange[] = []; + private _viewColumn?: vscode.ViewColumn; + private _active: boolean = false; + private _visible: boolean = false; + private _kernel?: vscode.NotebookKernel; + + private _onDidDispose = new Emitter(); + private _onDidReceiveMessage = new Emitter(); + + readonly onDidDispose: Event = this._onDidDispose.event; + readonly onDidReceiveMessage: vscode.Event = this._onDidReceiveMessage.event; + + constructor( + private readonly viewType: string, + readonly id: string, + public uri: URI, + private _proxy: MainThreadNotebookShape, + private _webComm: vscode.NotebookCommunication, + public readonly notebookData: ExtHostNotebookDocument, + ) { + super(); + this._register(this._webComm.onDidReceiveMessage(e => { + this._onDidReceiveMessage.fire(e); + })); + } + + get viewColumn(): vscode.ViewColumn | undefined { + return this._viewColumn; + } + + set viewColumn(_value) { + throw readonly('viewColumn'); + } + + get kernel() { + return this._kernel; + } + + set kernel(_kernel: vscode.NotebookKernel | undefined) { + throw readonly('kernel'); + } + + _acceptKernel(kernel?: vscode.NotebookKernel) { + this._kernel = kernel; + } + + get visible(): boolean { + return this._visible; + } + + set visible(_state: boolean) { + throw readonly('visible'); + } + + _acceptVisibility(value: boolean) { + this._visible = value; + } + + get visibleRanges() { + return this._visibleRanges; + } + + set visibleRanges(_range: vscode.NotebookCellRange[]) { + throw readonly('visibleRanges'); + } + + _acceptVisibleRanges(value: vscode.NotebookCellRange[]): void { + this._visibleRanges = value; + } + + get active(): boolean { + return this._active; + } + + set active(_state: boolean) { + throw readonly('active'); + } + + _acceptActive(value: boolean) { + this._active = value; + } + + get document(): vscode.NotebookDocument { + return this.notebookData.notebookDocument; + } + + edit(callback: (editBuilder: NotebookEditorCellEditBuilder) => void): Thenable { + const edit = new NotebookEditorCellEditBuilder(this.document.version); + callback(edit); + return this._applyEdit(edit.finalize()); + } + + private _applyEdit(editData: INotebookEditData): Promise { + + // return when there is nothing to do + if (editData.cellEdits.length === 0) { + return Promise.resolve(true); + } + + const compressedEdits: ICellEditOperation[] = []; + let compressedEditsIndex = -1; + + for (let i = 0; i < editData.cellEdits.length; i++) { + if (compressedEditsIndex < 0) { + compressedEdits.push(editData.cellEdits[i]); + compressedEditsIndex++; + continue; + } + + const prevIndex = compressedEditsIndex; + const prev = compressedEdits[prevIndex]; + + if (prev.editType === CellEditType.Replace && editData.cellEdits[i].editType === CellEditType.Replace) { + if (prev.index === editData.cellEdits[i].index) { + prev.cells.push(...(editData.cellEdits[i] as ICellReplaceEdit).cells); + prev.count += (editData.cellEdits[i] as ICellReplaceEdit).count; + continue; + } + } + + compressedEdits.push(editData.cellEdits[i]); + compressedEditsIndex++; + } + + return this._proxy.$tryApplyEdits(this.viewType, this.uri, editData.documentVersionId, compressedEdits, editData.newMetadata); + } + + revealRange(range: vscode.NotebookCellRange, revealType?: extHostTypes.NotebookEditorRevealType) { + this._proxy.$tryRevealRange(this.id, range, revealType || extHostTypes.NotebookEditorRevealType.Default); + } + + async postMessage(message: any): Promise { + return this._webComm.postMessage(message); + } + + asWebviewUri(localResource: vscode.Uri): vscode.Uri { + return this._webComm.asWebviewUri(localResource); + } + + dispose() { + this._onDidDispose.fire(); + super.dispose(); + } +} From cfe2bb233e2793addf75e43a60c21dde40dc94e9 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 9 Sep 2020 12:24:56 +0200 Subject: [PATCH 22/71] chore - remove ExtHostNotebookEditor#uri and use document.uri instead --- src/vs/workbench/api/common/extHostNotebook.ts | 7 +++---- src/vs/workbench/api/common/extHostNotebookEditor.ts | 12 +++++------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index d621d311119..c1944e6064a 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -529,7 +529,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN const uriStr = URI.revive(uriComponents).toString(); let editor: { editor: ExtHostNotebookEditor; } | undefined; this._editors.forEach(e => { - if (e.editor.uri.toString() === uriStr) { + if (e.editor.document.uri.toString() === uriStr) { editor = e; } }); @@ -619,9 +619,8 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } const editor = new ExtHostNotebookEditor( - document.notebookDocument.viewType, editorId, - revivedUri, + document.notebookDocument.viewType, this._proxy, webComm.contentProviderComm, document @@ -656,7 +655,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } for (const e of this._editors.values()) { - if (e.editor.uri.toString() === revivedUri.toString()) { + if (e.editor.document.uri.toString() === revivedUri.toString()) { e.editor.dispose(); this._editors.delete(e.editor.id); editorChanged = true; diff --git a/src/vs/workbench/api/common/extHostNotebookEditor.ts b/src/vs/workbench/api/common/extHostNotebookEditor.ts index 2822cb39e30..f12f9f4f78e 100644 --- a/src/vs/workbench/api/common/extHostNotebookEditor.ts +++ b/src/vs/workbench/api/common/extHostNotebookEditor.ts @@ -6,7 +6,6 @@ import { readonly } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; -import { URI } from 'vs/base/common/uri'; import { CellKind, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol'; import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; import { addIdToOutput, CellEditType, ICellEditOperation, ICellReplaceEdit, INotebookEditData, NotebookDocumentMetadata, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; @@ -125,12 +124,11 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook readonly onDidReceiveMessage: vscode.Event = this._onDidReceiveMessage.event; constructor( - private readonly viewType: string, readonly id: string, - public uri: URI, - private _proxy: MainThreadNotebookShape, - private _webComm: vscode.NotebookCommunication, - public readonly notebookData: ExtHostNotebookDocument, + private readonly _viewType: string, + private readonly _proxy: MainThreadNotebookShape, + private readonly _webComm: vscode.NotebookCommunication, + readonly notebookData: ExtHostNotebookDocument, ) { super(); this._register(this._webComm.onDidReceiveMessage(e => { @@ -236,7 +234,7 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook compressedEditsIndex++; } - return this._proxy.$tryApplyEdits(this.viewType, this.uri, editData.documentVersionId, compressedEdits, editData.newMetadata); + return this._proxy.$tryApplyEdits(this._viewType, this.document.uri, editData.documentVersionId, compressedEdits, editData.newMetadata); } revealRange(range: vscode.NotebookCellRange, revealType?: extHostTypes.NotebookEditorRevealType) { From eb92195570f4597b961019cb807e7270b6a48f29 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 9 Sep 2020 12:54:13 +0200 Subject: [PATCH 23/71] chore - :lipstick: member order: property, ctor, method --- .../notebook/common/notebookEditorModel.ts | 47 ++++++++++--------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts index bc6ee9afeb8..af198636e5a 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts @@ -30,20 +30,15 @@ export interface INotebookLoadOptions { export class NotebookEditorModel extends EditorModel implements INotebookEditorModel { - protected readonly _onDidChangeDirty = this._register(new Emitter()); - readonly onDidChangeDirty = this._onDidChangeDirty.event; + + private readonly _onDidChangeDirty = this._register(new Emitter()); private readonly _onDidChangeContent = this._register(new Emitter()); - readonly onDidChangeContent: Event = this._onDidChangeContent.event; + + readonly onDidChangeDirty = this._onDidChangeDirty.event; + readonly onDidChangeContent = this._onDidChangeContent.event; + private _notebook!: NotebookTextModel; - private _lastResolvedFileStat: IFileStatWithMetadata | undefined; - - get lastResolvedFileStat() { - return this._lastResolvedFileStat; - } - - get notebook() { - return this._notebook; - } + private _lastResolvedFileStat?: IFileStatWithMetadata; private readonly _name: string; private readonly _workingCopyResource: URI; @@ -62,23 +57,31 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM this._name = labelService.getUriBasenameLabel(resource); - const input = this; + const that = this; this._workingCopyResource = resource.with({ scheme: Schemas.vscodeNotebook }); const workingCopyAdapter = new class implements IWorkingCopy { - readonly resource = input._workingCopyResource; - get name() { return input._name; } - readonly capabilities = input.isUntitled() ? WorkingCopyCapabilities.Untitled : WorkingCopyCapabilities.None; - readonly onDidChangeDirty = input.onDidChangeDirty; - readonly onDidChangeContent = input.onDidChangeContent; - isDirty(): boolean { return input.isDirty(); } - backup(): Promise { return input.backup(); } - save(): Promise { return input.save(); } - revert(options?: IRevertOptions): Promise { return input.revert(options); } + readonly resource = that._workingCopyResource; + get name() { return that._name; } + readonly capabilities = that.isUntitled() ? WorkingCopyCapabilities.Untitled : WorkingCopyCapabilities.None; + readonly onDidChangeDirty = that.onDidChangeDirty; + readonly onDidChangeContent = that.onDidChangeContent; + isDirty(): boolean { return that.isDirty(); } + backup(): Promise { return that.backup(); } + save(): Promise { return that.save(); } + revert(options?: IRevertOptions): Promise { return that.revert(options); } }; this._register(this._workingCopyService.registerWorkingCopy(workingCopyAdapter)); } + get lastResolvedFileStat() { + return this._lastResolvedFileStat; + } + + get notebook() { + return this._notebook; + } + async backup(): Promise> { if (this._notebook.supportBackup) { const tokenSource = new CancellationTokenSource(); From 8b993b8d30fd079acd6e5dcffbe315a02495903d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Wed, 9 Sep 2020 13:50:40 +0200 Subject: [PATCH 24/71] trusted types related to #106285 --- src/vs/base/browser/ui/list/listWidget.ts | 5 +---- src/vs/base/browser/ui/sash/sash.ts | 2 +- src/vs/base/browser/ui/tree/abstractTree.ts | 6 +----- src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts | 2 +- 4 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 252150b28d7..81fe5160a5e 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -824,10 +824,7 @@ export class DefaultStyleController implements IStyleController { content.push(`.monaco-list-type-filter { box-shadow: 1px 1px 1px ${styles.listMatchesShadow}; }`); } - const newStyles = content.join('\n'); - if (newStyles !== this.styleElement.innerHTML) { - this.styleElement.innerHTML = newStyles; - } + this.styleElement.textContent = content.join('\n'); } } diff --git a/src/vs/base/browser/ui/sash/sash.ts b/src/vs/base/browser/ui/sash/sash.ts index 7941babc3f4..a8db08e032d 100644 --- a/src/vs/base/browser/ui/sash/sash.ts +++ b/src/vs/base/browser/ui/sash/sash.ts @@ -266,7 +266,7 @@ export class Sash extends Disposable { } } - style.innerHTML = `* { cursor: ${cursor} !important; }`; + style.textContent = `* { cursor: ${cursor} !important; }`; }; const disposables = new DisposableStore(); diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 16e17fcf763..74ab08a9fba 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -1474,11 +1474,7 @@ export abstract class AbstractTree implements IDisposable content.push(`.monaco-list${suffix} .monaco-tl-indent > .indent-guide.active { border-color: ${styles.treeIndentGuidesStroke}; }`); } - const newStyles = content.join('\n'); - if (newStyles !== this.styleElement.innerHTML) { - this.styleElement.innerHTML = newStyles; - } - + this.styleElement.textContent = content.join('\n'); this.view.style(styles); } diff --git a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts index 3ee75fbc9c8..8dafe78b342 100644 --- a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts +++ b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts @@ -1301,7 +1301,7 @@ export class DirtyDiffWorkbenchController extends Disposable implements ext.IWor private setViewState(state: IViewState): void { this.viewState = state; - this.stylesheet.innerHTML = ` + this.stylesheet.textContent = ` .monaco-editor .dirty-diff-modified,.monaco-editor .dirty-diff-added{border-left-width:${state.width}px;} .monaco-editor .dirty-diff-modified, .monaco-editor .dirty-diff-added, .monaco-editor .dirty-diff-deleted { opacity: ${state.visibility === 'always' ? 1 : 0}; From 6518457d909f50c54172780a3026d89125b99dc8 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 9 Sep 2020 13:51:46 +0200 Subject: [PATCH 25/71] use async await --- .../node/extensionManagementService.ts | 631 +++++++++--------- .../node/extensionsScanner.ts | 90 ++- 2 files changed, 375 insertions(+), 346 deletions(-) diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 5c1480c6bb3..e6c3252ada1 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -65,7 +65,7 @@ export class ExtensionManagementService extends Disposable implements IExtension private readonly extensionsScanner: ExtensionsScanner; private reportedExtensions: Promise | undefined; private lastReportTimestamp = 0; - private readonly installingExtensions: Map> = new Map>(); + private readonly installingExtensions = new Map>(); private readonly uninstallingExtensions: Map> = new Map>(); private readonly manifestCache: ExtensionsManifestCache; private readonly extensionsDownloader: ExtensionsDownloader; @@ -104,16 +104,17 @@ export class ExtensionManagementService extends Disposable implements IExtension })); } - zip(extension: ILocalExtension): Promise { + async zip(extension: ILocalExtension): Promise { this.logService.trace('ExtensionManagementService#zip', extension.identifier.id); - return this.collectFiles(extension) - .then(files => zip(path.join(tmpdir(), generateUuid()), files)) - .then(path => URI.file(path)); + const files = await this.collectFiles(extension); + const location = await zip(path.join(tmpdir(), generateUuid()), files); + return URI.file(location); } - unzip(zipLocation: URI): Promise { + async unzip(zipLocation: URI): Promise { this.logService.trace('ExtensionManagementService#unzip', zipLocation.toString()); - return this.install(zipLocation).then(local => local.identifier); + const local = await this.install(zipLocation); + return local.identifier; } async getManifest(vsix: URI): Promise { @@ -122,7 +123,7 @@ export class ExtensionManagementService extends Disposable implements IExtension return getManifest(zipPath); } - private collectFiles(extension: ILocalExtension): Promise { + private async collectFiles(extension: ILocalExtension): Promise { const collectFilesFromDirectory = async (dir: string): Promise => { let entries = await pfs.readdir(dir); @@ -143,97 +144,103 @@ export class ExtensionManagementService extends Disposable implements IExtension return promise; }; - return collectFilesFromDirectory(extension.location.fsPath) - .then(files => files.map(f => ({ path: `extension/${path.relative(extension.location.fsPath, f)}`, localPath: f }))); - + const files = await collectFilesFromDirectory(extension.location.fsPath); + return files.map(f => ({ path: `extension/${path.relative(extension.location.fsPath, f)}`, localPath: f })); } - install(vsix: URI, isMachineScoped?: boolean): Promise { + async install(vsix: URI, isMachineScoped?: boolean): Promise { this.logService.trace('ExtensionManagementService#install', vsix.toString()); - return createCancelablePromise(token => { - return this.downloadVsix(vsix).then(downloadLocation => { - const zipPath = path.resolve(downloadLocation.fsPath); + return createCancelablePromise(async token => { - return getManifest(zipPath) - .then(manifest => { - const identifier = { id: getGalleryExtensionId(manifest.publisher, manifest.name) }; - let operation: InstallOperation = InstallOperation.Install; - if (manifest.engines && manifest.engines.vscode && !isEngineValid(manifest.engines.vscode, product.version)) { - return Promise.reject(new Error(nls.localize('incompatible', "Unable to install extension '{0}' as it is not compatible with VS Code '{1}'.", identifier.id, product.version))); - } - const identifierWithVersion = new ExtensionIdentifierWithVersion(identifier, manifest.version); - return this.getInstalled(ExtensionType.User) - .then(installedExtensions => { - const existing = installedExtensions.filter(i => areSameExtensions(identifier, i.identifier))[0]; - if (existing) { - isMachineScoped = isMachineScoped || existing.isMachineScoped; - operation = InstallOperation.Update; - if (identifierWithVersion.equals(new ExtensionIdentifierWithVersion(existing.identifier, existing.manifest.version))) { - return this.extensionsScanner.removeExtension(existing, 'existing').then(null, e => Promise.reject(new Error(nls.localize('restartCode', "Please restart VS Code before reinstalling {0}.", manifest.displayName || manifest.name)))); - } else if (semver.gt(existing.manifest.version, manifest.version)) { - return this.uninstallExtension(existing); - } - } else { - // Remove the extension with same version if it is already uninstalled. - // Installing a VSIX extension shall replace the existing extension always. - return this.unsetUninstalledAndGetLocal(identifierWithVersion) - .then(existing => { - if (existing) { - return this.extensionsScanner.removeExtension(existing, 'existing').then(null, e => Promise.reject(new Error(nls.localize('restartCode', "Please restart VS Code before reinstalling {0}.", manifest.displayName || manifest.name)))); - } - return undefined; - }); - } - return undefined; - }) - .then(() => { - this.logService.info('Installing the extension:', identifier.id); - this._onInstallExtension.fire({ identifier, zipPath }); - return this.getGalleryMetadata(getGalleryExtensionId(manifest.publisher, manifest.name)) - .then( - metadata => this.installFromZipPath(identifierWithVersion, zipPath, isMachineScoped ? { ...metadata, isMachineScoped } : metadata, operation, token), - () => this.installFromZipPath(identifierWithVersion, zipPath, isMachineScoped ? { isMachineScoped } : undefined, operation, token)) - .then( - local => { this.logService.info('Successfully installed the extension:', identifier.id); return local; }, - e => { - this.logService.error('Failed to install the extension:', identifier.id, e.message); - return Promise.reject(e); - }); - }); - }); - }); + const downloadLocation = await this.downloadVsix(vsix); + const zipPath = path.resolve(downloadLocation.fsPath); + + const manifest = await getManifest(zipPath); + const identifier = { id: getGalleryExtensionId(manifest.publisher, manifest.name) }; + let operation: InstallOperation = InstallOperation.Install; + if (manifest.engines && manifest.engines.vscode && !isEngineValid(manifest.engines.vscode, product.version)) { + throw new Error(nls.localize('incompatible', "Unable to install extension '{0}' as it is not compatible with VS Code '{1}'.", identifier.id, product.version)); + } + + const identifierWithVersion = new ExtensionIdentifierWithVersion(identifier, manifest.version); + const installedExtensions = await this.getInstalled(ExtensionType.User); + const existing = installedExtensions.find(i => areSameExtensions(identifier, i.identifier)); + if (existing) { + isMachineScoped = isMachineScoped || existing.isMachineScoped; + operation = InstallOperation.Update; + if (identifierWithVersion.equals(new ExtensionIdentifierWithVersion(existing.identifier, existing.manifest.version))) { + try { + await this.extensionsScanner.removeExtension(existing, 'existing'); + } catch (e) { + throw new Error(nls.localize('restartCode', "Please restart VS Code before reinstalling {0}.", manifest.displayName || manifest.name)); + } + } else if (semver.gt(existing.manifest.version, manifest.version)) { + await this.uninstallExtension(existing); + } + } else { + // Remove the extension with same version if it is already uninstalled. + // Installing a VSIX extension shall replace the existing extension always. + const existing = await this.unsetUninstalledAndGetLocal(identifierWithVersion); + if (existing) { + try { + await this.extensionsScanner.removeExtension(existing, 'existing'); + } catch (e) { + throw new Error(nls.localize('restartCode', "Please restart VS Code before reinstalling {0}.", manifest.displayName || manifest.name)); + } + } + } + + this.logService.info('Installing the extension:', identifier.id); + this._onInstallExtension.fire({ identifier, zipPath }); + + let metadata: IGalleryMetadata | undefined; + try { + metadata = await this.getGalleryMetadata(getGalleryExtensionId(manifest.publisher, manifest.name)); + } catch (e) { /* Ignore */ } + + try { + const local = await this.installFromZipPath(identifierWithVersion, zipPath, isMachineScoped ? { ...(metadata || {}), isMachineScoped } : metadata, operation, token); + this.logService.info('Successfully installed the extension:', identifier.id); + return local; + } catch (e) { + this.logService.error('Failed to install the extension:', identifier.id, e.message); + throw e; + } }); } - private downloadVsix(vsix: URI): Promise { + private async downloadVsix(vsix: URI): Promise { if (vsix.scheme === Schemas.file) { - return Promise.resolve(vsix); + return vsix; } if (!this.downloadService) { throw new Error('Download service is not available'); } - const downloadedLocation = path.join(tmpdir(), generateUuid()); - return this.downloadService.download(vsix, URI.file(downloadedLocation)).then(() => URI.file(downloadedLocation)); + + const downloadedLocation = URI.file(path.join(tmpdir(), generateUuid())); + await this.downloadService.download(vsix, downloadedLocation); + return downloadedLocation; } - private installFromZipPath(identifierWithVersion: ExtensionIdentifierWithVersion, zipPath: string, metadata: IMetadata | undefined, operation: InstallOperation, token: CancellationToken): Promise { - return this.toNonCancellablePromise(this.installExtension({ zipPath, identifierWithVersion, metadata }, token) - .then(local => this.installDependenciesAndPackExtensions(local, undefined) - .then( - () => local, - error => { - if (isNonEmptyArray(local.manifest.extensionDependencies)) { - this.logService.warn(`Cannot install dependencies of extension:`, local.identifier.id, error.message); - } - if (isNonEmptyArray(local.manifest.extensionPack)) { - this.logService.warn(`Cannot install packed extensions of extension:`, local.identifier.id, error.message); - } - return local; - })) - .then( - local => { this._onDidInstallExtension.fire({ identifier: identifierWithVersion.identifier, zipPath, local, operation }); return local; }, - error => { this._onDidInstallExtension.fire({ identifier: identifierWithVersion.identifier, zipPath, operation, error }); return Promise.reject(error); } - )); + private async installFromZipPath(identifierWithVersion: ExtensionIdentifierWithVersion, zipPath: string, metadata: IMetadata | undefined, operation: InstallOperation, token: CancellationToken): Promise { + try { + const local = await this.installExtension({ zipPath, identifierWithVersion, metadata }, token); + try { + await this.installDependenciesAndPackExtensions(local, undefined); + } catch (error) { + if (isNonEmptyArray(local.manifest.extensionDependencies)) { + this.logService.warn(`Cannot install dependencies of extension:`, local.identifier.id, error.message); + } + if (isNonEmptyArray(local.manifest.extensionPack)) { + this.logService.warn(`Cannot install packed extensions of extension:`, local.identifier.id, error.message); + } + } + this._onDidInstallExtension.fire({ identifier: identifierWithVersion.identifier, zipPath, local, operation }); + return local; + } catch (error) { + this._onDidInstallExtension.fire({ identifier: identifierWithVersion.identifier, zipPath, operation, error }); + throw error; + } } async canInstall(extension: IGalleryExtension): Promise { @@ -242,17 +249,68 @@ export class ExtensionManagementService extends Disposable implements IExtension async installFromGallery(extension: IGalleryExtension, isMachineScoped?: boolean): Promise { if (!this.galleryService.isEnabled()) { - return Promise.reject(new Error(nls.localize('MarketPlaceDisabled', "Marketplace is not enabled"))); + throw new Error(nls.localize('MarketPlaceDisabled', "Marketplace is not enabled")); } - const startTime = new Date().getTime(); - const onDidInstallExtensionSuccess = (extension: IGalleryExtension, operation: InstallOperation, local: ILocalExtension) => { + try { + extension = await this.checkAndGetCompatibleVersion(extension); + } catch (error) { + const errorCode = error && (error).code ? (error).code : ERROR_UNKNOWN; + this.logService.error(`Failed to install extension:`, extension.identifier.id, error ? error.message : errorCode); + this.reportTelemetry(this.getTelemetryEvent(InstallOperation.Install), getGalleryExtensionTelemetryData(extension), undefined, error); + if (error instanceof Error) { + error.name = errorCode; + } + throw error; + } + + const key = new ExtensionIdentifierWithVersion(extension.identifier, extension.version).key(); + let cancellablePromise = this.installingExtensions.get(key); + if (!cancellablePromise) { + cancellablePromise = createCancelablePromise(token => this.doInstallFromGallery(extension, !!isMachineScoped, token)); + this.installingExtensions.set(key, cancellablePromise); + cancellablePromise.finally(() => this.installingExtensions.delete(key)); + } + + return cancellablePromise; + } + + private async doInstallFromGallery(extension: IGalleryExtension, isMachineScoped: boolean, token: CancellationToken): Promise { + const startTime = new Date().getTime(); + let operation: InstallOperation = InstallOperation.Install; + this.logService.info('Installing extension:', extension.identifier.id); + this._onInstallExtension.fire({ identifier: extension.identifier, gallery: extension }); + + try { + const installed = await this.getInstalled(ExtensionType.User); + const existingExtension = installed.find(i => areSameExtensions(i.identifier, extension.identifier)); + if (existingExtension) { + operation = InstallOperation.Update; + } + + const installableExtension = await this.downloadInstallableExtension(extension, operation); + installableExtension.metadata.isMachineScoped = isMachineScoped || existingExtension?.isMachineScoped; + const local = await this.installExtension(installableExtension, token); + + try { await this.extensionsDownloader.delete(URI.file(installableExtension.zipPath)); } catch (error) { /* Ignore */ } + + try { + await this.installDependenciesAndPackExtensions(local, existingExtension); + } catch (error) { + try { await this.uninstall(local); } catch (error) { /* Ignore */ } + throw error; + } + + if (existingExtension && semver.neq(existingExtension.manifest.version, extension.version)) { + await this.setUninstalled(existingExtension); + } + this.logService.info(`Extensions installed successfully:`, extension.identifier.id); this._onDidInstallExtension.fire({ identifier: extension.identifier, gallery: extension, local, operation }); this.reportTelemetry(this.getTelemetryEvent(operation), getGalleryExtensionTelemetryData(extension), new Date().getTime() - startTime, undefined); - }; + return local; - const onDidInstallExtensionFailure = (extension: IGalleryExtension, operation: InstallOperation, error: Error) => { + } catch (error) { const errorCode = error && (error).code ? (error).code : ERROR_UNKNOWN; this.logService.error(`Failed to install extension:`, extension.identifier.id, error ? error.message : errorCode); this._onDidInstallExtension.fire({ identifier: extension.identifier, gallery: extension, operation, error: errorCode }); @@ -260,162 +318,107 @@ export class ExtensionManagementService extends Disposable implements IExtension if (error instanceof Error) { error.name = errorCode; } - }; - - try { - extension = await this.checkAndGetCompatibleVersion(extension); - } catch (error) { - onDidInstallExtensionFailure(extension, InstallOperation.Install, error); - return Promise.reject(error); + throw error; } - - const key = new ExtensionIdentifierWithVersion(extension.identifier, extension.version).key(); - let cancellablePromise = this.installingExtensions.get(key); - if (!cancellablePromise) { - - this.logService.info('Installing extension:', extension.identifier.id); - this._onInstallExtension.fire({ identifier: extension.identifier, gallery: extension }); - - let operation: InstallOperation = InstallOperation.Install; - let cancellationToken: CancellationToken, successCallback: (local: ILocalExtension) => void, errorCallback: (e?: any) => any | null; - cancellablePromise = createCancelablePromise(token => { cancellationToken = token; return new Promise((c, e) => { successCallback = c; errorCallback = e; }); }); - this.installingExtensions.set(key, cancellablePromise); - try { - const installed = await this.getInstalled(ExtensionType.User); - const existingExtension = installed.find(i => areSameExtensions(i.identifier, extension.identifier)); - if (existingExtension) { - operation = InstallOperation.Update; - } - - this.downloadInstallableExtension(extension, operation) - .then(installableExtension => { - installableExtension.metadata.isMachineScoped = isMachineScoped || existingExtension?.isMachineScoped; - return this.installExtension(installableExtension, cancellationToken) - .then(local => this.extensionsDownloader.delete(URI.file(installableExtension.zipPath)).finally(() => { }).then(() => local)); - }) - .then(local => this.installDependenciesAndPackExtensions(local, existingExtension) - .then(() => local, error => this.uninstall(local).then(() => Promise.reject(error), () => Promise.reject(error)))) - .then( - async local => { - if (existingExtension && semver.neq(existingExtension.manifest.version, extension.version)) { - await this.setUninstalled(existingExtension); - } - this.installingExtensions.delete(key); - onDidInstallExtensionSuccess(extension, operation, local); - successCallback(local); - }, - error => { - this.installingExtensions.delete(key); - onDidInstallExtensionFailure(extension, operation, error); - errorCallback(error); - }); - - } catch (error) { - this.installingExtensions.delete(key); - onDidInstallExtensionFailure(extension, operation, error); - return Promise.reject(error); - } - - } - - return cancellablePromise; } private async checkAndGetCompatibleVersion(extension: IGalleryExtension): Promise { if (await this.isMalicious(extension)) { - return Promise.reject(new ExtensionManagementError(nls.localize('malicious extension', "Can't install extension since it was reported to be problematic."), INSTALL_ERROR_MALICIOUS)); + throw new ExtensionManagementError(nls.localize('malicious extension', "Can't install extension since it was reported to be problematic."), INSTALL_ERROR_MALICIOUS); } const compatibleExtension = await this.galleryService.getCompatibleExtension(extension); - if (!compatibleExtension) { - return Promise.reject(new ExtensionManagementError(nls.localize('notFoundCompatibleDependency', "Unable to install '{0}' extension because it is not compatible with the current version of VS Code (version {1}).", extension.identifier.id, product.version), INSTALL_ERROR_INCOMPATIBLE)); + throw new ExtensionManagementError(nls.localize('notFoundCompatibleDependency', "Unable to install '{0}' extension because it is not compatible with the current version of VS Code (version {1}).", extension.identifier.id, product.version), INSTALL_ERROR_INCOMPATIBLE); } return compatibleExtension; } - reinstallFromGallery(extension: ILocalExtension): Promise { + async reinstallFromGallery(extension: ILocalExtension): Promise { this.logService.trace('ExtensionManagementService#reinstallFromGallery', extension.identifier.id); if (!this.galleryService.isEnabled()) { - return Promise.reject(new Error(nls.localize('MarketPlaceDisabled', "Marketplace is not enabled"))); + throw new Error(nls.localize('MarketPlaceDisabled', "Marketplace is not enabled")); } - return this.findGalleryExtension(extension) - .then(galleryExtension => { - if (galleryExtension) { - return this.setUninstalled(extension) - .then(() => this.extensionsScanner.removeUninstalledExtension(extension) - .then( - () => this.installFromGallery(galleryExtension).then(), - e => Promise.reject(new Error(nls.localize('removeError', "Error while removing the extension: {0}. Please Quit and Start VS Code before trying again.", toErrorMessage(e)))))); - } - return Promise.reject(new Error(nls.localize('Not a Marketplace extension', "Only Marketplace Extensions can be reinstalled"))); - }); + + const galleryExtension = await this.findGalleryExtension(extension); + if (!galleryExtension) { + throw new Error(nls.localize('Not a Marketplace extension', "Only Marketplace Extensions can be reinstalled")); + } + + await this.setUninstalled(extension); + try { + await this.extensionsScanner.removeUninstalledExtension(extension); + } catch (e) { + throw new Error(nls.localize('removeError', "Error while removing the extension: {0}. Please Quit and Start VS Code before trying again.", toErrorMessage(e))); + } + + await this.installFromGallery(galleryExtension); } private getTelemetryEvent(operation: InstallOperation): string { return operation === InstallOperation.Update ? 'extensionGallery:update' : 'extensionGallery:install'; } - private isMalicious(extension: IGalleryExtension): Promise { - return this.getExtensionsReport() - .then(report => getMaliciousExtensionsSet(report).has(extension.identifier.id)); + private async isMalicious(extension: IGalleryExtension): Promise { + const report = await this.getExtensionsReport(); + return getMaliciousExtensionsSet(report).has(extension.identifier.id); } - private downloadInstallableExtension(extension: IGalleryExtension, operation: InstallOperation): Promise> { + private async downloadInstallableExtension(extension: IGalleryExtension, operation: InstallOperation): Promise> { const metadata = { id: extension.identifier.uuid, publisherId: extension.publisherId, publisherDisplayName: extension.publisherDisplayName, }; - this.logService.trace('Started downloading extension:', extension.identifier.id); - return this.extensionsDownloader.downloadExtension(extension, operation) - .then( - zip => { - const zipPath = zip.fsPath; - this.logService.info('Downloaded extension:', extension.identifier.id, zipPath); - return getManifest(zipPath) - .then( - manifest => (>{ zipPath, identifierWithVersion: new ExtensionIdentifierWithVersion(extension.identifier, manifest.version), metadata }), - error => Promise.reject(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_VALIDATING)) - ); - }, - error => Promise.reject(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_DOWNLOADING))); + let zipPath; + try { + this.logService.trace('Started downloading extension:', extension.identifier.id); + const zip = await this.extensionsDownloader.downloadExtension(extension, operation); + this.logService.info('Downloaded extension:', extension.identifier.id, zipPath); + zipPath = zip.fsPath; + } catch (error) { + throw new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_DOWNLOADING); + } + + try { + const manifest = await getManifest(zipPath); + return (>{ zipPath, identifierWithVersion: new ExtensionIdentifierWithVersion(extension.identifier, manifest.version), metadata }); + } catch (error) { + throw new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_VALIDATING); + } } - private installExtension(installableExtension: InstallableExtension, token: CancellationToken): Promise { - return this.unsetUninstalledAndGetLocal(installableExtension.identifierWithVersion) - .then( - local => { - if (local) { - return local; - } - return this.extractAndInstall(installableExtension, token); - }, - e => { - if (isMacintosh) { - return Promise.reject(new ExtensionManagementError(nls.localize('quitCode', "Unable to install the extension. Please Quit and Start VS Code before reinstalling."), INSTALL_ERROR_UNSET_UNINSTALLED)); - } - return Promise.reject(new ExtensionManagementError(nls.localize('exitCode', "Unable to install the extension. Please Exit and Start VS Code before reinstalling."), INSTALL_ERROR_UNSET_UNINSTALLED)); - }); + private async installExtension(installableExtension: InstallableExtension, token: CancellationToken): Promise { + try { + const local = await this.unsetUninstalledAndGetLocal(installableExtension.identifierWithVersion); + if (local) { + return local; + } + } catch (e) { + if (isMacintosh) { + throw new ExtensionManagementError(nls.localize('quitCode', "Unable to install the extension. Please Quit and Start VS Code before reinstalling."), INSTALL_ERROR_UNSET_UNINSTALLED); + } else { + throw new ExtensionManagementError(nls.localize('exitCode', "Unable to install the extension. Please Exit and Start VS Code before reinstalling."), INSTALL_ERROR_UNSET_UNINSTALLED); + } + } + return this.extractAndInstall(installableExtension, token); } - private unsetUninstalledAndGetLocal(identifierWithVersion: ExtensionIdentifierWithVersion): Promise { - return this.isUninstalled(identifierWithVersion) - .then(isUninstalled => { - if (isUninstalled) { - this.logService.trace('Removing the extension from uninstalled list:', identifierWithVersion.identifier.id); - // If the same version of extension is marked as uninstalled, remove it from there and return the local. - return this.unsetUninstalled(identifierWithVersion) - .then(() => { - this.logService.info('Removed the extension from uninstalled list:', identifierWithVersion.identifier.id); - return this.getInstalled(ExtensionType.User); - }) - .then(installed => installed.filter(i => new ExtensionIdentifierWithVersion(i.identifier, i.manifest.version).equals(identifierWithVersion))[0]); - } - return null; - }); + private async unsetUninstalledAndGetLocal(identifierWithVersion: ExtensionIdentifierWithVersion): Promise { + const isUninstalled = await this.isUninstalled(identifierWithVersion); + if (!isUninstalled) { + return null; + } + + this.logService.trace('Removing the extension from uninstalled list:', identifierWithVersion.identifier.id); + // If the same version of extension is marked as uninstalled, remove it from there and return the local. + await this.unsetUninstalled(identifierWithVersion); + this.logService.info('Removed the extension from uninstalled list:', identifierWithVersion.identifier.id); + + const installed = await this.getInstalled(ExtensionType.User); + return installed.find(i => new ExtensionIdentifierWithVersion(i.identifier, i.manifest.version).equals(identifierWithVersion)) || null; } private async extractAndInstall({ zipPath, identifierWithVersion, metadata }: InstallableExtension, token: CancellationToken): Promise { @@ -429,57 +432,56 @@ export class ExtensionManagementService extends Disposable implements IExtension } private async installDependenciesAndPackExtensions(installed: ILocalExtension, existing: ILocalExtension | undefined): Promise { - if (this.galleryService.isEnabled()) { - const dependenciesAndPackExtensions: string[] = installed.manifest.extensionDependencies || []; - if (installed.manifest.extensionPack) { - for (const extension of installed.manifest.extensionPack) { - // add only those extensions which are new in currently installed extension - if (!(existing && existing.manifest.extensionPack && existing.manifest.extensionPack.some(old => areSameExtensions({ id: old }, { id: extension })))) { - if (dependenciesAndPackExtensions.every(e => !areSameExtensions({ id: e }, { id: extension }))) { - dependenciesAndPackExtensions.push(extension); - } + if (!this.galleryService.isEnabled()) { + return; + } + const dependenciesAndPackExtensions: string[] = installed.manifest.extensionDependencies || []; + if (installed.manifest.extensionPack) { + for (const extension of installed.manifest.extensionPack) { + // add only those extensions which are new in currently installed extension + if (!(existing && existing.manifest.extensionPack && existing.manifest.extensionPack.some(old => areSameExtensions({ id: old }, { id: extension })))) { + if (dependenciesAndPackExtensions.every(e => !areSameExtensions({ id: e }, { id: extension }))) { + dependenciesAndPackExtensions.push(extension); } } } - if (dependenciesAndPackExtensions.length) { - return this.getInstalled() - .then(installed => { - // filter out installed extensions - const names = dependenciesAndPackExtensions.filter(id => installed.every(({ identifier: galleryIdentifier }) => !areSameExtensions(galleryIdentifier, { id }))); - if (names.length) { - return this.galleryService.query({ names, pageSize: dependenciesAndPackExtensions.length }, CancellationToken.None) - .then(galleryResult => { - const extensionsToInstall = galleryResult.firstPage; - return Promise.all(extensionsToInstall.map(e => this.installFromGallery(e))) - .then(undefined, errors => this.rollback(extensionsToInstall).then(() => Promise.reject(errors), () => Promise.reject(errors))); - }); - } - return; - }); + } + if (dependenciesAndPackExtensions.length) { + const installed = await this.getInstalled(); + // filter out installed extensions + const names = dependenciesAndPackExtensions.filter(id => installed.every(({ identifier: galleryIdentifier }) => !areSameExtensions(galleryIdentifier, { id }))); + if (names.length) { + const galleryResult = await this.galleryService.query({ names, pageSize: dependenciesAndPackExtensions.length }, CancellationToken.None); + const extensionsToInstall = galleryResult.firstPage; + try { + await Promise.all(extensionsToInstall.map(e => this.installFromGallery(e))); + } catch (error) { + try { await this.rollback(extensionsToInstall); } catch (e) { /* ignore */ } + throw error; + } } } - return Promise.resolve(undefined); } - private rollback(extensions: IGalleryExtension[]): Promise { - return this.getInstalled(ExtensionType.User) - .then(installed => - Promise.all(installed.filter(local => extensions.some(galleryExtension => new ExtensionIdentifierWithVersion(local.identifier, local.manifest.version).equals(new ExtensionIdentifierWithVersion(galleryExtension.identifier, galleryExtension.version)))) // Check with version because we want to rollback the exact version - .map(local => this.uninstall(local)))) - .then(() => undefined, () => undefined); + private async rollback(extensions: IGalleryExtension[]): Promise { + const installed = await this.getInstalled(ExtensionType.User); + const extensionsToUninstall = installed.filter(local => extensions.some(galleryExtension => new ExtensionIdentifierWithVersion(local.identifier, local.manifest.version).equals(new ExtensionIdentifierWithVersion(galleryExtension.identifier, galleryExtension.version)))); // Check with version because we want to rollback the exact version + await Promise.all(extensionsToUninstall.map(local => this.uninstall(local))); } - uninstall(extension: ILocalExtension): Promise { + async uninstall(extension: ILocalExtension): Promise { this.logService.trace('ExtensionManagementService#uninstall', extension.identifier.id); - return this.toNonCancellablePromise(this.getInstalled(ExtensionType.User) - .then(installed => { - const extensionToUninstall = installed.filter(e => areSameExtensions(e.identifier, extension.identifier))[0]; - if (extensionToUninstall) { - return this.checkForDependenciesAndUninstall(extensionToUninstall, installed).then(undefined, error => Promise.reject(this.joinErrors(error))); - } else { - return Promise.reject(new Error(nls.localize('notInstalled', "Extension '{0}' is not installed.", extension.manifest.displayName || extension.manifest.name))); - } - })); + const installed = await this.getInstalled(ExtensionType.User); + const extensionToUninstall = installed.find(e => areSameExtensions(e.identifier, extension.identifier)); + if (!extensionToUninstall) { + throw new Error(nls.localize('notInstalled', "Extension '{0}' is not installed.", extension.manifest.displayName || extension.manifest.name)); + } + + try { + await this.checkForDependenciesAndUninstall(extensionToUninstall, installed); + } catch (error) { + throw this.joinErrors(error); + } } async updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata): Promise { @@ -489,25 +491,27 @@ export class ExtensionManagementService extends Disposable implements IExtension return local; } - private getGalleryMetadata(extensionName: string): Promise { - return this.findGalleryExtensionByName(extensionName) - .then(galleryExtension => galleryExtension ? { id: galleryExtension.identifier.uuid, publisherDisplayName: galleryExtension.publisherDisplayName, publisherId: galleryExtension.publisherId } : undefined); + private async getGalleryMetadata(extensionName: string): Promise { + const galleryExtension = await this.findGalleryExtensionByName(extensionName); + return galleryExtension ? { id: galleryExtension.identifier.uuid, publisherDisplayName: galleryExtension.publisherDisplayName, publisherId: galleryExtension.publisherId } : undefined; } - private findGalleryExtension(local: ILocalExtension): Promise { + private async findGalleryExtension(local: ILocalExtension): Promise { if (local.identifier.uuid) { - return this.findGalleryExtensionById(local.identifier.uuid) - .then(galleryExtension => galleryExtension ? galleryExtension : this.findGalleryExtensionByName(local.identifier.id)); + const galleryExtension = await this.findGalleryExtensionById(local.identifier.uuid); + return galleryExtension ? galleryExtension : this.findGalleryExtensionByName(local.identifier.id); } return this.findGalleryExtensionByName(local.identifier.id); } - private findGalleryExtensionById(uuid: string): Promise { - return this.galleryService.query({ ids: [uuid], pageSize: 1 }, CancellationToken.None).then(galleryResult => galleryResult.firstPage[0]); + private async findGalleryExtensionById(uuid: string): Promise { + const galleryResult = await this.galleryService.query({ ids: [uuid], pageSize: 1 }, CancellationToken.None); + return galleryResult.firstPage[0]; } - private findGalleryExtensionByName(name: string): Promise { - return this.galleryService.query({ names: [name], pageSize: 1 }, CancellationToken.None).then(galleryResult => galleryResult.firstPage[0]); + private async findGalleryExtensionByName(name: string): Promise { + const galleryResult = await this.galleryService.query({ names: [name], pageSize: 1 }, CancellationToken.None); + return galleryResult.firstPage[0]; } private joinErrors(errorOrErrors: (Error | string) | (Array)): Error { @@ -520,20 +524,20 @@ export class ExtensionManagementService extends Disposable implements IExtension }, new Error('')); } - private checkForDependenciesAndUninstall(extension: ILocalExtension, installed: ILocalExtension[]): Promise { - return this.preUninstallExtension(extension) - .then(() => { - const packedExtensions = this.getAllPackExtensionsToUninstall(extension, installed); - if (packedExtensions.length) { - return this.uninstallExtensions(extension, packedExtensions, installed); - } - return this.uninstallExtensions(extension, [], installed); - }) - .then(() => this.postUninstallExtension(extension), - error => { - this.postUninstallExtension(extension, new ExtensionManagementError(error instanceof Error ? error.message : error, INSTALL_ERROR_LOCAL)); - return Promise.reject(error); - }); + private async checkForDependenciesAndUninstall(extension: ILocalExtension, installed: ILocalExtension[]): Promise { + try { + await this.preUninstallExtension(extension); + const packedExtensions = this.getAllPackExtensionsToUninstall(extension, installed); + if (packedExtensions.length) { + await this.uninstallExtensions(extension, packedExtensions, installed); + } else { + await this.uninstallExtensions(extension, [], installed); + } + } catch (error) { + await this.postUninstallExtension(extension, new ExtensionManagementError(error instanceof Error ? error.message : error, INSTALL_ERROR_LOCAL)); + throw error; + } + await this.postUninstallExtension(extension); } private async uninstallExtensions(extension: ILocalExtension, otherExtensionsToUninstall: ILocalExtension[], installed: ILocalExtension[]): Promise { @@ -604,33 +608,36 @@ export class ExtensionManagementService extends Disposable implements IExtension return installed.filter(e => e.manifest.extensionDependencies && e.manifest.extensionDependencies.some(id => areSameExtensions({ id }, extension.identifier))); } - private doUninstall(extension: ILocalExtension): Promise { - return this.preUninstallExtension(extension) - .then(() => this.uninstallExtension(extension)) - .then(() => this.postUninstallExtension(extension), - error => { - this.postUninstallExtension(extension, new ExtensionManagementError(error instanceof Error ? error.message : error, INSTALL_ERROR_LOCAL)); - return Promise.reject(error); - }); + private async doUninstall(extension: ILocalExtension): Promise { + try { + await this.preUninstallExtension(extension); + await this.uninstallExtension(extension); + } catch (error) { + await this.postUninstallExtension(extension, new ExtensionManagementError(error instanceof Error ? error.message : error, INSTALL_ERROR_LOCAL)); + throw error; + } + await this.postUninstallExtension(extension); } - private preUninstallExtension(extension: ILocalExtension): Promise { - return Promise.resolve(pfs.exists(extension.location.fsPath)) - .then(exists => exists ? null : Promise.reject(new Error(nls.localize('notExists', "Could not find extension")))) - .then(() => { - this.logService.info('Uninstalling extension:', extension.identifier.id); - this._onUninstallExtension.fire(extension.identifier); - }); + private async preUninstallExtension(extension: ILocalExtension): Promise { + const exists = await pfs.exists(extension.location.fsPath); + if (!exists) { + throw new Error(nls.localize('notExists', "Could not find extension")); + } + this.logService.info('Uninstalling extension:', extension.identifier.id); + this._onUninstallExtension.fire(extension.identifier); } - private uninstallExtension(local: ILocalExtension): Promise { + private async uninstallExtension(local: ILocalExtension): Promise { let promise = this.uninstallingExtensions.get(local.identifier.id); if (!promise) { // Set all versions of the extension as uninstalled - promise = createCancelablePromise(token => this.extensionsScanner.scanUserExtensions(false) - .then(userExtensions => this.setUninstalled(...userExtensions.filter(u => areSameExtensions(u.identifier, local.identifier)))) - .then(() => { this.uninstallingExtensions.delete(local.identifier.id); })); + promise = createCancelablePromise(async () => { + const userExtensions = await this.extensionsScanner.scanUserExtensions(false); + await this.setUninstalled(...userExtensions.filter(u => areSameExtensions(u.identifier, local.identifier))); + }); this.uninstallingExtensions.set(local.identifier.id, promise); + promise.finally(() => this.uninstallingExtensions.delete(local.identifier.id)); } return promise; } @@ -642,7 +649,9 @@ export class ExtensionManagementService extends Disposable implements IExtension this.logService.info('Successfully uninstalled extension:', extension.identifier.id); // only report if extension has a mapped gallery extension. UUID identifies the gallery extension. if (extension.identifier.uuid) { - await this.galleryService.reportStatistic(extension.manifest.publisher, extension.manifest.name, extension.manifest.version, StatisticType.Uninstall); + try { + await this.galleryService.reportStatistic(extension.manifest.publisher, extension.manifest.name, extension.manifest.version, StatisticType.Uninstall); + } catch (error) { /* ignore */ } } } this.reportTelemetry('extensionGallery:uninstall', getLocalExtensionTelemetryData(extension), undefined, error); @@ -658,8 +667,9 @@ export class ExtensionManagementService extends Disposable implements IExtension return this.extensionsScanner.cleanUp(); } - private isUninstalled(identifier: ExtensionIdentifierWithVersion): Promise { - return this.filterUninstalled(identifier).then(uninstalled => uninstalled.length === 1); + private async isUninstalled(identifier: ExtensionIdentifierWithVersion): Promise { + const uninstalled = await this.filterUninstalled(identifier); + return uninstalled.length === 1; } private filterUninstalled(...identifiers: ExtensionIdentifierWithVersion[]): Promise { @@ -697,21 +707,16 @@ export class ExtensionManagementService extends Disposable implements IExtension return this.reportedExtensions; } - private updateReportCache(): Promise { - this.logService.trace('ExtensionManagementService.refreshReportedCache'); - - return this.galleryService.getExtensionsReport() - .then(result => { - this.logService.trace(`ExtensionManagementService.refreshReportedCache - got ${result.length} reported extensions from service`); - return result; - }, err => { - this.logService.trace('ExtensionManagementService.refreshReportedCache - failed to get extension report'); - return []; - }); - } - - private toNonCancellablePromise(promise: Promise): Promise { - return new Promise((c, e) => promise.then(result => c(result), error => e(error))); + private async updateReportCache(): Promise { + try { + this.logService.trace('ExtensionManagementService.refreshReportedCache'); + const result = await this.galleryService.getExtensionsReport(); + this.logService.trace(`ExtensionManagementService.refreshReportedCache - got ${result.length} reported extensions from service`); + return result; + } catch (err) { + this.logService.trace('ExtensionManagementService.refreshReportedCache - failed to get extension report'); + return []; + } } private reportTelemetry(eventName: string, extensionData: any, duration?: number, error?: Error): void { diff --git a/src/vs/platform/extensionManagement/node/extensionsScanner.ts b/src/vs/platform/extensionManagement/node/extensionsScanner.ts index 819dc9d2bd2..40218225f3e 100644 --- a/src/vs/platform/extensionManagement/node/extensionsScanner.ts +++ b/src/vs/platform/extensionManagement/node/extensionsScanner.ts @@ -69,7 +69,12 @@ export class ExtensionsScanner extends Disposable { promises.push(this.scanUserExtensions(true).then(null, e => Promise.reject(new ExtensionManagementError(this.joinErrors(e).message, ERROR_SCANNING_USER_EXTENSIONS)))); } - return Promise.all(promises).then(flatten, errors => Promise.reject(this.joinErrors(errors))); + try { + const result = await Promise.all(promises); + return flatten(result); + } catch (error) { + throw this.joinErrors(error); + } } async scanUserExtensions(excludeOutdated: boolean): Promise { @@ -145,20 +150,31 @@ export class ExtensionsScanner extends Disposable { async withUninstalledExtensions(fn: (uninstalled: IStringDictionary) => T): Promise { return this.uninstalledFileLimiter.queue(async () => { - let result: T | null = null; - return pfs.readFile(this.uninstalledPath, 'utf8') - .then(undefined, err => err.code === 'ENOENT' ? Promise.resolve('{}') : Promise.reject(err)) - .then<{ [id: string]: boolean }>(raw => { try { return JSON.parse(raw); } catch (e) { return {}; } }) - .then(uninstalled => { result = fn(uninstalled); return uninstalled; }) - .then(uninstalled => { - if (Object.keys(uninstalled).length === 0) { - return pfs.rimraf(this.uninstalledPath); - } else { - const raw = JSON.stringify(uninstalled); - return pfs.writeFile(this.uninstalledPath, raw); - } - }) - .then(() => result); + let raw: string | undefined; + try { + raw = await pfs.readFile(this.uninstalledPath, 'utf8'); + } catch (err) { + if (err.code !== 'ENOENT') { + throw err; + } + } + + let uninstalled = {}; + if (raw) { + try { + uninstalled = JSON.parse(raw); + } catch (e) { /* ignore */ } + } + + const result = fn(uninstalled); + + if (Object.keys(uninstalled).length) { + await pfs.writeFile(this.uninstalledPath, JSON.stringify(uninstalled)); + } else { + await pfs.rimraf(this.uninstalledPath); + } + + return result; }); } @@ -173,27 +189,35 @@ export class ExtensionsScanner extends Disposable { await this.withUninstalledExtensions(uninstalled => delete uninstalled[new ExtensionIdentifierWithVersion(extension.identifier, extension.manifest.version).key()]); } - private extractAtLocation(identifier: IExtensionIdentifier, zipPath: string, location: string, token: CancellationToken): Promise { + private async extractAtLocation(identifier: IExtensionIdentifier, zipPath: string, location: string, token: CancellationToken): Promise { this.logService.trace(`Started extracting the extension from ${zipPath} to ${location}`); - return pfs.rimraf(location) - .then( - () => extract(zipPath, location, { sourcePath: 'extension', overwrite: true }, token) - .then( - () => this.logService.info(`Extracted extension to ${location}:`, identifier.id), - e => pfs.rimraf(location).finally(() => { }) - .then(() => Promise.reject(new ExtensionManagementError(e.message, e instanceof ExtractError && e.type ? e.type : INSTALL_ERROR_EXTRACTING)))), - e => Promise.reject(new ExtensionManagementError(this.joinErrors(e).message, INSTALL_ERROR_DELETING))); + + // Clean the location + try { + await pfs.rimraf(location); + } catch (e) { + throw new ExtensionManagementError(this.joinErrors(e).message, INSTALL_ERROR_DELETING); + } + + try { + await extract(zipPath, location, { sourcePath: 'extension', overwrite: true }, token); + this.logService.info(`Extracted extension to ${location}:`, identifier.id); + } catch (e) { + try { await pfs.rimraf(location); } catch (e) { /* Ignore */ } + throw new ExtensionManagementError(e.message, e instanceof ExtractError && e.type ? e.type : INSTALL_ERROR_EXTRACTING); + } } - private rename(identifier: IExtensionIdentifier, extractPath: string, renamePath: string, retryUntil: number): Promise { - return pfs.rename(extractPath, renamePath) - .then(undefined, error => { - if (isWindows && error && error.code === 'EPERM' && Date.now() < retryUntil) { - this.logService.info(`Failed renaming ${extractPath} to ${renamePath} with 'EPERM' error. Trying again...`, identifier.id); - return this.rename(identifier, extractPath, renamePath, retryUntil); - } - return Promise.reject(new ExtensionManagementError(error.message || localize('renameError', "Unknown error while renaming {0} to {1}", extractPath, renamePath), error.code || INSTALL_ERROR_RENAMING)); - }); + private async rename(identifier: IExtensionIdentifier, extractPath: string, renamePath: string, retryUntil: number): Promise { + try { + await pfs.rename(extractPath, renamePath); + } catch (error) { + if (isWindows && error && error.code === 'EPERM' && Date.now() < retryUntil) { + this.logService.info(`Failed renaming ${extractPath} to ${renamePath} with 'EPERM' error. Trying again...`, identifier.id); + return this.rename(identifier, extractPath, renamePath, retryUntil); + } + throw new ExtensionManagementError(error.message || localize('renameError', "Unknown error while renaming {0} to {1}", extractPath, renamePath), error.code || INSTALL_ERROR_RENAMING); + } } private async scanSystemExtensions(): Promise { From 900e16aea719985f02e07a3395145ba58a4800bd Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 9 Sep 2020 14:22:00 +0200 Subject: [PATCH 26/71] update trusted types search --- .vscode/searches/TrustedTypes.code-search | 69 +---------------------- 1 file changed, 2 insertions(+), 67 deletions(-) diff --git a/.vscode/searches/TrustedTypes.code-search b/.vscode/searches/TrustedTypes.code-search index 9710c95777e..ad37236c4ac 100644 --- a/.vscode/searches/TrustedTypes.code-search +++ b/.vscode/searches/TrustedTypes.code-search @@ -4,7 +4,7 @@ # Excluding: *.test.ts # ContextLines: 3 -52 results - 34 files +44 results - 27 files src/vs/base/browser/markdownRenderer.ts: 156 const strValue = values[0]; @@ -23,15 +23,6 @@ src/vs/base/browser/markdownRenderer.ts: 243 // allowedTags should included everything that markdown renders to. 244 // Since we have our own sanitize function for marked, it's possible we missed some tag so let insane make sure. -src/vs/base/browser/ui/codicons/codiconStyles.ts: - 20 for (let c of iconRegistry.all) { - 21 rules.push(formatRule(c)); - 22 } - 23: codiconStyleSheet.innerHTML = rules.join('\n'); - 24 } - 25 - 26 const delayer = new RunOnceScheduler(updateAll, 0); - src/vs/base/browser/ui/contextview/contextview.ts: 157 this.shadowRootHostElement = DOM.$('.shadow-root-host'); 158 this.container.appendChild(this.shadowRootHostElement); @@ -41,15 +32,6 @@ src/vs/base/browser/ui/contextview/contextview.ts: 162 ${SHADOW_ROOT_CSS} 163 -src/vs/base/browser/ui/list/listWidget.ts: - 826 - 827 const newStyles = content.join('\n'); - 828 if (newStyles !== this.styleElement.innerHTML) { - 829: this.styleElement.innerHTML = newStyles; - 830 } - 831 } - 832 } - src/vs/base/browser/ui/menu/menu.ts: 229 private initializeStyleSheet(container: HTMLElement): void { 230 if (isInShadowDOM(container)) { @@ -74,15 +56,6 @@ src/vs/base/browser/ui/menu/menubar.ts: 555 556 let mnemonicMatches = MENU_MNEMONIC_REGEX.exec(label); -src/vs/base/browser/ui/sash/sash.ts: - 266 } - 267 } - 268 - 269: style.innerHTML = `* { cursor: ${cursor} !important; }`; - 270 }; - 271 - 272 const disposables = new DisposableStore(); - src/vs/base/browser/ui/selectBox/selectBoxCustom.ts: 367 content.push(`.monaco-select-box-dropdown-container > .select-box-dropdown-list-container .monaco-list .monaco-list-row.option-disabled:hover { outline: none !important; }`); 368 } @@ -100,15 +73,6 @@ src/vs/base/browser/ui/selectBox/selectBoxCustom.ts: 685 } 686 -src/vs/base/browser/ui/tree/abstractTree.ts: - 1476 - 1477 const newStyles = content.join('\n'); - 1478 if (newStyles !== this.styleElement.innerHTML) { - 1479: this.styleElement.innerHTML = newStyles; - 1480 } - 1481 - 1482 this.view.style(styles); - src/vs/base/parts/quickinput/browser/quickInput.ts: 1693 } 1694 const newStyles = content.join('\n'); @@ -364,15 +328,6 @@ src/vs/workbench/contrib/sash/browser/sash.ts: 40 .monaco-sash.horizontal { cursor: ns-resize; left: 0; width: 100%; height: ${size}px; } 41 .monaco-sash:not(.disabled).orthogonal-start::before, .monaco-sash:not(.disabled).orthogonal-end::after { content: ' '; height: ${size * 2}px; width: ${size * 2}px; z-index: 100; display: block; cursor: all-scroll; position: absolute; } -src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts: - 1301 - 1302 private setViewState(state: IViewState): void { - 1303 this.viewState = state; - 1304: this.stylesheet.innerHTML = ` - 1305 .monaco-editor .dirty-diff-modified,.monaco-editor .dirty-diff-added{border-left-width:${state.width}px;} - 1306 .monaco-editor .dirty-diff-modified, .monaco-editor .dirty-diff-added, .monaco-editor .dirty-diff-deleted { - 1307 opacity: ${state.visibility === 'always' ? 1 : 0}; - src/vs/workbench/contrib/webview/browser/webviewIconManager.ts: 61 } 62 } @@ -380,6 +335,7 @@ src/vs/workbench/contrib/webview/browser/webviewIconManager.ts: 64: this._styleElement.innerHTML = cssRules.join('\n'); 65 } 66 } + 67 src/vs/workbench/contrib/webview/browser/pre/main.js: 386 // apply default styles @@ -415,24 +371,3 @@ src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts: 307 this.content.appendChild(innerContent); 308 309 model.snippets.forEach((snippet, i) => { - -src/vs/workbench/services/textMate/browser/abstractTextMateService.ts: - 312 grammarFactory.setTheme(theme, tokenColorMap); - 313 let colorMap = AbstractTextMateService._toColorMap(tokenColorMap); - 314 let cssRules = generateTokensCSSForColorMap(colorMap); - 315: this._styleElement.innerHTML = cssRules; - 316 TokenizationRegistry.setColorMap(colorMap); - 317 } - 318 - -src/vs/workbench/services/themes/browser/workbenchThemeService.ts: - 685 const elStyle = document.createElement('style'); - 686 elStyle.type = 'text/css'; - 687 elStyle.className = rulesClassName; - 688: elStyle.innerHTML = styleSheetContent; - 689 document.head.appendChild(elStyle); - 690 } else { - 691: (themeStyles[0]).innerHTML = styleSheetContent; - 692 } - 693 } - 694 From 758f35532b6058e7d07ccac0d38ce893c3daabed Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 9 Sep 2020 14:37:27 +0200 Subject: [PATCH 27/71] trusted types - use textContent for style elements, fyi @rebornix --- .vscode/searches/TrustedTypes.code-search | 21 +------------------ .../browser/diff/notebookTextDiffList.ts | 4 ++-- .../notebook/browser/view/notebookCellList.ts | 4 ++-- 3 files changed, 5 insertions(+), 24 deletions(-) diff --git a/.vscode/searches/TrustedTypes.code-search b/.vscode/searches/TrustedTypes.code-search index ad37236c4ac..edd2d713940 100644 --- a/.vscode/searches/TrustedTypes.code-search +++ b/.vscode/searches/TrustedTypes.code-search @@ -4,7 +4,7 @@ # Excluding: *.test.ts # ContextLines: 3 -44 results - 27 files +42 results - 25 files src/vs/base/browser/markdownRenderer.ts: 156 const strValue = values[0]; @@ -267,24 +267,6 @@ src/vs/workbench/contrib/debug/browser/repl.ts: 311 font-size: ${fontSize}px; 312 font-family: ${fontFamily}; -src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts: - 222 - 223 const newStyles = content.join('\n'); - 224 if (newStyles !== this.styleElement.innerHTML) { - 225: this.styleElement.innerHTML = newStyles; - 226 } - 227 } - 228 } - -src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts: - 1033 - 1034 const newStyles = content.join('\n'); - 1035 if (newStyles !== this.styleElement.innerHTML) { - 1036: this.styleElement.innerHTML = newStyles; - 1037 } - 1038 } - 1039 - src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts: 454 455 private getMarkdownDragImage(templateData: MarkdownCellRenderTemplate): HTMLElement { @@ -335,7 +317,6 @@ src/vs/workbench/contrib/webview/browser/webviewIconManager.ts: 64: this._styleElement.innerHTML = cssRules.join('\n'); 65 } 66 } - 67 src/vs/workbench/contrib/webview/browser/pre/main.js: 386 // apply default styles diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts index 2931710c259..6e664d46aa3 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffList.ts @@ -221,8 +221,8 @@ export class NotebookTextDiffList extends WorkbenchList imple } const newStyles = content.join('\n'); - if (newStyles !== this.styleElement.innerHTML) { - this.styleElement.innerHTML = newStyles; + if (newStyles !== this.styleElement.textContent) { + this.styleElement.textContent = newStyles; } } } diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts index f9846096655..9eee8bc18ee 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts @@ -1032,8 +1032,8 @@ export class NotebookCellList extends WorkbenchList implements ID } const newStyles = content.join('\n'); - if (newStyles !== this.styleElement.innerHTML) { - this.styleElement.innerHTML = newStyles; + if (newStyles !== this.styleElement.textContent) { + this.styleElement.textContent = newStyles; } } From d3b95c579301777b72b2a38a7ae422a36550665f Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 9 Sep 2020 06:07:03 -0700 Subject: [PATCH 28/71] Fix compile after merge --- .../contrib/terminal/browser/links/terminalWordLinkProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts index 754e65196a4..09a7b179d65 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts @@ -58,7 +58,7 @@ export class TerminalWordLinkProvider extends TerminalBaseLinkProvider { text = text.slice(0, -1); endX--; } - result.push(new TerminalLink(this._xterm, { start: { x: startX + 1, y }, end: { endX, y } }, text, this._xterm.buffer.active.viewportY, activateCallback, this._tooltipCallback, false, localize('searchWorkspace', 'Search workspace'), this._configurationService)); + result.push(new TerminalLink(this._xterm, { start: { x: startX + 1, y }, end: { x: endX, y } }, text, this._xterm.buffer.active.viewportY, activateCallback, this._tooltipCallback, false, localize('searchWorkspace', 'Search workspace'), this._configurationService)); text = ''; startX = -1; } From 17a2ff467d2c7a5d275cad259be3a3653d1655ae Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 9 Sep 2020 06:08:55 -0700 Subject: [PATCH 29/71] Use instantiation service to create TerminalLinks --- .../terminal/browser/links/terminalWordLinkProvider.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts index 09a7b179d65..2409bdd03da 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts @@ -58,7 +58,7 @@ export class TerminalWordLinkProvider extends TerminalBaseLinkProvider { text = text.slice(0, -1); endX--; } - result.push(new TerminalLink(this._xterm, { start: { x: startX + 1, y }, end: { x: endX, y } }, text, this._xterm.buffer.active.viewportY, activateCallback, this._tooltipCallback, false, localize('searchWorkspace', 'Search workspace'), this._configurationService)); + result.push(this._instantiationService.createInstance(TerminalLink, this._xterm, { start: { x: startX + 1, y }, end: { x: endX, y } }, text, this._xterm.buffer.active.viewportY, activateCallback, this._tooltipCallback, false, localize('searchWorkspace', 'Search workspace'))); text = ''; startX = -1; } @@ -80,7 +80,7 @@ export class TerminalWordLinkProvider extends TerminalBaseLinkProvider { text = text.slice(0, -1); endX--; } - result.push(new TerminalLink(this._xterm, { start: { x: startX + 1, y }, end: { x: endX, y } }, text, this._xterm.buffer.active.viewportY, activateCallback, this._tooltipCallback, false, localize('searchWorkspace', 'Search workspace'), this._configurationService)); + result.push(this._instantiationService.createInstance(TerminalLink, this._xterm, { start: { x: startX + 1, y }, end: { x: endX, y } }, text, this._xterm.buffer.active.viewportY, activateCallback, this._tooltipCallback, false, localize('searchWorkspace', 'Search workspace'))); } return result; From 3456e084e2b2bcca6c39f76b88b4126769312be4 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 9 Sep 2020 06:12:01 -0700 Subject: [PATCH 30/71] Consolidate colon trim logic --- .../browser/links/terminalWordLinkProvider.ts | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts index 2409bdd03da..90aa6852f58 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts @@ -53,12 +53,7 @@ export class TerminalWordLinkProvider extends TerminalBaseLinkProvider { // Add a link if this is a separator and also remove trailing colon if exists if (width !== 0 && wordSeparators.indexOf(chars) >= 0) { if (startX !== -1) { - let endX = x; - if (text.slice(-1) === ':') { - text = text.slice(0, -1); - endX--; - } - result.push(this._instantiationService.createInstance(TerminalLink, this._xterm, { start: { x: startX + 1, y }, end: { x: endX, y } }, text, this._xterm.buffer.active.viewportY, activateCallback, this._tooltipCallback, false, localize('searchWorkspace', 'Search workspace'))); + result.push(this._createTerminalLink(startX, x, y, text, activateCallback)); text = ''; startX = -1; } @@ -75,17 +70,29 @@ export class TerminalWordLinkProvider extends TerminalBaseLinkProvider { // Add the final link if there is one and also remove trailing colon if exists if (startX !== -1) { - let endX = line.length; - if (text.slice(-1) === ':') { - text = text.slice(0, -1); - endX--; - } - result.push(this._instantiationService.createInstance(TerminalLink, this._xterm, { start: { x: startX + 1, y }, end: { x: endX, y } }, text, this._xterm.buffer.active.viewportY, activateCallback, this._tooltipCallback, false, localize('searchWorkspace', 'Search workspace'))); + result.push(this._createTerminalLink(startX, line.length, y, text, activateCallback)); } return result; } + private _createTerminalLink(startX: number, endX: number, y: number, text: string, activateCallback: XtermLinkMatcherHandler): TerminalLink { + if (text.slice(-1) === ':') { + text = text.slice(0, -1); + endX--; + } + return this._instantiationService.createInstance(TerminalLink, + this._xterm, + { start: { x: startX + 1, y }, end: { x: endX, y } }, + text, + this._xterm.buffer.active.viewportY, + activateCallback, + this._tooltipCallback, + false, + localize('searchWorkspace', 'Search workspace') + ); + } + private async _activate(link: string) { const results = await this._searchService.fileSearch( this._fileQueryBuilder.file(this._workspaceContextService.getWorkspace().folders, { From 67def0324c0f3f91c80eeb90b2b3d8208fdaad42 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 9 Sep 2020 06:12:41 -0700 Subject: [PATCH 31/71] Avoid slice when checking colon --- .../contrib/terminal/browser/links/terminalWordLinkProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts index 90aa6852f58..2f5ea148017 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts @@ -77,7 +77,7 @@ export class TerminalWordLinkProvider extends TerminalBaseLinkProvider { } private _createTerminalLink(startX: number, endX: number, y: number, text: string, activateCallback: XtermLinkMatcherHandler): TerminalLink { - if (text.slice(-1) === ':') { + if (text.charAt(text.length - 1) === ':') { text = text.slice(0, -1); endX--; } From 5fabfbf6c086e36d2885a9942d639ae6b304cb68 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 9 Sep 2020 06:14:38 -0700 Subject: [PATCH 32/71] Check length again I prefer chatAt over slice as it's more obvious what's happening --- .../contrib/terminal/browser/links/terminalWordLinkProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts index 2f5ea148017..692d345da6c 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts @@ -77,7 +77,7 @@ export class TerminalWordLinkProvider extends TerminalBaseLinkProvider { } private _createTerminalLink(startX: number, endX: number, y: number, text: string, activateCallback: XtermLinkMatcherHandler): TerminalLink { - if (text.charAt(text.length - 1) === ':') { + if (text.length > 0 && text.charAt(text.length - 1) === ':') { text = text.slice(0, -1); endX--; } From 66e99a777cc79f892e186e515e26c42767d9eeb9 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 9 Sep 2020 06:28:49 -0700 Subject: [PATCH 33/71] Move comment into helper function --- .../terminal/browser/links/terminalWordLinkProvider.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts index 692d345da6c..a2cb37f78e4 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts @@ -50,7 +50,7 @@ export class TerminalWordLinkProvider extends TerminalBaseLinkProvider { const chars = cellData.getChars(); const width = cellData.getWidth(); - // Add a link if this is a separator and also remove trailing colon if exists + // Add a link if this is a separator if (width !== 0 && wordSeparators.indexOf(chars) >= 0) { if (startX !== -1) { result.push(this._createTerminalLink(startX, x, y, text, activateCallback)); @@ -68,7 +68,7 @@ export class TerminalWordLinkProvider extends TerminalBaseLinkProvider { text += chars; } - // Add the final link if there is one and also remove trailing colon if exists + // Add the final link if there is one if (startX !== -1) { result.push(this._createTerminalLink(startX, line.length, y, text, activateCallback)); } @@ -77,6 +77,7 @@ export class TerminalWordLinkProvider extends TerminalBaseLinkProvider { } private _createTerminalLink(startX: number, endX: number, y: number, text: string, activateCallback: XtermLinkMatcherHandler): TerminalLink { + // Remove trailing colon if there is one so the link is more useful if (text.length > 0 && text.charAt(text.length - 1) === ':') { text = text.slice(0, -1); endX--; From 9ff783bdd6913e733f24699725c2c6888646a833 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Wed, 9 Sep 2020 16:29:53 +0200 Subject: [PATCH 34/71] Update extensions/git/src/commands.ts --- extensions/git/src/commands.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index c6fc87a0290..f745e3f3d61 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -980,10 +980,10 @@ export class CommandCenter { return; } - await this._stageChanges(textEditor, [changes[index]]).then(() => { - const firstStagedLine = changes[index].modifiedStartLineNumber - 1; - textEditor.selections = [new Selection(firstStagedLine, 0, firstStagedLine, 0)]; - }); + await this._stageChanges(textEditor, [changes[index]]); + + const firstStagedLine = changes[index].modifiedStartLineNumber - 1; + textEditor.selections = [new Selection(firstStagedLine, 0, firstStagedLine, 0)]; } @command('git.stageSelectedRanges', { diff: true }) From bec65e80deed257f8de4803200f7ada430be8ad5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Wed, 9 Sep 2020 16:30:18 +0200 Subject: [PATCH 35/71] Update extensions/git/src/commands.ts --- extensions/git/src/commands.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index f745e3f3d61..20dbe68a385 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -1030,10 +1030,10 @@ export class CommandCenter { return; } - await this._revertChanges(textEditor, [...changes.slice(0, index), ...changes.slice(index + 1)]).then(() => { - const firstStagedLine = changes[index].modifiedStartLineNumber - 1; - textEditor.selections = [new Selection(firstStagedLine, 0, firstStagedLine, 0)]; - }); + await this._revertChanges(textEditor, [...changes.slice(0, index), ...changes.slice(index + 1)]); + + const firstStagedLine = changes[index].modifiedStartLineNumber - 1; + textEditor.selections = [new Selection(firstStagedLine, 0, firstStagedLine, 0)]; } @command('git.revertSelectedRanges', { diff: true }) From d9d314e8c6b509877d1307deee8b72a1d212674f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Wed, 9 Sep 2020 16:30:24 +0200 Subject: [PATCH 36/71] Update extensions/git/src/commands.ts --- extensions/git/src/commands.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 20dbe68a385..6ca709e2fb1 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -1056,9 +1056,8 @@ export class CommandCenter { } const selectionsBeforeRevert = textEditor.selections; - await this._revertChanges(textEditor, selectedChanges).then(() => { - textEditor.selections = selectionsBeforeRevert; - }); + await this._revertChanges(textEditor, selectedChanges); + textEditor.selections = selectionsBeforeRevert; } private async _revertChanges(textEditor: TextEditor, changes: LineChange[]): Promise { From c1e950222340898117bac71981ab58f3eceb0fc6 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 9 Sep 2020 16:51:31 +0200 Subject: [PATCH 37/71] Save prompt is shown while saving from settings split json editor (fix #106330) --- src/vs/workbench/contrib/files/browser/fileCommands.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts index cf8f51deedc..66a5bd24f00 100644 --- a/src/vs/workbench/contrib/files/browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts @@ -371,9 +371,14 @@ async function saveSelectedEditors(accessor: ServicesAccessor, options?: ISaveEd // Special treatment for side by side editors: if the active editor // has 2 sides, we consider both, to support saving both sides. - // We only allow this when saving, not for "Save As". + // 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. // See also https://github.com/microsoft/vscode/issues/4180 - if (activeGroup.activeEditor instanceof SideBySideEditorInput && !options?.saveAs) { + // See also https://github.com/microsoft/vscode/issues/106330 + if ( + activeGroup.activeEditor instanceof SideBySideEditorInput && + !options?.saveAs && !(activeGroup.activeEditor.primary.isUntitled() || activeGroup.activeEditor.secondary.isUntitled()) + ) { editors.push({ groupId: activeGroup.id, editor: activeGroup.activeEditor.primary }); editors.push({ groupId: activeGroup.id, editor: activeGroup.activeEditor.secondary }); } else { From 2a954244f0136a1a12dbc3b491a4ab8cf19b3720 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 9 Sep 2020 09:13:25 -0700 Subject: [PATCH 38/71] Only allow configurePlugin against main TS Server Fixes #106346 Looks like the TS Server doesn't support this in partial mode at the moment --- .../typescript-language-features/src/tsServer/server.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/typescript-language-features/src/tsServer/server.ts b/extensions/typescript-language-features/src/tsServer/server.ts index 6ad3d015679..fe8671ab906 100644 --- a/extensions/typescript-language-features/src/tsServer/server.ts +++ b/extensions/typescript-language-features/src/tsServer/server.ts @@ -318,7 +318,6 @@ class RequestRouter { 'open', 'updateOpen', 'configure', - 'configurePlugin', ]); constructor( @@ -490,7 +489,8 @@ export class SyntaxRoutingTsServer extends Disposable implements ITypeScriptServ private static readonly semanticCommands = new Set([ 'geterr', 'geterrForProject', - 'projectInfo' + 'projectInfo', + 'configurePlugin', ]); /** From c348165cc045cd48e08f85342a6380b670274620 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 9 Sep 2020 09:20:25 -0700 Subject: [PATCH 39/71] Adding more explicit typings for promises This gets us ready for TS 4.1 --- build/azure-pipelines/common/createAsset.ts | 2 +- build/azure-pipelines/common/publish-webview.ts | 4 ++-- build/lib/i18n.ts | 2 +- build/lib/preLaunch.ts | 2 +- src/vs/base/test/common/async.test.ts | 2 +- src/vs/base/test/common/utils.ts | 6 +++--- src/vs/code/node/cli.ts | 2 +- .../test/parameterHintsModel.test.ts | 2 +- .../contrib/rename/test/onTypeRename.test.ts | 2 +- .../contrib/smartSelect/bracketSelections.ts | 4 ++-- .../test/common/configurationService.test.ts | 4 ++-- .../debug/electron-main/extensionHostDebugIpc.ts | 2 +- src/vs/workbench/api/browser/mainThreadProgress.ts | 2 +- .../contrib/debug/browser/debugViewlet.ts | 2 +- .../electron-browser/extensionsActions.test.ts | 8 ++++---- .../browser/diff/notebookTextDiffEditor.ts | 2 +- .../notebook/browser/view/notebookCellList.ts | 4 ++-- .../electron-browser/startupProfiler.ts | 2 +- src/vs/workbench/contrib/remote/browser/remote.ts | 2 +- .../workbench/contrib/search/browser/searchView.ts | 4 ++-- .../workbench/contrib/search/common/searchModel.ts | 2 +- .../contrib/search/test/common/cacheState.test.ts | 2 +- .../contrib/snippets/browser/insertSnippet.ts | 2 +- .../browser/terminalProcessExtHostProxy.ts | 6 +++--- .../contrib/themes/browser/themes.contribution.ts | 2 +- .../contrib/userDataSync/browser/userDataSync.ts | 2 +- .../browser/authenticationService.ts | 2 +- .../electron-browser/configurationService.test.ts | 14 +++++++------- .../test/browser/decorationsService.test.ts | 4 ++-- .../test/browser/textModelResolverService.test.ts | 4 ++-- .../api/extHostDocumentSaveParticipant.test.ts | 2 +- .../test/browser/api/extHostNotebook.test.ts | 2 +- .../test/browser/api/extHostTreeViews.test.ts | 4 ++-- .../api/mainThreadDocumentContentProviders.test.ts | 2 +- 34 files changed, 55 insertions(+), 55 deletions(-) diff --git a/build/azure-pipelines/common/createAsset.ts b/build/azure-pipelines/common/createAsset.ts index 53c15a28fcf..d7e62629cb8 100644 --- a/build/azure-pipelines/common/createAsset.ts +++ b/build/azure-pipelines/common/createAsset.ts @@ -53,7 +53,7 @@ async function uploadBlob(blobService: azure.BlobService, quality: string, blobN } }; - await new Promise((c, e) => blobService.createBlockBlobFromLocalFile(quality, blobName, filePath, blobOptions, err => err ? e(err) : c())); + await new Promise((c, e) => blobService.createBlockBlobFromLocalFile(quality, blobName, filePath, blobOptions, err => err ? e(err) : c())); } function getEnv(name: string): string { diff --git a/build/azure-pipelines/common/publish-webview.ts b/build/azure-pipelines/common/publish-webview.ts index 143b61bb61a..b1947ebc024 100644 --- a/build/azure-pipelines/common/publish-webview.ts +++ b/build/azure-pipelines/common/publish-webview.ts @@ -17,7 +17,7 @@ const fileNames = [ ]; async function assertContainer(blobService: azure.BlobService, container: string): Promise { - await new Promise((c, e) => blobService.createContainerIfNotExists(container, { publicAccessLevel: 'blob' }, err => err ? e(err) : c())); + await new Promise((c, e) => blobService.createContainerIfNotExists(container, { publicAccessLevel: 'blob' }, err => err ? e(err) : c())); } async function doesBlobExist(blobService: azure.BlobService, container: string, blobName: string): Promise { @@ -33,7 +33,7 @@ async function uploadBlob(blobService: azure.BlobService, container: string, blo } }; - await new Promise((c, e) => blobService.createBlockBlobFromLocalFile(container, blobName, file, blobOptions, err => err ? e(err) : c())); + await new Promise((c, e) => blobService.createBlockBlobFromLocalFile(container, blobName, file, blobOptions, err => err ? e(err) : c())); } async function publish(commit: string, files: readonly string[]): Promise { diff --git a/build/lib/i18n.ts b/build/lib/i18n.ts index 89131b0129f..418a7b5c362 100644 --- a/build/lib/i18n.ts +++ b/build/lib/i18n.ts @@ -996,7 +996,7 @@ function createResource(project: string, slug: string, xlfFile: File, apiHostnam * https://dev.befoolish.co/tx-docs/public/projects/updating-content#what-happens-when-you-update-files */ function updateResource(project: string, slug: string, xlfFile: File, apiHostname: string, credentials: string): Promise { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { const data = JSON.stringify({ content: xlfFile.contents.toString() }); const options = { hostname: apiHostname, diff --git a/build/lib/preLaunch.ts b/build/lib/preLaunch.ts index bd084f5fec5..57441870cc4 100644 --- a/build/lib/preLaunch.ts +++ b/build/lib/preLaunch.ts @@ -15,7 +15,7 @@ const yarn = process.platform === 'win32' ? 'yarn.cmd' : 'yarn'; const rootDir = path.resolve(__dirname, '..', '..'); function runProcess(command: string, args: ReadonlyArray = []) { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { const child = spawn(command, args, { cwd: rootDir, stdio: 'inherit', env: process.env }); child.on('exit', err => !err ? resolve() : process.exit(err ?? 1)); child.on('error', reject); diff --git a/src/vs/base/test/common/async.test.ts b/src/vs/base/test/common/async.test.ts index 9d133c91dfa..aa055d77c67 100644 --- a/src/vs/base/test/common/async.test.ts +++ b/src/vs/base/test/common/async.test.ts @@ -527,7 +527,7 @@ suite('Async', () => { r1Queue.queue(syncPromiseFactory); - return new Promise(c => setTimeout(() => c(), 0)).then(() => { + return new Promise(c => setTimeout(() => c(), 0)).then(() => { const r1Queue2 = queue.queueFor(URI.file('/some/path')); assert.notEqual(r1Queue, r1Queue2); // previous one got disposed after finishing }); diff --git a/src/vs/base/test/common/utils.ts b/src/vs/base/test/common/utils.ts index d77f9f786e2..9175dad6365 100644 --- a/src/vs/base/test/common/utils.ts +++ b/src/vs/base/test/common/utils.ts @@ -25,21 +25,21 @@ export class DeferredPromise { } public complete(value: T) { - return new Promise(resolve => { + return new Promise(resolve => { this.completeCallback(value); resolve(); }); } public error(err: any) { - return new Promise(resolve => { + return new Promise(resolve => { this.errorCallback(err); resolve(); }); } public cancel() { - new Promise(resolve => { + new Promise(resolve => { this.errorCallback(canceled()); resolve(); }); diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts index 5f2d74a1c68..1cdfe65520f 100644 --- a/src/vs/code/node/cli.ts +++ b/src/vs/code/node/cli.ts @@ -138,7 +138,7 @@ export async function main(argv: string[]): Promise { child.stdout!.on('data', (data: Buffer) => console.log(data.toString('utf8').trim())); child.stderr!.on('data', (data: Buffer) => console.log(data.toString('utf8').trim())); - await new Promise(c => child.once('exit', () => c())); + await new Promise(c => child.once('exit', () => c())); }); } diff --git a/src/vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts b/src/vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts index 2d5e1967fe7..f6c75e1f194 100644 --- a/src/vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts +++ b/src/vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts @@ -290,7 +290,7 @@ suite('ParameterHintsModel', () => { hintsModel.trigger({ triggerKind: modes.SignatureHelpTriggerKind.Invoke }, 0); assert.strictEqual(-1, didRequestCancellationOf); - return new Promise((resolve, reject) => + return new Promise((resolve, reject) => hintsModel.onChangedHints(newParamterHints => { try { assert.strictEqual(0, didRequestCancellationOf); diff --git a/src/vs/editor/contrib/rename/test/onTypeRename.test.ts b/src/vs/editor/contrib/rename/test/onTypeRename.test.ts index 303b3554445..05dba5e4b62 100644 --- a/src/vs/editor/contrib/rename/test/onTypeRename.test.ts +++ b/src/vs/editor/contrib/rename/test/onTypeRename.test.ts @@ -104,7 +104,7 @@ suite('On type rename', () => { await operations(testEditor); - return new Promise((resolve) => { + return new Promise((resolve) => { setTimeout(() => { if (typeof expectedEndText === 'string') { assert.equal(editor.getModel()!.getValue(), expectedEndText); diff --git a/src/vs/editor/contrib/smartSelect/bracketSelections.ts b/src/vs/editor/contrib/smartSelect/bracketSelections.ts index 69bbb6eadea..3842ad87b54 100644 --- a/src/vs/editor/contrib/smartSelect/bracketSelections.ts +++ b/src/vs/editor/contrib/smartSelect/bracketSelections.ts @@ -19,8 +19,8 @@ export class BracketSelectionRangeProvider implements SelectionRangeProvider { result.push(bucket); const ranges = new Map>(); - await new Promise(resolve => BracketSelectionRangeProvider._bracketsRightYield(resolve, 0, model, position, ranges)); - await new Promise(resolve => BracketSelectionRangeProvider._bracketsLeftYield(resolve, 0, model, position, ranges, bucket)); + await new Promise(resolve => BracketSelectionRangeProvider._bracketsRightYield(resolve, 0, model, position, ranges)); + await new Promise(resolve => BracketSelectionRangeProvider._bracketsLeftYield(resolve, 0, model, position, ranges, bucket)); } return result; diff --git a/src/vs/platform/configuration/test/common/configurationService.test.ts b/src/vs/platform/configuration/test/common/configurationService.test.ts index 2d19fcf1308..ee4dcdfa773 100644 --- a/src/vs/platform/configuration/test/common/configurationService.test.ts +++ b/src/vs/platform/configuration/test/common/configurationService.test.ts @@ -89,7 +89,7 @@ suite('ConfigurationService', () => { test('trigger configuration change event when file does not exist', async () => { const testObject = disposables.add(new ConfigurationService(settingsResource, fileService)); await testObject.initialize(); - return new Promise(async (c, e) => { + return new Promise(async (c) => { disposables.add(Event.filter(testObject.onDidChangeConfiguration, e => e.source === ConfigurationTarget.USER)(() => { assert.equal(testObject.getValue('foo'), 'bar'); c(); @@ -104,7 +104,7 @@ suite('ConfigurationService', () => { await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "bar" }')); await testObject.initialize(); - return new Promise((c, e) => { + return new Promise((c) => { disposables.add(Event.filter(testObject.onDidChangeConfiguration, e => e.source === ConfigurationTarget.USER)(async (e) => { assert.equal(testObject.getValue('foo'), 'barz'); c(); diff --git a/src/vs/platform/debug/electron-main/extensionHostDebugIpc.ts b/src/vs/platform/debug/electron-main/extensionHostDebugIpc.ts index 923305acd75..074e9ecb3e6 100644 --- a/src/vs/platform/debug/electron-main/extensionHostDebugIpc.ts +++ b/src/vs/platform/debug/electron-main/extensionHostDebugIpc.ts @@ -101,7 +101,7 @@ export class ElectronExtensionHostDebugBroadcastChannel extends Extens }); }); - await new Promise(r => server.listen(0, r)); + await new Promise(r => server.listen(0, r)); codeWindow.win.on('close', () => server.close()); return { rendererDebugPort: (server.address() as AddressInfo).port }; diff --git a/src/vs/workbench/api/browser/mainThreadProgress.ts b/src/vs/workbench/api/browser/mainThreadProgress.ts index e3b6422e891..f038168e205 100644 --- a/src/vs/workbench/api/browser/mainThreadProgress.ts +++ b/src/vs/workbench/api/browser/mainThreadProgress.ts @@ -73,7 +73,7 @@ export class MainThreadProgress implements MainThreadProgressShape { private _createTask(handle: number) { return (progress: IProgress) => { - return new Promise(resolve => { + return new Promise(resolve => { this._progress.set(handle, { resolve, progress }); }); }; diff --git a/src/vs/workbench/contrib/debug/browser/debugViewlet.ts b/src/vs/workbench/contrib/debug/browser/debugViewlet.ts index d32774028e5..9496cfa1898 100644 --- a/src/vs/workbench/contrib/debug/browser/debugViewlet.ts +++ b/src/vs/workbench/contrib/debug/browser/debugViewlet.ts @@ -198,7 +198,7 @@ export class DebugViewPaneContainer extends ViewPaneContainer { if (state === State.Initializing) { this.progressService.withProgress({ location: VIEWLET_ID, }, _progress => { - return new Promise(resolve => this.progressResolve = resolve); + return new Promise(resolve => this.progressResolve = resolve); }); } diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts index 3f1897744af..9cd9d45d85e 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts @@ -469,7 +469,7 @@ suite('ExtensionsActions', () => { testObject.extension = extensions[0]; instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { identifier: local.identifier, version: '1.0.1' }))); assert.ok(!testObject.enabled); - return new Promise(c => { + return new Promise(c => { testObject.onDidChange(() => { if (testObject.enabled) { c(); @@ -1025,7 +1025,7 @@ suite('ExtensionsActions', () => { .then(async () => { instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { identifier: local[0].identifier, version: '1.0.2' }), aGalleryExtension('b', { identifier: local[1].identifier, version: '1.0.2' }), aGalleryExtension('c', local[2].manifest))); assert.ok(!testObject.enabled); - return new Promise(c => { + return new Promise(c => { testObject.onDidChange(() => { if (testObject.enabled) { c(); @@ -1046,7 +1046,7 @@ suite('ExtensionsActions', () => { .then(async () => { instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(...gallery)); assert.ok(!testObject.enabled); - return new Promise(c => { + return new Promise(c => { installEvent.fire({ identifier: local[0].identifier, gallery: gallery[0] }); testObject.onDidChange(() => { if (testObject.enabled) { @@ -1246,7 +1246,7 @@ suite('ReloadAction', () => { const extensions = await workbenchService.queryLocal(); testObject.extension = extensions[0]; - return new Promise(c => { + return new Promise(c => { testObject.onDidChange(() => { if (testObject.enabled && testObject.tooltip === 'Please reload Visual Studio Code to enable the updated extension.') { c(); diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts index 78bcd753b76..e973897c2ee 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts @@ -321,7 +321,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD r(); })); - return new Promise(resolve => { r = resolve; }); + return new Promise(resolve => { r = resolve; }); } getDomNode() { diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts index 9eee8bc18ee..c2cce19e6e5 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts @@ -760,7 +760,7 @@ export class NotebookCellList extends WorkbenchList implements ID upwards = true; } - const editorAttachedPromise = new Promise((resolve, reject) => { + const editorAttachedPromise = new Promise((resolve, reject) => { element.onDidChangeEditorAttachState(() => { element.editorAttached ? resolve() : reject(); }); @@ -1052,7 +1052,7 @@ export class NotebookCellList extends WorkbenchList implements ID } function getEditorAttachedPromise(element: CellViewModel) { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { Event.once(element.onDidChangeEditorAttachState)(() => element.editorAttached ? resolve() : reject()); }); } diff --git a/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts b/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts index 2e0f791450a..3e8c73d7e28 100644 --- a/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts +++ b/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts @@ -55,7 +55,7 @@ export class StartupProfiler implements IWorkbenchContribution { const removeArgs: string[] = ['--prof-startup']; const markerFile = readFile(profileFilenamePrefix).then(value => removeArgs.push(...value.toString().split('|'))) .then(() => rimraf(profileFilenamePrefix)) // (1) delete the file to tell the main process to stop profiling - .then(() => new Promise(resolve => { // (2) wait for main that recreates the fail to signal profiling has stopped + .then(() => new Promise(resolve => { // (2) wait for main that recreates the fail to signal profiling has stopped const check = () => { exists(profileFilenamePrefix).then(exists => { if (exists) { diff --git a/src/vs/workbench/contrib/remote/browser/remote.ts b/src/vs/workbench/contrib/remote/browser/remote.ts index a59510f2aa3..af5e7bdee76 100644 --- a/src/vs/workbench/contrib/remote/browser/remote.ts +++ b/src/vs/workbench/contrib/remote/browser/remote.ts @@ -274,7 +274,7 @@ class HelpItemValue { constructor(private commandService: ICommandService, public extensionDescription: IExtensionDescription, public remoteAuthority: string[] | undefined, private urlOrCommand?: string) { } get url(): Promise { - return new Promise(async (resolve) => { + return new Promise(async (resolve) => { if (this._url === undefined) { if (this.urlOrCommand) { let url = URI.parse(this.urlOrCommand); diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 96521c068ed..7e2f15d238f 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -596,7 +596,7 @@ export class SearchView extends ViewPane { this.progressService.withProgress({ location: this.getProgressLocation(), delay: 100, total: occurrences }, p => { progressReporter = p; - return new Promise(resolve => progressComplete = resolve); + return new Promise(resolve => progressComplete = resolve); }); const confirmation: IConfirmation = { @@ -1398,7 +1398,7 @@ export class SearchView extends ViewPane { private doSearch(query: ITextQuery, excludePatternText: string, includePatternText: string, triggeredOnType: boolean): Thenable { let progressComplete: () => void; this.progressService.withProgress({ location: this.getProgressLocation(), delay: triggeredOnType ? 300 : 0 }, _progress => { - return new Promise(resolve => progressComplete = resolve); + return new Promise(resolve => progressComplete = resolve); }); this.searchWidget.searchInput.clearMessage(); diff --git a/src/vs/workbench/contrib/search/common/searchModel.ts b/src/vs/workbench/contrib/search/common/searchModel.ts index 983355a2396..8e8dd4acf61 100644 --- a/src/vs/workbench/contrib/search/common/searchModel.ts +++ b/src/vs/workbench/contrib/search/common/searchModel.ts @@ -737,7 +737,7 @@ export class SearchResult extends Disposable { set query(query: ITextQuery | null) { // When updating the query we could change the roots, so keep a reference to them to clean up when we trigger `disposePastResults` const oldFolderMatches = this.folderMatches(); - new Promise(resolve => this.disposePastResults = resolve) + new Promise(resolve => this.disposePastResults = resolve) .then(() => oldFolderMatches.forEach(match => match.clear())) .then(() => oldFolderMatches.forEach(match => match.dispose())) .then(() => this._isDirty = false); diff --git a/src/vs/workbench/contrib/search/test/common/cacheState.test.ts b/src/vs/workbench/contrib/search/test/common/cacheState.test.ts index 6baa982999a..242b2b17d1e 100644 --- a/src/vs/workbench/contrib/search/test/common/cacheState.test.ts +++ b/src/vs/workbench/contrib/search/test/common/cacheState.test.ts @@ -207,7 +207,7 @@ suite('FileQueryCacheState', () => { } public awaitDisposal(n: number) { - return new Promise(resolve => { + return new Promise(resolve => { if (n === Object.keys(this.disposing).length) { resolve(); } else { diff --git a/src/vs/workbench/contrib/snippets/browser/insertSnippet.ts b/src/vs/workbench/contrib/snippets/browser/insertSnippet.ts index 5b5c430ac30..fd8440e4056 100644 --- a/src/vs/workbench/contrib/snippets/browser/insertSnippet.ts +++ b/src/vs/workbench/contrib/snippets/browser/insertSnippet.ts @@ -91,7 +91,7 @@ class InsertSnippetAction extends EditorAction { const clipboardService = accessor.get(IClipboardService); const quickInputService = accessor.get(IQuickInputService); - const snippet = await new Promise(async (resolve, reject) => { + const snippet = await new Promise(async (resolve, reject) => { const { lineNumber, column } = editor.getPosition(); let { snippet, name, langId } = Args.fromUser(arg); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy.ts index 7d7623c1a0d..03793e6a0e1 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy.ts @@ -43,9 +43,9 @@ export class TerminalProcessExtHostProxy extends Disposable implements ITerminal private readonly _onRequestLatency = this._register(new Emitter()); public readonly onRequestLatency: Event = this._onRequestLatency.event; - private _pendingInitialCwdRequests: ((value?: string | Thenable) => void)[] = []; - private _pendingCwdRequests: ((value?: string | Thenable) => void)[] = []; - private _pendingLatencyRequests: ((value?: number | Thenable) => void)[] = []; + private _pendingInitialCwdRequests: ((value: string | Thenable) => void)[] = []; + private _pendingCwdRequests: ((value: string | Thenable) => void)[] = []; + private _pendingLatencyRequests: ((value: number | Thenable) => void)[] = []; constructor( public terminalId: number, diff --git a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts index dca30b971db..aec062d251c 100644 --- a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts +++ b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts @@ -147,7 +147,7 @@ abstract class AbstractIconThemeAction extends Action { }, applyTheme ? 0 : 200); }; - return new Promise((s, _) => { + return new Promise((s, _) => { let isCompleted = false; const autoFocusIndex = firstIndex(picks, p => isItem(p) && p.id === currentTheme.id); diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index d8ed519d348..cf6a3bf0793 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -949,7 +949,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo }); } run(accessor: ServicesAccessor): any { - return new Promise((c, e) => { + return new Promise((c, e) => { const quickInputService = accessor.get(IQuickInputService); const commandService = accessor.get(ICommandService); const disposables = new DisposableStore(); diff --git a/src/vs/workbench/services/authentication/browser/authenticationService.ts b/src/vs/workbench/services/authentication/browser/authenticationService.ts index 82a27ec5e0c..7d058a717aa 100644 --- a/src/vs/workbench/services/authentication/browser/authenticationService.ts +++ b/src/vs/workbench/services/authentication/browser/authenticationService.ts @@ -329,7 +329,7 @@ export class AuthenticationService extends Disposable implements IAuthentication // Activate has already been called for the authentication provider, but it cannot block on registering itself // since this is sync and returns a disposable. So, wait for registration event to fire that indicates the // provider is now in the map. - await new Promise((resolve, _) => { + await new Promise((resolve, _) => { this.onDidRegisterAuthenticationProvider(e => { if (e.id === providerId) { provider = this._authenticationProviders.get(providerId); diff --git a/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts b/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts index e04efb9e339..56879ce1a36 100644 --- a/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts +++ b/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts @@ -1191,14 +1191,14 @@ suite('WorkspaceConfigurationService - Folder', () => { test('change event when there are global tasks', () => { fs.writeFileSync(globalTasksFile, '{ "version": "1.0.0", "tasks": [{ "taskName": "myTask" }'); - return new Promise((c) => testObject.onDidChangeConfiguration(() => c())); + return new Promise((c) => testObject.onDidChangeConfiguration(() => c())); }); test('creating workspace settings', async () => { fs.writeFileSync(globalSettingsFile, '{ "configurationService.folder.testSetting": "userValue" }'); await testObject.reloadConfiguration(); const workspaceSettingsResource = URI.file(path.join(workspaceDir, '.vscode', 'settings.json')); - await new Promise(async (c) => { + await new Promise(async (c) => { const disposable = testObject.onDidChangeConfiguration(e => { assert.ok(e.affectsConfiguration('configurationService.folder.testSetting')); assert.equal(testObject.getValue('configurationService.folder.testSetting'), 'workspaceValue'); @@ -1217,7 +1217,7 @@ suite('WorkspaceConfigurationService - Folder', () => { const workspaceSettingsResource = URI.file(path.join(workspaceDir, '.vscode', 'settings.json')); await fileService.writeFile(workspaceSettingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); - await new Promise(async (c) => { + await new Promise(async (c) => { const disposable = testObject.onDidChangeConfiguration(e => { assert.ok(e.affectsConfiguration('configurationService.folder.testSetting')); assert.equal(testObject.getValue('configurationService.folder.testSetting'), 'userValue'); @@ -1822,7 +1822,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { await workspaceService.removeFolders([uri]); fs.writeFileSync(path.join(uri.fsPath, '.vscode', 'settings.json'), '{ "configurationService.workspace.testResourceSetting": "workspaceFolderValue" }'); - return new Promise((c, e) => { + return new Promise((c, e) => { testObject.onDidChangeConfiguration(() => { try { assert.equal(testObject.getValue('configurationService.workspace.testResourceSetting', { resource: uri }), 'workspaceFolderValue'); @@ -1948,7 +1948,7 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { fs.writeFileSync(remoteSettingsFile, '{ "configurationService.remote.machineSetting": "remoteValue" }'); registerRemoteFileSystemProvider(); await initialize(); - const promise = new Promise((c, e) => { + const promise = new Promise((c, e) => { testObject.onDidChangeConfiguration(event => { try { assert.equal(event.source, ConfigurationTarget.USER); @@ -1968,7 +1968,7 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { fs.writeFileSync(remoteSettingsFile, '{ "configurationService.remote.machineSetting": "remoteValue" }'); registerRemoteFileSystemProviderOnActivation(); await initialize(); - const promise = new Promise((c, e) => { + const promise = new Promise((c, e) => { testObject.onDidChangeConfiguration(event => { try { assert.equal(event.source, ConfigurationTarget.USER); @@ -1989,7 +1989,7 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { resolveRemoteEnvironment(); await initialize(); assert.equal(testObject.getValue('configurationService.remote.machineSetting'), 'isSet'); - const promise = new Promise((c, e) => { + const promise = new Promise((c, e) => { testObject.onDidChangeConfiguration(event => { try { assert.equal(event.source, ConfigurationTarget.USER); diff --git a/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts b/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts index 2f1298ea85b..b129b381291 100644 --- a/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts +++ b/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts @@ -94,7 +94,7 @@ suite('DecorationsService', function () { // un-register -> ensure good event let didSeeEvent = false; - let p = new Promise(resolve => { + let p = new Promise(resolve => { service.onDidChangeDecorations(e => { assert.equal(e.affectsResource(uri), true); assert.deepEqual(service.getDecoration(uri, false), undefined); @@ -275,7 +275,7 @@ suite('DecorationsService', function () { data = service.getDecoration(uri2, true)!; assert.ok(data.tooltip); // emphazied items... - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { let l = service.onDidChangeDecorations(e => { l.dispose(); try { diff --git a/src/vs/workbench/services/textmodelResolver/test/browser/textModelResolverService.test.ts b/src/vs/workbench/services/textmodelResolver/test/browser/textModelResolverService.test.ts index f8b2464c808..a8abe3cce15 100644 --- a/src/vs/workbench/services/textmodelResolver/test/browser/textModelResolverService.test.ts +++ b/src/vs/workbench/services/textmodelResolver/test/browser/textModelResolverService.test.ts @@ -58,7 +58,7 @@ suite('Workbench - TextModelResolverService', () => { assert.ok(model); assert.equal(snapshotToString(((model as ResourceEditorModel).createSnapshot()!)), 'Hello Test'); let disposed = false; - let disposedPromise = new Promise(resolve => { + let disposedPromise = new Promise(resolve => { Event.once(model.onDispose)(() => { disposed = true; resolve(); @@ -201,7 +201,7 @@ suite('Workbench - TextModelResolverService', () => { modelRef1.dispose(); assert(!textModel.isDisposed(), 'the text model should still not be disposed'); - let p1 = new Promise(resolve => textModel.onWillDispose(resolve)); + let p1 = new Promise(resolve => textModel.onWillDispose(resolve)); modelRef2.dispose(); await p1; diff --git a/src/vs/workbench/test/browser/api/extHostDocumentSaveParticipant.test.ts b/src/vs/workbench/test/browser/api/extHostDocumentSaveParticipant.test.ts index 7a9a6ddc45b..8c4732b5915 100644 --- a/src/vs/workbench/test/browser/api/extHostDocumentSaveParticipant.test.ts +++ b/src/vs/workbench/test/browser/api/extHostDocumentSaveParticipant.test.ts @@ -207,7 +207,7 @@ suite('ExtHostDocumentSaveParticipant', () => { let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (event) { - event.waitUntil(new Promise((resolve, reject) => { + event.waitUntil(new Promise((resolve, reject) => { setTimeout(() => { try { assert.throws(() => event.waitUntil(timeout(10))); diff --git a/src/vs/workbench/test/browser/api/extHostNotebook.test.ts b/src/vs/workbench/test/browser/api/extHostNotebook.test.ts index c707cc3e276..d1093e90efe 100644 --- a/src/vs/workbench/test/browser/api/extHostNotebook.test.ts +++ b/src/vs/workbench/test/browser/api/extHostNotebook.test.ts @@ -135,7 +135,7 @@ suite('NotebookCell#Document', function () { test('cell document is vscode.TextDocument after changing it', async function () { - const p = new Promise((resolve, reject) => { + const p = new Promise((resolve, reject) => { extHostNotebooks.onDidChangeNotebookCells(e => { try { assert.strictEqual(e.changes.length, 1); diff --git a/src/vs/workbench/test/browser/api/extHostTreeViews.test.ts b/src/vs/workbench/test/browser/api/extHostTreeViews.test.ts index 9ba5a4e9f1b..690b204329a 100644 --- a/src/vs/workbench/test/browser/api/extHostTreeViews.test.ts +++ b/src/vs/workbench/test/browser/api/extHostTreeViews.test.ts @@ -248,7 +248,7 @@ suite('ExtHostTreeView', function () { }); async function runWithEventMerging(action: (resolve: () => void) => void) { - await new Promise((resolve) => { + await new Promise((resolve) => { let subscription: IDisposable | undefined = undefined; subscription = target.onRefresh.event(() => { subscription!.dispose(); @@ -256,7 +256,7 @@ suite('ExtHostTreeView', function () { }); onDidChangeTreeNode.fire(getNode('b')); }); - await new Promise(action); + await new Promise(action); } test('refresh parent and child node trigger refresh only on parent - scenario 1', async () => { diff --git a/src/vs/workbench/test/browser/api/mainThreadDocumentContentProviders.test.ts b/src/vs/workbench/test/browser/api/mainThreadDocumentContentProviders.test.ts index 22fb7d3502f..f8f606f9829 100644 --- a/src/vs/workbench/test/browser/api/mainThreadDocumentContentProviders.test.ts +++ b/src/vs/workbench/test/browser/api/mainThreadDocumentContentProviders.test.ts @@ -35,7 +35,7 @@ suite('MainThreadDocumentContentProviders', function () { }, ); - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { let expectedEvents = 1; model.onDidChangeContent(e => { expectedEvents -= 1; From 7e0c1aa76e4ce162679409828af17535d50a4424 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 9 Sep 2020 09:21:27 -0700 Subject: [PATCH 40/71] Don't use async on abstract functions --- src/vs/platform/driver/browser/baseDriver.ts | 2 +- src/vs/workbench/api/common/extHostExtensionService.ts | 2 +- src/vs/workbench/api/common/extHostTask.ts | 6 +++--- .../contrib/notebook/browser/contrib/coreActions.ts | 4 ++-- src/vs/workbench/contrib/tasks/common/problemCollectors.ts | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/vs/platform/driver/browser/baseDriver.ts b/src/vs/platform/driver/browser/baseDriver.ts index 2b4ee06778e..90610713f9d 100644 --- a/src/vs/platform/driver/browser/baseDriver.ts +++ b/src/vs/platform/driver/browser/baseDriver.ts @@ -186,5 +186,5 @@ export abstract class BaseWindowDriver implements IWindowDriver { return { x, y }; } - abstract async openDevTools(): Promise; + abstract openDevTools(): Promise; } diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 0bb5188614b..33b062b329c 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -747,7 +747,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme protected abstract _beforeAlmostReadyToRunExtensions(): Promise; protected abstract _getEntryPoint(extensionDescription: IExtensionDescription): string | undefined; protected abstract _loadCommonJSModule(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise; - public abstract async $setRemoteEnvironment(env: { [key: string]: string | null }): Promise; + public abstract $setRemoteEnvironment(env: { [key: string]: string | null }): Promise; } diff --git a/src/vs/workbench/api/common/extHostTask.ts b/src/vs/workbench/api/common/extHostTask.ts index 3fb57723234..f0bb1dfacaa 100644 --- a/src/vs/workbench/api/common/extHostTask.ts +++ b/src/vs/workbench/api/common/extHostTask.ts @@ -456,7 +456,7 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape, IExtHostTask }); } - public abstract async executeTask(extension: IExtensionDescription, task: vscode.Task): Promise; + public abstract executeTask(extension: IExtensionDescription, task: vscode.Task): Promise; public get taskExecutions(): vscode.TaskExecution[] { const result: vscode.TaskExecution[] = []; @@ -561,7 +561,7 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape, IExtHostTask }); } - protected abstract async resolveTaskInternal(resolvedTaskDTO: tasks.TaskDTO): Promise; + protected abstract resolveTaskInternal(resolvedTaskDTO: tasks.TaskDTO): Promise; public async $resolveTask(handle: number, taskDTO: tasks.TaskDTO): Promise { const handler = this._handlers.get(handle); @@ -601,7 +601,7 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape, IExtHostTask return await this.resolveTaskInternal(resolvedTaskDTO); } - public abstract async $resolveVariables(uriComponents: UriComponents, toResolve: { process?: { name: string; cwd?: string; path?: string }, variables: string[] }): Promise<{ process?: string, variables: { [key: string]: string; } }>; + public abstract $resolveVariables(uriComponents: UriComponents, toResolve: { process?: { name: string; cwd?: string; path?: string }, variables: string[] }): Promise<{ process?: string, variables: { [key: string]: string; } }>; public abstract $getDefaultShellAndArgs(): Promise<{ shell: string, args: string[] | string | undefined }>; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts index 4feefecc161..3a192a15261 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts @@ -145,7 +145,7 @@ abstract class NotebookAction extends Action2 { this.runWithContext(accessor, context); } - abstract async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise; + abstract runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise; private isNotebookActionContext(context?: unknown): context is INotebookActionContext { return !!context && !!(context as INotebookActionContext).notebookEditor; @@ -185,7 +185,7 @@ abstract class NotebookCellAction extends NotebookAction { this.runWithContext(accessor, context); } - abstract async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise; + abstract runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise; } registerAction2(class extends NotebookCellAction { diff --git a/src/vs/workbench/contrib/tasks/common/problemCollectors.ts b/src/vs/workbench/contrib/tasks/common/problemCollectors.ts index 70e9d1b1fb5..3a6610a6ee8 100644 --- a/src/vs/workbench/contrib/tasks/common/problemCollectors.ts +++ b/src/vs/workbench/contrib/tasks/common/problemCollectors.ts @@ -115,7 +115,7 @@ export abstract class AbstractProblemCollector implements IDisposable { } } - protected abstract async processLineInternal(line: string): Promise; + protected abstract processLineInternal(line: string): Promise; public dispose() { this.modelListeners.dispose(); From f3532c9061bb22e3423f21fb0b89ee3c1e76c001 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 9 Sep 2020 18:24:39 +0200 Subject: [PATCH 41/71] chore - use workingCopyService.isDirty instead notebook.isDirty --- .../contrib/notebook/browser/notebook.contribution.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 1bbae8210d7..105cfb02576 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -564,9 +564,9 @@ class NotebookFileTracker implements IWorkbenchContribution { constructor( @INotebookService private readonly _notebookService: INotebookService, @IEditorService private readonly _editorService: IEditorService, - @IWorkingCopyService workingCopyService: IWorkingCopyService, + @IWorkingCopyService private readonly _workingCopyService: IWorkingCopyService, ) { - this._dirtyListener = Event.debounce(workingCopyService.onDidChangeDirty, () => { }, 100)(() => { + this._dirtyListener = Event.debounce(_workingCopyService.onDidChangeDirty, () => { }, 100)(() => { const inputs = this._createMissingNotebookEditors(); this._editorService.openEditors(inputs); }); @@ -580,7 +580,7 @@ class NotebookFileTracker implements IWorkbenchContribution { const result: IResourceEditorInput[] = []; for (const notebook of this._notebookService.getNotebookTextModels()) { - if (notebook.isDirty && !this._editorService.isOpen({ resource: notebook.uri })) { + if (this._workingCopyService.isDirty(notebook.uri.with({ scheme: Schemas.vscodeNotebook })) && !this._editorService.isOpen({ resource: notebook.uri })) { result.push({ resource: notebook.uri, options: { inactive: true, preserveFocus: true, pinned: true } From fecec73e53f3a8076ee3a63b9fea6b102ef743a4 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Wed, 9 Sep 2020 09:29:42 -0700 Subject: [PATCH 42/71] Update Codicons - Update 'pinned' - Add 'export' - Compress 'merge' https://github.com/microsoft/vscode-codicons/commit/5bcb1a045413eb5a9737e6e46411e13f9c4e5c9c --- .../browser/ui/codicons/codicon/codicon.ttf | Bin 61024 -> 61120 bytes src/vs/base/common/codicons.ts | 1 + 2 files changed, 1 insertion(+) diff --git a/src/vs/base/browser/ui/codicons/codicon/codicon.ttf b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf index 82acc8995b8d7ee0338bc65547844f5f99b953ce..4a724c9d7d3c7dbd4440808379b2ddffb1e9102a 100644 GIT binary patch delta 4267 zcmYM%d3;pW6$kL&nMo!PLRKUzAt5Uv3E9X-$N~WZgs{f|0Svp!z8MfRfQX2Q=zv<( zC}s=CCqD@ zJ0o#dOx0@O^PRjy;DY&!mo&v3eg!zW4+sl-=s;4%Ac%cHpk!&r;wd3z7tzUm);`DQ zEie82|9bth`+N6yJnsOP{VD(PZ)A`!US5(tZAT^D|6!M(?~*4X-*e*;;RgTP$IfI8 z1pGYka~LNNOEHa~)tmf$UYATfgcjT%MxN4~C32tsFsBFuL#!0Wvgw_z^kVLldMAr@gVmS7p~Kr@zO1@6R3 z+yyUIVKwiw26tmE*5O`kz(zcPP1uZw;lmbe#iMu(zr!{>jwkRWTJaRN;~6}Q=dl|v zU=RL)KVmOl!k^HFSFj&{#$WIn4tnud9KstojKAY;`~&adpLicf@gY9K$2f+6;Zyt@ zpW$rg1!%>H7iN`RE#1QmFG-9w1voHq_N<(A_3ue7oVV1N|A$?F`EtM@k!uSUua-}H^}x}NP|7v#si#Nl`dYckI-{ww{L&|wJ zSFu;&%v0=IIP(?z7tR93PKL8kv8UlIQux4kzEfn%BRpHG*y(VVDP*xSNi6B%%M42L z_T$-|it7N*O2ric=PqS)#A#7nC2+jT=83aP;dR#4ifahY8pYKF=WfMy1!t|oeXRTl z!|g%r8(60##CIwv(wo4u4N5w&-mfH*l`&|^O{@

BzcS+1PUI(~@*&eOO5ht50zu z!`Y%>AA75kU{-#39($0uww0Y9cXK(z`BrgZ!#S(C#Nm9WxRBvEipw63Q49b$?TS$V z=X=FafOAeU9^m|-7!+{MD@F#K9|5mDFg@U0P|OiHKPe^(oSzl51M?xulpuaDG)xBRH28a|zBB#W&`KV;hecR&ah(j4e3V6ax&-b;T%Sd?)8K zyur8>gAT^67i#!$d0vIt+W%Vzk4=DlbzWCQdQ$VY(?MKTN#h41npbI1OMD6z2j= zqT+;r>7h6)V3HK4228Tz{D4VOILMmH>(2u?Q()2*4zZ>y&Ka1ViW3JWLvi-N^irHc zFqw+;2qsH$GQngk&M27Piqi@vN8u>1zsXe`VK8}$!we>0ajd}6^A2CU-I(6ISEsyI5A=RDb7xqa>XeMQ=vFdVJa0TD@>K* zjD_j1IBj7DD9&A&YQ+f*Gf;6B!)pfdgzUO9gB1rfOpW5Gh8dzbv|)xSj&GP@ih~@c zR&k`m3|AcPFm;M!9%iKCz=s)i13&*?d1JKVE&yhX;(h>Ttm4i9W}L!x)_Ntvdb1L{ z{u`9o^*>&TKkEb~9@dFU0$CgR{HBp<+=+FPk|5T}N;i#_%p;1sLYPMt9%Ow?NxpA-bZ_s| zJbO}c#|YD^aE|pU#a$$fy&rKu3A0^sX9=@IagPb}jN)z+=2>OGFB$t*NCH^xTOn>m zVV(!#?SZ>e82i2mrL4P@l=^aFB12y0*&bzgt-hL=kG&01ty*;5aVMAiq#20%^=+T-~pL9O? zm6V#4O({E4+EcrvPDnkR=1D6~TbI_J-XncQ`thC_J)3*($?(fKl5w@yyj~|V6EfFi z9?3kPH92cV*0$`S-t3v#o3fAe&g*?9r!r?*&W@b7b1vkj=5EW~mlv8>nYS+Qz5L+( zn*5ghz4_l1WEV^>Xzx>9SW~#Z@NiK+ON9b+J1Y=gUYkZ%PK-EHdgGbEUVmH6azpG2M!&$e&B^cg@aZO4j$Yx_-ai}&HF<_hm0O_aA@Gr4MSfXdUV+7 z+JxG&+S#@5)}9-lJbYeVa$Q~B=DJTuU_`-)#u3{_-ZHXvs#u#)nC22u_3%+Y{Sfk=7tRoPmK4BuN(i| z_|GQfOxQm$aAM`ewG+=Y<}|Kp+}Zfvq@YQalir+kX7Yh4p;MSpZO7u4=M=ekz2V;N zy2vk!j4MeiiVR3A>XXC>58f4l##@^V-H*32)z%-kv!uU#9~Ry%xV zzN=Se+rCxHg1oM;+`AEhB->z-0l_>Jg%yQW-1u%)lolcpk?|6l1J5xw~Z3y~Ak z^ZI)d&Q*Pw=wFeWIJs*}U}{8cX2|YqmlKM+WOvNH>VCFx;+62x1>OCwJzga#?u7hN zAy+09E{wbOO;m@ZD0f~2zs|+xy7^_7?_~P}Cq~Giww99{TrSt(whpJ3`v-VtG%uLH MXi3}ZufOQrf0S<{qLrZ#EgIA!)cYHFKV>D-U!kMX*G@A+Q%WP8uK&$I8i?}=l+_s4jb z0kQ!=)yxHTjpy%f`5o}P1oWt!+puEJoaa9M9EdmsyuE7P?7CT@cRo|g_nx4E^Y{V( zBOQ|XdN0M!Td;Kbh3$L=`3mUJvtiN9y5|c%UkQW_2Yfaxs9WCXGaY~9=R^3W=!JC) zX7_nMY{(kmi%PyBpmEWXr42zP#lZ0zpu6t_dt-{rAnpqS6T83IJgsZNkLcuk+I^2N z3erdYzh1rUd)D_`-gktL`zZhM|43O&l)NH4+Yd@&hnC;`dSrR7eL47Cf$z0f$*ci@ z#{-YgC;U%cu6+SNv(9WV@0s^)h27aw;-4}534D!{xD%gAU;YAD@tXKzfE3~f{Dxmq zk9d59Iy{IMks^Iig)UNnrI?9-<1cs~36>}cmFuxy zI!k!VQU6E0^DrL`Sb&9Cght$q#aMz{unf0iIaXjLZo?`x!He7Zo;6sDJ8&18aS!gr z2K)i{;Q?$y3tI6I9>!)of=BTfwqPr^VLP70Q+O87qYXRp0$#$)*o9Z{D*lMq@do~c zJ$TEDxAAA}!(Z_Z-opp@J3hoe@G%bJpZEly;t-DDC_cx(@CAMgx6T>6*~~-CWQg44T@a}vp}&wVHPTOD$F9qo`q>tsBihQQ*hx& zyj!B!$uLV5dm82z#qNe-TsTN!y;Vt{mdwByZvpRaQ|x`1RfsapsyzU}#ylGY(HJG~<++W z_bLfuWfD5lll49&9a-HsCkbYKKuHwqCMBUrgK6Onah_pX72G#+e?yYatPd#(Z(rN_ zF<*{5%r}aI4|7^E0${#X9CR3?7z;2~F(6>hC`JX$S;f$RIj0yOFyAQ#3CwxLNP)TF z{%E`~U0^OM<_ygDiirdBgJSl;{HT~hFh42g5zNnu$ppjI;V`3Meo;&-n9B<8DZB0? z{$gI3-*`g|GZ^k3hp`58MKR!Dt|~?y!$mRlU_~+hV0{#W5SF9iFcM)sis1;`K`|y_ z{T%*eOiWm|fy3;C<>PjkqOjK~<|%A|VzR<^Qp{M`K*h9$OW_bu#T#mx&lL~#ehmMCsx*rAGh8Mah$L&KIS z?rM1LFy0W?HtY?`&6pjoxXNKiC@yu_k&5dbc9i0xhb>oJ`LLrEmp^QU;xPcWQt?0l zJN6p>{V(ywIK@)}?0CiV0_+6ElLPETg)6L8isKJ^qY~cOY9%fUCMof-PFCXPe~Oap zSZmz#uVFF=uufIdiM3WqAnPBd^8q&w>jB|TVYDsiJaOSuEDovkE{b&le3 zhcD(TiD8|mB$jo)l6cm7B?+uIDM@5)P?E&DKuJH=g-ViH7b$U*(Wt~t#?9{e^CE6E z7Ar|-U7{p|b*Yjp)?1XgX;`MjO~b89+%zm#;-+DRl3do6O7dB6Q!>cS|0>>)xK3ZK z#C3X;64&YOF_E}Vzg>yz^fgLcr>|8!2*Tc>cqD|qQ}J*Jdza!d5w_X!u!DBAYzi4K zku9e}#%HwigAXd66=5GzJT<~Ttgw-Fvy#k~`p`7*erf`4j0+y#eQqQZhhUD&iKo?u~j zD(r5l3H#K$Hsn-jcIeY#p0JXzE#X1obHevT_(Uv>XpJ}!VIr4DZi(C%c`_9EyE3E;(*-TyxyPxJ&Ux@jK#8LPSD+!oftp#M;C? zNnMg^lI~AB-LI|RiT>mI?@RV1&q;1eK9o|DQk&A0nwmP&o4Po)EiEYRKzeNYnHB6@>C^P)fQB(tU7jM z!HpNIv#WvT}m6iX%>bMp>oH1vP<%H?TCwxq=bhCF^x$gc2qPv?&QtM?b=^tsUG zP2aZQ=0}4A`=-2j<<}#dljf&?oD_ Date: Wed, 9 Sep 2020 10:56:48 -0700 Subject: [PATCH 43/71] Add explicit undefined parameters / types These errors come from the new stricter types for Promises in TS 4.1 --- src/vs/base/node/zip.ts | 9 +++++---- src/vs/base/parts/quickinput/browser/quickInput.ts | 2 +- .../platform/launch/electron-main/launchMainService.ts | 4 ++-- .../contrib/debug/browser/debugConfigurationManager.ts | 2 +- .../contrib/tasks/browser/abstractTaskService.ts | 2 +- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/vs/base/node/zip.ts b/src/vs/base/node/zip.ts index 8047aa275f7..a0708bf92df 100644 --- a/src/vs/base/node/zip.ts +++ b/src/vs/base/node/zip.ts @@ -12,6 +12,7 @@ import { mkdirp, rimraf } from 'vs/base/node/pfs'; import { open as _openZip, Entry, ZipFile } from 'yauzl'; import * as yazl from 'yazl'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { assertIsDefined } from 'vs/base/common/types'; export interface IExtractOptions { overwrite?: boolean; @@ -161,24 +162,24 @@ function extractZip(zipfile: ZipFile, targetPath: string, options: IOptions, tok } function openZip(zipFile: string, lazy: boolean = false): Promise { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { _openZip(zipFile, lazy ? { lazyEntries: true } : undefined!, (error?: Error, zipfile?: ZipFile) => { if (error) { reject(toExtractError(error)); } else { - resolve(zipfile); + resolve(assertIsDefined(zipfile)); } }); }); } function openZipStream(zipFile: ZipFile, entry: Entry): Promise { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { zipFile.openReadStream(entry, (error?: Error, stream?: Readable) => { if (error) { reject(toExtractError(error)); } else { - resolve(stream); + resolve(assertIsDefined(stream)); } }); }); diff --git a/src/vs/base/parts/quickinput/browser/quickInput.ts b/src/vs/base/parts/quickinput/browser/quickInput.ts index d2ca01cf849..1061baac5ff 100644 --- a/src/vs/base/parts/quickinput/browser/quickInput.ts +++ b/src/vs/base/parts/quickinput/browser/quickInput.ts @@ -1416,7 +1416,7 @@ export class QuickInputController extends Disposable { } input(options: IInputOptions = {}, token: CancellationToken = CancellationToken.None): Promise { - return new Promise((resolve, reject) => { + return new Promise((resolve) => { if (token.isCancellationRequested) { resolve(undefined); return; diff --git a/src/vs/platform/launch/electron-main/launchMainService.ts b/src/vs/platform/launch/electron-main/launchMainService.ts index 7ebd8132de1..67a8d7f5345 100644 --- a/src/vs/platform/launch/electron-main/launchMainService.ts +++ b/src/vs/platform/launch/electron-main/launchMainService.ts @@ -230,7 +230,7 @@ export class LaunchMainService implements ILaunchMainService { getRemoteDiagnostics(options: IRemoteDiagnosticOptions): Promise<(IRemoteDiagnosticInfo | IRemoteDiagnosticError)[]> { const windows = this.windowsMainService.getWindows(); const promises: Promise[] = windows.map(window => { - return new Promise((resolve, reject) => { + return new Promise((resolve) => { const remoteAuthority = window.remoteAuthority; if (remoteAuthority) { const replyChannel = `vscode:getDiagnosticInfoResponse${window.id}`; @@ -254,7 +254,7 @@ export class LaunchMainService implements ILaunchMainService { resolve({ hostName: remoteAuthority, errorMessage: `Fetching remote diagnostics for '${remoteAuthority}' timed out.` }); }, 5000); } else { - resolve(); + resolve(undefined); } }); }); diff --git a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts index 7cc46462c3f..ee4b7b6ec24 100644 --- a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts +++ b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts @@ -317,7 +317,7 @@ export class ConfigurationManager implements IConfigurationManager { await (launch as Launch).writeConfiguration(config); await this.selectConfiguration(launch, config.name); })); - disposables.add(input.onDidHide(() => { chosenDidCancel = true; resolve(); })); + disposables.add(input.onDidHide(() => { chosenDidCancel = true; resolve(undefined); })); }); const token = new CancellationTokenSource(); diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 883e2ffcb85..7b7ba3dde69 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -2390,7 +2390,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } picker.dispose(); if (!selection) { - resolve(); + resolve(undefined); } resolve(selection); })); From e40d854df6306d91c4eff12d8e1559107242c1c8 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 9 Sep 2020 19:19:22 +0200 Subject: [PATCH 44/71] debt - IMainNotebookController#removeNotebookDocument --- src/vs/workbench/api/browser/mainThreadNotebook.ts | 11 ----------- .../contrib/notebook/browser/notebookServiceImpl.ts | 13 +++---------- .../contrib/notebook/common/notebookService.ts | 1 - 3 files changed, 3 insertions(+), 22 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index 8bfadac04d7..d9a4559fd98 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -166,14 +166,6 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo return textModel.applyEdit(modelVersionId, cellEdits, true); } - async removeNotebookTextModel(uri: URI): Promise { - // TODO@rebornix remove this? obsolete? - // TODO@rebornix, remove cell should use emitDelta as well to ensure document/editor events are sent together - this._proxy.$acceptDocumentAndEditorsDelta({ removedDocuments: [uri] }); - this._documentEventListenersMapping.get(uri)?.dispose(); - this._documentEventListenersMapping.delete(uri); - } - private _isDeltaEmpty(delta: INotebookDocumentsAndEditorsDelta) { if (delta.addedDocuments !== undefined && delta.addedDocuments.length > 0) { return false; @@ -478,9 +470,6 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo onDidReceiveMessage: (editorId: string, rendererType: string | undefined, message: unknown) => { this._proxy.$onDidReceiveMessage(editorId, rendererType, message); }, - removeNotebookDocument: async (uri: URI) => { - return this.removeNotebookTextModel(uri); - }, save: async (uri: URI, token: CancellationToken) => { return this._proxy.$saveNotebook(viewType, uri, token); }, diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index 28b08f6d08d..f34eefdd2c2 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -919,19 +919,12 @@ export class NotebookService extends Disposable implements INotebookService, ICu } }); + modelData.model.dispose(); + modelData.dispose(); + willRemovedEditors.forEach(e => this._notebookEditors.delete(e.getId())); - - const provider = this._notebookProviders.get(modelData!.model.viewType); - - if (provider) { - provider.controller.removeNotebookDocument(modelData!.model.uri); - modelData!.model.dispose(); - } - - this._onNotebookEditorsRemove.fire(willRemovedEditors.map(e => e)); this._onDidRemoveNotebookDocument.fire(modelData.model.uri); - modelData.dispose(); } } } diff --git a/src/vs/workbench/contrib/notebook/common/notebookService.ts b/src/vs/workbench/contrib/notebook/common/notebookService.ts index 99a754b4e55..b5010650f00 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookService.ts @@ -28,7 +28,6 @@ export interface IMainNotebookController { reloadNotebook(mainthreadTextModel: NotebookTextModel): Promise; resolveNotebookEditor(viewType: string, uri: URI, editorId: string): Promise; onDidReceiveMessage(editorId: string, rendererType: string | undefined, message: any): void; - removeNotebookDocument(uri: URI): Promise; save(uri: URI, token: CancellationToken): Promise; saveAs(uri: URI, target: URI, token: CancellationToken): Promise; backup(uri: URI, token: CancellationToken): Promise; From 9753398da5cca522ba8de488e092733121744f42 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 9 Sep 2020 20:34:54 +0200 Subject: [PATCH 45/71] debt - invoke resolve notebook when opening a notebook in an editor, not when loading a notebook from source --- .../notebook/browser/notebookDiffEditorInput.ts | 6 +++--- .../contrib/notebook/browser/notebookEditor.ts | 6 +++++- .../contrib/notebook/browser/notebookEditorInput.ts | 4 ++-- .../contrib/notebook/browser/notebookServiceImpl.ts | 13 ++++++++----- .../contrib/notebook/common/notebookEditorModel.ts | 10 ++++------ .../common/notebookEditorModelResolverService.ts | 12 ++++++------ .../contrib/notebook/common/notebookService.ts | 3 ++- 7 files changed, 30 insertions(+), 24 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookDiffEditorInput.ts b/src/vs/workbench/contrib/notebook/browser/notebookDiffEditorInput.ts index 6ab3652eaba..1c1c65468a6 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookDiffEditorInput.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookDiffEditorInput.ts @@ -193,14 +193,14 @@ ${patterns} return; } - async resolve(editorId?: string): Promise { + async resolve(): Promise { if (!await this._notebookService.canResolve(this.viewType!)) { return null; } if (!this._textModel) { - this._textModel = await this._notebookModelResolverService.resolve(this.resource, this.viewType!, editorId); - this._originalTextModel = await this._notebookModelResolverService.resolve(this.originalResource, this.viewType!, editorId); + this._textModel = await this._notebookModelResolverService.resolve(this.resource, this.viewType!); + this._originalTextModel = await this._notebookModelResolverService.resolve(this.originalResource, this.viewType!); } return new NotebookDiffEditorModel(this._originalTextModel!.object as NotebookEditorModel, this._textModel.object as NotebookEditorModel); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts index 03021e5ba4d..624eff243f0 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts @@ -25,6 +25,7 @@ import { IEditorDropService } from 'vs/workbench/services/editor/browser/editorD import { IEditorGroup, IEditorGroupsService, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { NotebookEditorOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; const NOTEBOOK_EDITOR_VIEW_STATE_PREFERENCE_KEY = 'NotebookEditorViewState'; @@ -54,6 +55,7 @@ export class NotebookEditor extends EditorPane { @IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService, @IEditorDropService private readonly _editorDropService: IEditorDropService, @INotificationService private readonly _notificationService: INotificationService, + @INotebookService private readonly _notebookService: INotebookService, @INotebookEditorWidgetService private readonly _notebookWidgetService: INotebookEditorWidgetService, ) { super(NotebookEditor.ID, telemetryService, themeService, storageService); @@ -152,7 +154,7 @@ export class NotebookEditor extends EditorPane { this._widget.value!.layout(this._dimension, this._rootElement); } - const model = await input.resolve(this._widget.value!.getId()); + const model = await input.resolve(); // Check for cancellation if (token.isCancellationRequested) { return undefined; @@ -174,6 +176,8 @@ export class NotebookEditor extends EditorPane { return; } + await this._notebookService.resolveNotebookEditor(model.viewType, model.resource, this._widget.value!.getId()); + const viewState = this._loadNotebookEditorViewState(input); await this._widget.value!.setModel(model.notebook, viewState); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorInput.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorInput.ts index 1df2b996c03..9e11a1b57c5 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorInput.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorInput.ts @@ -168,13 +168,13 @@ ${patterns} return; } - async resolve(editorId?: string): Promise { + async resolve(): Promise { if (!await this._notebookService.canResolve(this.viewType!)) { return null; } if (!this._textModel) { - this._textModel = await this._notebookModelResolverService.resolve(this.resource, this.viewType!, editorId); + this._textModel = await this._notebookModelResolverService.resolve(this.resource, this.viewType!); this._register(this._textModel.object.onDidChangeDirty(() => { this._onDidChangeDirty.fire(); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index f34eefdd2c2..d05617cf39a 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -605,7 +605,7 @@ export class NotebookService extends Disposable implements INotebookService, ICu return this.notebookRenderersInfoStore.get(id); } - async resolveNotebook(viewType: string, uri: URI, forceReload: boolean, editorId?: string, backupId?: string): Promise { + async resolveNotebook(viewType: string, uri: URI, forceReload: boolean, backupId?: string): Promise { if (!await this.canResolve(viewType)) { throw new Error(`CANNOT load notebook, no provider for '${viewType}'`); @@ -637,10 +637,6 @@ export class NotebookService extends Disposable implements INotebookService, ICu // after the document is added to the store and sent to ext host, we transform the ouputs await this.transformTextModelOutputs(notebookModel); - if (editorId) { - await provider.controller.resolveNotebookEditor(viewType, uri, editorId); - } - return modelData.model; } @@ -786,6 +782,13 @@ export class NotebookService extends Disposable implements INotebookService, ICu return ret; } + async resolveNotebookEditor(viewType: string, uri: URI, editorId: string): Promise { + const entry = this._notebookProviders.get(viewType); + if (entry) { + entry.controller.resolveNotebookEditor(viewType, uri, editorId); + } + } + removeNotebookEditor(editor: INotebookEditor) { const editorCache = this._notebookEditors.get(editor.getId()); diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts index af198636e5a..11357ebb093 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts @@ -24,8 +24,6 @@ export interface INotebookLoadOptions { * Go to disk bypassing any cache of the model if any. */ forceReadFromDisk?: boolean; - - editorId?: string; } @@ -124,7 +122,7 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM async load(options?: INotebookLoadOptions): Promise { if (options?.forceReadFromDisk) { - return this._loadFromProvider(true, undefined, undefined); + return this._loadFromProvider(true, undefined); } if (this.isResolved()) { @@ -137,11 +135,11 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM return this; // Make sure meanwhile someone else did not succeed in loading } - return this._loadFromProvider(false, options?.editorId, backup?.meta?.backupId); + return this._loadFromProvider(false, backup?.meta?.backupId); } - private async _loadFromProvider(forceReloadFromDisk: boolean, editorId: string | undefined, backupId: string | undefined) { - this._notebook = await this._notebookService.resolveNotebook(this.viewType!, this.resource, forceReloadFromDisk, editorId, backupId); + private async _loadFromProvider(forceReloadFromDisk: boolean, backupId: string | undefined) { + this._notebook = await this._notebookService.resolveNotebook(this.viewType!, this.resource, forceReloadFromDisk, backupId); const newStats = await this._resolveStats(this.resource); this._lastResolvedFileStat = newStats; diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorModelResolverService.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorModelResolverService.ts index 2206d05e61e..1667fe75ca4 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorModelResolverService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorModelResolverService.ts @@ -16,7 +16,7 @@ export const INotebookEditorModelResolverService = createDecorator>; + resolve(resource: URI, viewType?: string): Promise>; } @@ -33,7 +33,7 @@ export class NotebookModelReferenceCollection extends ReferenceCollection { const resource = URI.parse(key); - let [viewType, editorId] = args as [string | undefined, string | undefined]; + let [viewType] = args as [string | undefined]; if (!viewType) { viewType = this._notebookService.getContributedNotebookProviders(resource)[0]?.id; } @@ -42,7 +42,7 @@ export class NotebookModelReferenceCollection extends ReferenceCollection> { - const reference = this._data.acquire(resource.toString(), viewType, editorId); + async resolve(resource: URI, viewType?: string): Promise> { + const reference = this._data.acquire(resource.toString(), viewType); const model = await reference.object; - NotebookModelResolverService._autoReferenceDirtyModel(model, () => this._data.acquire(resource.toString(), viewType, editorId)); + NotebookModelResolverService._autoReferenceDirtyModel(model, () => this._data.acquire(resource.toString(), viewType)); return { object: model, dispose() { reference.dispose(); } diff --git a/src/vs/workbench/contrib/notebook/common/notebookService.ts b/src/vs/workbench/contrib/notebook/common/notebookService.ts index b5010650f00..9b1704f0135 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookService.ts @@ -54,7 +54,7 @@ export interface INotebookService { getContributedNotebookOutputRenderers(id: string): NotebookOutputRendererInfo | undefined; getRendererInfo(id: string): INotebookRendererInfo | undefined; - resolveNotebook(viewType: string, uri: URI, forceReload: boolean, editorId?: string, backupId?: string): Promise; + resolveNotebook(viewType: string, uri: URI, forceReload: boolean, backupId?: string): Promise; getNotebookTextModel(uri: URI): NotebookTextModel | undefined; getNotebookTextModels(): Iterable; getContributedNotebookProviders(resource: URI): readonly NotebookProviderInfo[]; @@ -71,6 +71,7 @@ export interface INotebookService { getToCopy(): { items: NotebookCellTextModel[], isCopy: boolean; } | undefined; // editor events + resolveNotebookEditor(viewType: string, uri: URI, editorId: string): Promise; addNotebookEditor(editor: IEditor): void; removeNotebookEditor(editor: IEditor): void; getNotebookEditor(editorId: string): IEditor | undefined; From eb05b79e9b222c18ca307ed5e0a0f920c95c2827 Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 8 Sep 2020 16:48:14 -0700 Subject: [PATCH 46/71] do not need isUntitled. --- .../api/browser/mainThreadNotebook.ts | 14 ++- .../browser/view/renderers/cellRenderer.ts | 2 + .../common/model/notebookTextModel.ts | 106 +++++------------- 3 files changed, 37 insertions(+), 85 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index d9a4559fd98..e75b6f5d174 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -8,6 +8,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter } from 'vs/base/common/event'; import { combinedDisposable, Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { ResourceMap } from 'vs/base/common/map'; +import { Schemas } from 'vs/base/common/network'; import { URI, UriComponents } from 'vs/base/common/uri'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -456,9 +457,14 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo if (data.cells.length) { textModel.initialize(data!.cells); - } else { - const mainCell = textModel.createCellTextModel('', textModel.resolvedLanguages.length ? textModel.resolvedLanguages[0] : '', CellKind.Code, [], undefined); - textModel.insertTemplateCell(mainCell); + } else if (textModel.uri.scheme === Schemas.untitled) { + textModel.initialize([{ + cellKind: CellKind.Code, + language: textModel.resolvedLanguages.length ? textModel.resolvedLanguages[0] : '', + outputs: [], + metadata: undefined, + source: '' + }]); } this._proxy.$acceptDocumentPropertiesChanged(textModel.uri, { metadata: textModel.metadata }); @@ -604,7 +610,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo const textModel = this._notebookService.getNotebookTextModel(URI.from(resource)); if (textModel) { - textModel.handleEdit(label, () => { + textModel.handleUnknownEdit(label, () => { return this._proxy.$undoNotebook(textModel.viewType, textModel.uri, editId, textModel.isDirty); }, () => { return this._proxy.$redoNotebook(textModel.viewType, textModel.uri, editId, textModel.isDirty); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index fcffc8ee0fd..c388e390c67 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -1021,6 +1021,8 @@ export class ListTopCellToolbar extends Disposable { this._modelDisposables.add(this.notebookEditor.viewModel.onDidChangeViewCells(() => { this.updateClass(); })); + + this.updateClass(); } })); diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index 5537f5d9f15..3846db316af 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -113,7 +113,7 @@ class DelayedEmitter { emit(data: { triggerDirty: { value: boolean } | undefined, - modelContentChange: { value: NotebookTextModelChangedEvent } | undefined, + contentChange: { value: NotebookTextModelChangedEvent } | undefined, }) { this._increaseVersion(); @@ -121,10 +121,10 @@ class DelayedEmitter { this._textModel.setDirty(data.triggerDirty.value); } - if (data.modelContentChange) { + if (data.contentChange) { this._onDidChangeContent.fire( { - ...data.modelContentChange.value, + ...data.contentChange.value, versionId: this._textModel.versionId } ); @@ -164,7 +164,6 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel metadata: NotebookDocumentMetadata = notebookDocumentMetadataDefaults; transientOptions: TransientOptions = { transientMetadata: {}, transientOutputs: false }; - private _isUntitled: boolean | undefined = undefined; private _versionId = 0; //#region selection TODO@rebornix this shouldn't be here @@ -251,14 +250,15 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel return new NotebookCellTextModel(cellUri, cellHandle, cell.source, cell.language, cell.cellKind, cell.outputs || [], cell.metadata, this.transientOptions, this._modelService); }); - this._isUntitled = false; - for (let i = 0; i < mainCells.length; i++) { this._mapping.set(mainCells[i].handle, mainCells[i]); const dirtyStateListener = mainCells[i].onDidChangeContent(() => { - this.setDirty(true); - this._increaseVersionId(); - this._onDidChangeContent.fire({ kind: NotebookCellsChangeType.ChangeCellContent, versionId: this.versionId, synchronous: true }); + this._eventEmitter.emit({ + triggerDirty: { value: true }, + contentChange: { + value: { kind: NotebookCellsChangeType.ChangeCellContent, versionId: this.versionId, synchronous: true } + } + }); }); this._cellListeners.set(mainCells[i].handle, dirtyStateListener); @@ -325,7 +325,6 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel return; } - this._isUntitled = false; //TODO@rebornix fishy? const oldViewCells = this._cells.slice(0); const oldMap = new Map(this._mapping); @@ -348,7 +347,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel const dirtyStateListener = cell.onDidChangeContent(() => { this._eventEmitter.emit({ triggerDirty: { value: true }, - modelContentChange: { value: { kind: NotebookCellsChangeType.ChangeCellContent, versionId: this.versionId, synchronous: true } } + contentChange: { value: { kind: NotebookCellsChangeType.ChangeCellContent, versionId: this.versionId, synchronous: true } } }); }); this._cellListeners.set(cell.handle, dirtyStateListener); @@ -378,7 +377,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel // should be deferred this._eventEmitter.emit({ triggerDirty: { value: true }, - modelContentChange: { + contentChange: { value: { kind: NotebookCellsChangeType.ModelChange, versionId: this._versionId, @@ -389,7 +388,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel }); } - handleEdit(label: string | undefined, undo: () => void, redo: () => void): void { + handleUnknownEdit(label: string | undefined, undo: () => void, redo: () => void): void { this._operationManager.pushEditOperation({ type: UndoRedoElementType.Resource, resource: this.uri, @@ -404,6 +403,10 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this.setDirty(true); } + handleUnknownChange() { + this.setDirty(true); + } + createSnapshot(preserveBOM?: boolean): ITextSnapshot { return new NotebookTextModelSnapshot(this); } @@ -412,17 +415,13 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this._versionId = this._versionId + 1; } - handleUnknownChange() { - this.setDirty(true); - } - updateLanguages(languages: string[]) { const allLanguages = languages.find(lan => lan === '*'); this._allLanguages = allLanguages !== undefined; this._languages = languages; const resolvedLanguages = this.resolvedLanguages; - if (this._isUntitled && resolvedLanguages.length && this._cells.length) { + if (resolvedLanguages.length && this._cells.length) { this._cells[0].language = resolvedLanguages[0]; } } @@ -431,70 +430,17 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this.metadata = metadata; this._eventEmitter.emit({ triggerDirty: { value: true }, - modelContentChange: { value: { kind: NotebookCellsChangeType.ChangeDocumentMetadata, versionId: this.versionId, metadata: this.metadata, synchronous: true } } + contentChange: { value: { kind: NotebookCellsChangeType.ChangeDocumentMetadata, versionId: this.versionId, metadata: this.metadata, synchronous: true } } }); } - insertTemplateCell(cell: NotebookCellTextModel) { - if (this._cells.length > 0 || this._isUntitled !== undefined) { - return; - } - - this._isUntitled = true; - this._cells = [cell]; - this._mapping.set(cell.handle, cell); - - const dirtyStateListener = cell.onDidChangeContent(() => { - this._isUntitled = false; - this._eventEmitter.emit({ - triggerDirty: { value: true }, - modelContentChange: { value: { kind: NotebookCellsChangeType.ChangeCellContent, versionId: this.versionId, synchronous: true } } - }); - }); - - this._cellListeners.set(cell.handle, dirtyStateListener); - this.setDirty(false); - - // this._eventEmitter.emit({ - // triggerDirty: undefined, - // modelContentChange: { value: { - // kind: NotebookCellsChangeType.ModelChange, - // versionId: this._versionId, changes: - // [[ - // 0, - // 0, - // [cell] - // ]], - // synchronous: true - // } } - // }); - - this.setDirty(false); - // this._onDidChangeContent.fire(NotebookCellsChangeType.ModelChange); - - this._onDidChangeContent.fire({ - kind: NotebookCellsChangeType.ModelChange, - versionId: this._versionId, changes: - [[ - 0, - 0, - [cell] - ]], - synchronous: true - }); - - return; - } - private _insertNewCell(index: number, cells: NotebookCellTextModel[], synchronous: boolean, endSelections?: number[]): void { - this._isUntitled = false; - for (let i = 0; i < cells.length; i++) { this._mapping.set(cells[i].handle, cells[i]); const dirtyStateListener = cells[i].onDidChangeContent(() => { this._eventEmitter.emit({ triggerDirty: { value: true }, - modelContentChange: { value: { kind: NotebookCellsChangeType.ChangeCellContent, versionId: this.versionId, synchronous: true } } + contentChange: { value: { kind: NotebookCellsChangeType.ChangeCellContent, versionId: this.versionId, synchronous: true } } }); }); @@ -504,7 +450,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this._cells.splice(index, 0, ...cells); this._eventEmitter.emit({ triggerDirty: { value: true }, - modelContentChange: { + contentChange: { value: { kind: NotebookCellsChangeType.ModelChange, versionId: this._versionId, changes: @@ -523,8 +469,6 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel } private _removeCell(index: number, count: number, synchronous: boolean, endSelections?: number[]) { - this._isUntitled = false; - for (let i = index; i < index + count; i++) { const cell = this._cells[i]; this._cellListeners.get(cell.handle)?.dispose(); @@ -533,7 +477,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this._cells.splice(index, count); this._eventEmitter.emit({ triggerDirty: { value: true }, - modelContentChange: { value: { kind: NotebookCellsChangeType.ModelChange, versionId: this._versionId, changes: [[index, count, []]], synchronous, endSelections } } + contentChange: { value: { kind: NotebookCellsChangeType.ModelChange, versionId: this._versionId, changes: [[index, count, []]], synchronous, endSelections } } }); } @@ -582,7 +526,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this._eventEmitter.emit({ triggerDirty: { value: triggerDirtyChange }, - modelContentChange: { + contentChange: { value: { kind: NotebookCellsChangeType.ChangeCellMetadata, versionId: this._versionId, index: this._cells.indexOf(cell), metadata: cell.metadata, synchronous: true } } }); @@ -596,7 +540,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this._eventEmitter.emit({ triggerDirty: { value: !this.transientOptions.transientOutputs }, - modelContentChange: { + contentChange: { value: { kind: NotebookCellsChangeType.Output, versionId: this.versionId, @@ -626,7 +570,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this._eventEmitter.emit({ triggerDirty: undefined, - modelContentChange: { value: { kind: NotebookCellsChangeType.ChangeLanguage, versionId: this._versionId, index: this._cells.indexOf(cell), language: languageId, synchronous: true } } + contentChange: { value: { kind: NotebookCellsChangeType.ChangeLanguage, versionId: this._versionId, index: this._cells.indexOf(cell), language: languageId, synchronous: true } } }); } } @@ -671,7 +615,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this._cells.splice(newIdx, 0, ...cells); this._eventEmitter.emit({ triggerDirty: { value: true }, - modelContentChange: { value: { kind: NotebookCellsChangeType.Move, versionId: this._versionId, index, length, newIdx, cells, synchronous, endSelections } } + contentChange: { value: { kind: NotebookCellsChangeType.Move, versionId: this._versionId, index, length, newIdx, cells, synchronous, endSelections } } }); } From deaf9f268923634f3f754cba3db5b89a11784ea0 Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 8 Sep 2020 16:57:55 -0700 Subject: [PATCH 47/71] :guard: polish nb tests. --- .../src/notebook.test.ts | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/extensions/vscode-notebook-tests/src/notebook.test.ts b/extensions/vscode-notebook-tests/src/notebook.test.ts index d549dc8dc3f..311455f7573 100644 --- a/extensions/vscode-notebook-tests/src/notebook.test.ts +++ b/extensions/vscode-notebook-tests/src/notebook.test.ts @@ -1032,15 +1032,14 @@ suite('metadata', () => { }); suite('regression', () => { - test('microsoft/vscode-github-issue-notebooks#26. Insert template cell in the new empty document', async function () { - assertInitalState(); - const resource = await createRandomFile('', undefined, 'empty', '.vsctestnb'); - await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first'); - assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), ''); - assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript'); - await saveFileAndCloseAll(resource); - }); + // test('microsoft/vscode-github-issue-notebooks#26. Insert template cell in the new empty document', async function () { + // assertInitalState(); + // await vscode.commands.executeCommand('workbench.action.files.newUntitledFile', { "viewType": "notebookCoreTest" }); + // assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first'); + // assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), ''); + // assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript'); + // await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + // }); test('#97830, #97764. Support switch to other editor types', async function () { assertInitalState(); @@ -1073,7 +1072,7 @@ suite('regression', () => { // now it's dirty, open the resource with notebook editor should open a new one await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); assert.notEqual(vscode.notebook.activeNotebookEditor, undefined, 'notebook first'); - assert.notEqual(vscode.window.activeTextEditor, undefined); + // assert.notEqual(vscode.window.activeTextEditor, undefined); await vscode.commands.executeCommand('workbench.action.closeAllEditors'); }); From eb7f042acf7e711d75a3a09ee0af32a26a901a08 Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 8 Sep 2020 16:59:47 -0700 Subject: [PATCH 48/71] remove selections from nb text model. --- .../notebook/common/model/notebookTextModel.ts | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index 3846db316af..52cdfbcabf1 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -136,17 +136,12 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel private readonly _onWillDispose: Emitter = this._register(new Emitter()); private readonly _onDidChangeContent = this._register(new Emitter()); - private readonly _onDidChangeMetadata = this._register(new Emitter()); - readonly onWillDispose: Event = this._onWillDispose.event; readonly onDidChangeContent = this._onDidChangeContent.event; - readonly onDidChangeMetadata = this._onDidChangeMetadata.event; - private _cellhandlePool: number = 0; private _mapping: Map = new Map(); private _cellListeners: Map = new Map(); private _cells: NotebookCellTextModel[]; - private _languages: string[] = []; private _allLanguages: boolean = false; @@ -165,15 +160,6 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel metadata: NotebookDocumentMetadata = notebookDocumentMetadataDefaults; transientOptions: TransientOptions = { transientMetadata: {}, transientOutputs: false }; private _versionId = 0; - - //#region selection TODO@rebornix this shouldn't be here - - private readonly _emitSelections = this._register(new Emitter()); - readonly emitSelections = this._emitSelections.event; - private _onDidSelectionChangeProxy = this._register(new Emitter()); - readonly onDidSelectionChange = this._onDidSelectionChangeProxy.event; - - private _operationManager: NotebookOperationManager; private _eventEmitter: DelayedEmitter; @@ -181,8 +167,6 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel protected readonly _onDidChangeDirty = this._register(new Emitter()); readonly onDidChangeDirty = this._onDidChangeDirty.event; - - constructor( readonly viewType: string, readonly supportBackup: boolean, From e491efbaabaf1416b8c56a7ea669db1b6099efad Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 8 Sep 2020 17:10:01 -0700 Subject: [PATCH 49/71] replace changeCellLanguage to applyEdit --- .../notebook/browser/contrib/coreActions.ts | 7 ++++- .../notebook/browser/diff/cellComponents.ts | 8 +++++- .../common/model/notebookTextModel.ts | 28 +++++++++++-------- .../contrib/notebook/common/notebookCommon.ts | 10 ++++++- 4 files changed, 38 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts index 3a192a15261..fc890cbcdad 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts @@ -1387,7 +1387,12 @@ export class ChangeCellLanguageAction extends NotebookCellAction { } else if (selection.languageId !== 'markdown' && context.cell?.language === 'markdown') { await changeCellToKind(CellKind.Code, { cell: context.cell, notebookEditor: context.notebookEditor }, selection.languageId); } else { - context.notebookEditor.viewModel!.notebookDocument.changeCellLanguage(context.cell.handle, selection.languageId); + const index = context.notebookEditor.viewModel!.notebookDocument.cells.indexOf(context.cell.model); + context.notebookEditor.viewModel!.notebookDocument.applyEdit( + context.notebookEditor.viewModel!.notebookDocument.versionId, + [{ editType: CellEditType.CellLanguage, index, language: selection.languageId }], + true + ); } } } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts index 327c3ee0721..bb972c44851 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts @@ -449,8 +449,14 @@ abstract class AbstractCellRenderer extends Disposable { } if (newLangauge !== undefined && newLangauge !== this.cell.modified!.language) { - this.notebookEditor.textModel!.changeCellLanguage(this.cell.modified!.handle, newLangauge); + const index = this.notebookEditor.textModel!.cells.indexOf(this.cell.modified!); + this.notebookEditor.textModel!.applyEdit( + this.notebookEditor.textModel!.versionId, + [{ editType: CellEditType.CellLanguage, index, language: newLangauge }], + true + ); } + const index = this.notebookEditor.textModel!.cells.indexOf(this.cell.modified!); if (index < 0) { diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index 52cdfbcabf1..89e2de9a83b 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -297,6 +297,10 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this._assertIndex(edit.index); this._changeCellMetadata(this._cells[edit.index].handle, edit.metadata, true); break; + case CellEditType.CellLanguage: + this._assertIndex(edit.index); + this._changeCellLanguage(this._cells[edit.index].handle, edit.language); + break; } } @@ -516,6 +520,18 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel }); } + private _changeCellLanguage(handle: number, languageId: string) { + const cell = this._mapping.get(handle); + if (cell && cell.language !== languageId) { + cell.language = languageId; + + this._eventEmitter.emit({ + triggerDirty: { value: true }, + contentChange: { value: { kind: NotebookCellsChangeType.ChangeLanguage, versionId: this._versionId, index: this._cells.indexOf(cell), language: languageId, synchronous: true } } + }); + } + } + // TODO@rebornix, once adopted the new Edit API in ext host, the method should be private. _spliceNotebookCellOutputs(cellHandle: number, splices: NotebookCellOutputsSplice[]): void { const cell = this._mapping.get(cellHandle); @@ -547,18 +563,6 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel //#region Notebook Text Model Edit API - changeCellLanguage(handle: number, languageId: string) { - const cell = this._mapping.get(handle); - if (cell && cell.language !== languageId) { - cell.language = languageId; - - this._eventEmitter.emit({ - triggerDirty: undefined, - contentChange: { value: { kind: NotebookCellsChangeType.ChangeLanguage, versionId: this._versionId, index: this._cells.indexOf(cell), language: languageId, synchronous: true } } - }); - } - } - insertCell(index: number, cell: NotebookCellTextModel, synchronous: boolean, pushUndoStop: boolean, beforeSelections: number[] | undefined, endSelections: number[] | undefined): void { if (pushUndoStop) { this._operationManager.pushEditOperation(new InsertCellEdit(this.uri, index, cell, { diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 66443e9d3ac..b6ee3c00851 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -412,6 +412,7 @@ export const enum CellEditType { Replace = 1, Output = 2, Metadata = 3, + CellLanguage = 4 } export interface ICellDto2 { @@ -441,7 +442,14 @@ export interface ICellMetadataEdit { metadata: NotebookCellMetadata; } -export type ICellEditOperation = ICellReplaceEdit | ICellOutputEdit | ICellMetadataEdit; + +export interface ICellLanguageEdit { + editType: CellEditType.CellLanguage; + index: number; + language: string; +} + +export type ICellEditOperation = ICellReplaceEdit | ICellOutputEdit | ICellMetadataEdit | ICellLanguageEdit; export interface INotebookEditData { documentVersionId: number; From 4e70e4a6993b0175f2661cc066774c07989881ab Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 9 Sep 2020 10:30:08 -0700 Subject: [PATCH 50/71] fix #105916. expand metadata section if modified. --- .../notebook/browser/diff/cellComponents.ts | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts index bb972c44851..56eea16ad4b 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/cellComponents.ts @@ -283,6 +283,14 @@ abstract class AbstractCellRenderer extends Disposable { this._metadataHeaderContainer = DOM.append(this._diffEditorContainer, DOM.$('.metadata-header-container')); this._metadataInfoContainer = DOM.append(this._diffEditorContainer, DOM.$('.metadata-info-container')); + const checkIfModified = (cell: CellDiffViewModel) => { + return cell.type !== 'delete' && cell.type !== 'insert' && hash(this._getFormatedMetadataJSON(cell.original?.metadata || {}, cell.original?.language)) !== hash(this._getFormatedMetadataJSON(cell.modified?.metadata ?? {}, cell.modified?.language)); + }; + + if (checkIfModified(this.cell)) { + this.cell.metadataFoldingState = PropertyFoldingState.Expanded; + } + this._metadataHeader = this.instantiationService.createInstance( PropertyHeader, this.cell, @@ -291,7 +299,7 @@ abstract class AbstractCellRenderer extends Disposable { { updateInfoRendering: this.updateMetadataRendering.bind(this), checkIfModified: (cell) => { - return cell.type !== 'delete' && cell.type !== 'insert' && hash(this._getFormatedMetadataJSON(cell.original?.metadata || {}, cell.original?.language)) !== hash(this._getFormatedMetadataJSON(cell.modified?.metadata ?? {}, cell.modified?.language)); + return checkIfModified(cell); }, getFoldingState: (cell) => { return cell.metadataFoldingState; @@ -311,6 +319,14 @@ abstract class AbstractCellRenderer extends Disposable { this._outputHeaderContainer = DOM.append(this._diffEditorContainer, DOM.$('.output-header-container')); this._outputInfoContainer = DOM.append(this._diffEditorContainer, DOM.$('.output-info-container')); + const checkIfOutputsModified = (cell: CellDiffViewModel) => { + return cell.type !== 'delete' && cell.type !== 'insert' && !this.notebookEditor.textModel!.transientOptions.transientOutputs && cell.type === 'modified' && hash(cell.original?.outputs ?? []) !== hash(cell.modified?.outputs ?? []); + }; + + if (checkIfOutputsModified(this.cell)) { + this.cell.outputFoldingState = PropertyFoldingState.Expanded; + } + this._outputHeader = this.instantiationService.createInstance( PropertyHeader, this.cell, @@ -319,10 +335,10 @@ abstract class AbstractCellRenderer extends Disposable { { updateInfoRendering: this.updateOutputRendering.bind(this), checkIfModified: (cell) => { - return cell.type !== 'delete' && cell.type !== 'insert' && !this.notebookEditor.textModel!.transientOptions.transientOutputs && cell.type === 'modified' && hash(cell.original?.outputs ?? []) !== hash(cell.modified?.outputs ?? []); + return checkIfOutputsModified(cell); }, getFoldingState: (cell) => { - return this.cell.outputFoldingState; + return cell.outputFoldingState; }, updateFoldingState: (cell, state) => { cell.outputFoldingState = state; From 8bc3145bc555efde56a6b9a1cf04c6f451329827 Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 9 Sep 2020 11:59:08 -0700 Subject: [PATCH 51/71] move dirty state to NotebookEditorModel. --- .../api/browser/mainThreadNotebook.ts | 20 ++++- .../common/model/notebookTextModel.ts | 76 ++++++++----------- .../contrib/notebook/common/notebookCommon.ts | 12 ++- .../notebook/common/notebookEditorModel.ts | 27 ++++--- .../notebookEditorModelResolverService.ts | 4 +- 5 files changed, 78 insertions(+), 61 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index e75b6f5d174..7d22de8bc10 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -21,6 +21,7 @@ import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/com import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellEditType, CellKind, DisplayOrderKey, ICellEditOperation, ICellRange, IEditor, IMainCellDto, INotebookDocumentFilter, NotebookCellMetadata, NotebookCellOutputsSplice, NotebookCellsChangeType, NotebookDocumentMetadata, NOTEBOOK_DISPLAY_ORDER, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { IMainNotebookController, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { ExtHostContext, ExtHostNotebookShape, IExtHostContext, INotebookCellStatusBarEntryDto, INotebookDocumentsAndEditorsDelta, INotebookModelAddedData, MainContext, MainThreadNotebookShape, NotebookEditorRevealType, NotebookExtensionDescription } from '../common/extHost.protocol'; class DocumentAndEditorState { @@ -147,7 +148,8 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo @IEditorService private readonly editorService: IEditorService, @IAccessibilityService private readonly accessibilityService: IAccessibilityService, @ILogService private readonly logService: ILogService, - @INotebookCellStatusBarService private readonly cellStatusBarService: INotebookCellStatusBarService + @INotebookCellStatusBarService private readonly cellStatusBarService: INotebookCellStatusBarService, + @IWorkingCopyService private readonly _workingCopyService: IWorkingCopyService, ) { super(); this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostNotebook); @@ -295,7 +297,15 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo } : e ); - this._proxy.$acceptModelChanged(textModel.uri, data, textModel.isDirty); + + /** + * TODO@rebornix, @jrieken + * When a document is modified, it will trigger onDidChangeContent events. + * The first event listener is this one, which doesn't know if the text model is dirty or not. It can ask `workingCopyService` but get the wrong result + * The second event listener is `NotebookEditorModel`, which will then set `isDirty` to `true`. + * Since `e.transient` decides if the model should be dirty or not, we will use the same logic here. + */ + this._proxy.$acceptModelChanged(textModel.uri, data, !e.transient); this._proxy.$acceptDocumentPropertiesChanged(textModel.uri, { metadata: null }); })); @@ -611,9 +621,11 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo if (textModel) { textModel.handleUnknownEdit(label, () => { - return this._proxy.$undoNotebook(textModel.viewType, textModel.uri, editId, textModel.isDirty); + const isDirty = this._workingCopyService.isDirty(textModel.uri.with({ scheme: Schemas.vscodeNotebook })); + return this._proxy.$undoNotebook(textModel.viewType, textModel.uri, editId, isDirty); }, () => { - return this._proxy.$redoNotebook(textModel.viewType, textModel.uri, editId, textModel.isDirty); + const isDirty = this._workingCopyService.isDirty(textModel.uri.with({ scheme: Schemas.vscodeNotebook })); + return this._proxy.$redoNotebook(textModel.viewType, textModel.uri, editId, isDirty); }); } } diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index 89e2de9a83b..2bdff2d1a74 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -112,15 +112,10 @@ class DelayedEmitter { } emit(data: { - triggerDirty: { value: boolean } | undefined, contentChange: { value: NotebookTextModelChangedEvent } | undefined, }) { this._increaseVersion(); - if (data.triggerDirty) { - this._textModel.setDirty(data.triggerDirty.value); - } - if (data.contentChange) { this._onDidChangeContent.fire( { @@ -163,10 +158,6 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel private _operationManager: NotebookOperationManager; private _eventEmitter: DelayedEmitter; - private _dirty = false; - protected readonly _onDidChangeDirty = this._register(new Emitter()); - readonly onDidChangeDirty = this._onDidChangeDirty.event; - constructor( readonly viewType: string, readonly supportBackup: boolean, @@ -201,17 +192,6 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel return this._versionId; } - get isDirty() { - return this._dirty; - } - - setDirty(newState: boolean) { - if (this._dirty !== newState) { - this._dirty = newState; - this._onDidChangeDirty.fire(); - } - } - createCellTextModel( source: string, language: string, @@ -238,9 +218,8 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this._mapping.set(mainCells[i].handle, mainCells[i]); const dirtyStateListener = mainCells[i].onDidChangeContent(() => { this._eventEmitter.emit({ - triggerDirty: { value: true }, contentChange: { - value: { kind: NotebookCellsChangeType.ChangeCellContent, versionId: this.versionId, synchronous: true } + value: { kind: NotebookCellsChangeType.ChangeCellContent, versionId: this.versionId, synchronous: true, transient: false } } }); }); @@ -334,8 +313,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel ); const dirtyStateListener = cell.onDidChangeContent(() => { this._eventEmitter.emit({ - triggerDirty: { value: true }, - contentChange: { value: { kind: NotebookCellsChangeType.ChangeCellContent, versionId: this.versionId, synchronous: true } } + contentChange: { value: { kind: NotebookCellsChangeType.ChangeCellContent, versionId: this.versionId, synchronous: true, transient: false } } }); }); this._cellListeners.set(cell.handle, dirtyStateListener); @@ -364,13 +342,13 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel // should be deferred this._eventEmitter.emit({ - triggerDirty: { value: true }, contentChange: { value: { kind: NotebookCellsChangeType.ModelChange, versionId: this._versionId, changes: diffs, - synchronous + synchronous, + transient: false } } }); @@ -388,11 +366,30 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel redo(); }, }); - this.setDirty(true); + + this._eventEmitter.emit({ + contentChange: { + value: { + kind: NotebookCellsChangeType.Unknown, + transient: false, + synchronous: true, + versionId: this._versionId, + } + } + }); } handleUnknownChange() { - this.setDirty(true); + this._eventEmitter.emit({ + contentChange: { + value: { + kind: NotebookCellsChangeType.Unknown, + transient: false, + synchronous: true, + versionId: this._versionId, + } + } + }); } createSnapshot(preserveBOM?: boolean): ITextSnapshot { @@ -417,8 +414,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel updateNotebookMetadata(metadata: NotebookDocumentMetadata) { this.metadata = metadata; this._eventEmitter.emit({ - triggerDirty: { value: true }, - contentChange: { value: { kind: NotebookCellsChangeType.ChangeDocumentMetadata, versionId: this.versionId, metadata: this.metadata, synchronous: true } } + contentChange: { value: { kind: NotebookCellsChangeType.ChangeDocumentMetadata, versionId: this.versionId, metadata: this.metadata, synchronous: true, transient: false } } }); } @@ -427,8 +423,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this._mapping.set(cells[i].handle, cells[i]); const dirtyStateListener = cells[i].onDidChangeContent(() => { this._eventEmitter.emit({ - triggerDirty: { value: true }, - contentChange: { value: { kind: NotebookCellsChangeType.ChangeCellContent, versionId: this.versionId, synchronous: true } } + contentChange: { value: { kind: NotebookCellsChangeType.ChangeCellContent, versionId: this.versionId, synchronous: true, transient: false } } }); }); @@ -437,7 +432,6 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this._cells.splice(index, 0, ...cells); this._eventEmitter.emit({ - triggerDirty: { value: true }, contentChange: { value: { kind: NotebookCellsChangeType.ModelChange, @@ -448,7 +442,8 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel cells ]], synchronous, - endSelections: endSelections + endSelections: endSelections, + transient: false } } }); @@ -464,8 +459,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel } this._cells.splice(index, count); this._eventEmitter.emit({ - triggerDirty: { value: true }, - contentChange: { value: { kind: NotebookCellsChangeType.ModelChange, versionId: this._versionId, changes: [[index, count, []]], synchronous, endSelections } } + contentChange: { value: { kind: NotebookCellsChangeType.ModelChange, versionId: this._versionId, changes: [[index, count, []]], synchronous, endSelections, transient: false } } }); } @@ -513,9 +507,8 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel } this._eventEmitter.emit({ - triggerDirty: { value: triggerDirtyChange }, contentChange: { - value: { kind: NotebookCellsChangeType.ChangeCellMetadata, versionId: this._versionId, index: this._cells.indexOf(cell), metadata: cell.metadata, synchronous: true } + value: { kind: NotebookCellsChangeType.ChangeCellMetadata, versionId: this._versionId, index: this._cells.indexOf(cell), metadata: cell.metadata, synchronous: true, transient: !triggerDirtyChange } } }); } @@ -526,8 +519,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel cell.language = languageId; this._eventEmitter.emit({ - triggerDirty: { value: true }, - contentChange: { value: { kind: NotebookCellsChangeType.ChangeLanguage, versionId: this._versionId, index: this._cells.indexOf(cell), language: languageId, synchronous: true } } + contentChange: { value: { kind: NotebookCellsChangeType.ChangeLanguage, versionId: this._versionId, index: this._cells.indexOf(cell), language: languageId, synchronous: true, transient: false } } }); } } @@ -539,7 +531,6 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel cell.spliceNotebookCellOutputs(splices); this._eventEmitter.emit({ - triggerDirty: { value: !this.transientOptions.transientOutputs }, contentChange: { value: { kind: NotebookCellsChangeType.Output, @@ -602,8 +593,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel const cells = this._cells.splice(index, length); this._cells.splice(newIdx, 0, ...cells); this._eventEmitter.emit({ - triggerDirty: { value: true }, - contentChange: { value: { kind: NotebookCellsChangeType.Move, versionId: this._versionId, index, length, newIdx, cells, synchronous, endSelections } } + contentChange: { value: { kind: NotebookCellsChangeType.Move, versionId: this._versionId, index, length, newIdx, cells, synchronous, endSelections, transient: false } } }); } diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index b6ee3c00851..444d228c1f2 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -348,7 +348,8 @@ export enum NotebookCellsChangeType { ChangeCellMetadata = 7, Output = 8, ChangeCellContent = 9, - ChangeDocumentMetadata = 10 + ChangeDocumentMetadata = 10, + Unknown = 11 } export interface NotebookCellsInitializeEvent { @@ -405,8 +406,13 @@ export interface NotebookDocumentChangeMetadataEvent { readonly metadata: NotebookDocumentMetadata | undefined; } -export type NotebookCellsChangedEventDto = NotebookCellsInitializeEvent | NotebookDocumentChangeMetadataEvent | NotebookCellContentChangeEvent | NotebookCellsModelChangedEvent | NotebookCellsModelMoveEvent | NotebookOutputChangedEvent | NotebookCellsChangeLanguageEvent | NotebookCellsChangeMetadataEvent; -export type NotebookTextModelChangedEvent = (NotebookCellsInitializeEvent | NotebookDocumentChangeMetadataEvent | NotebookCellContentChangeEvent | NotebookCellsModelChangedEvent | NotebookCellsModelMoveEvent | NotebookOutputChangedEvent | NotebookCellsChangeLanguageEvent | NotebookCellsChangeMetadataEvent) & { synchronous: boolean; endSelections?: number[] }; +export interface NotebookDocumentUnknownChangeEvent { + readonly kind: NotebookCellsChangeType.Unknown; + readonly versionId: number; +} + +export type NotebookCellsChangedEventDto = NotebookCellsInitializeEvent | NotebookDocumentChangeMetadataEvent | NotebookCellContentChangeEvent | NotebookCellsModelChangedEvent | NotebookCellsModelMoveEvent | NotebookOutputChangedEvent | NotebookCellsChangeLanguageEvent | NotebookCellsChangeMetadataEvent | NotebookDocumentUnknownChangeEvent; +export type NotebookTextModelChangedEvent = (NotebookCellsInitializeEvent | NotebookDocumentChangeMetadataEvent | NotebookCellContentChangeEvent | NotebookCellsModelChangedEvent | NotebookCellsModelMoveEvent | NotebookOutputChangedEvent | NotebookCellsChangeLanguageEvent | NotebookCellsChangeMetadataEvent | NotebookDocumentUnknownChangeEvent) & { synchronous: boolean; endSelections?: number[]; transient: boolean }; export const enum CellEditType { Replace = 1, diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts index 11357ebb093..3520b6d5ee1 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts @@ -41,6 +41,8 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM private readonly _name: string; private readonly _workingCopyResource: URI; + private _dirty = false; + constructor( readonly resource: URI, readonly viewType: string, @@ -80,6 +82,13 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM return this._notebook; } + setDirty(newState: boolean) { + if (this._dirty !== newState) { + this._dirty = newState; + this._onDidChangeDirty.fire(); + } + } + async backup(): Promise> { if (this._notebook.supportBackup) { const tokenSource = new CancellationTokenSource(); @@ -116,7 +125,7 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM const newStats = await this._resolveStats(this.resource); this._lastResolvedFileStat = newStats; - this._notebook.setDirty(false); + this.setDirty(false); this._onDidChangeDirty.fire(); } @@ -150,19 +159,19 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM if (e.kind !== NotebookCellsChangeType.Initialize) { this._onDidChangeContent.fire(); } - })); - this._register(this._notebook.onDidChangeDirty(() => { - this._onDidChangeDirty.fire(); + if (!e.transient) { + this.setDirty(true); + } })); if (forceReloadFromDisk) { - this._notebook.setDirty(false); + this.setDirty(false); } if (backupId) { await this._backupFileService.discardBackup(this._workingCopyResource); - this._notebook.setDirty(true); + this.setDirty(true); } return this; @@ -173,7 +182,7 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM } isDirty() { - return this._notebook?.isDirty; + return this._dirty; } isUntitled() { @@ -225,7 +234,7 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM await this._notebookService.save(this.notebook.viewType, this.notebook.uri, tokenSource.token); const newStats = await this._resolveStats(this.resource); this._lastResolvedFileStat = newStats; - this._notebook.setDirty(false); + this.setDirty(false); return true; } @@ -245,7 +254,7 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM await this._notebookService.saveAs(this.notebook.viewType, this.notebook.uri, targetResource, tokenSource.token); const newStats = await this._resolveStats(this.resource); this._lastResolvedFileStat = newStats; - this._notebook.setDirty(false); + this.setDirty(false); return true; } diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorModelResolverService.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorModelResolverService.ts index 1667fe75ca4..d10ed8444c7 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorModelResolverService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorModelResolverService.ts @@ -81,8 +81,8 @@ export class NotebookModelResolverService implements INotebookEditorModelResolve private static _autoReferenceDirtyModel(model: INotebookEditorModel, ref: () => IDisposable) { const references = new DisposableStore(); - const listener = model.notebook.onDidChangeDirty(() => { - if (model.notebook.isDirty) { + const listener = model.onDidChangeDirty(() => { + if (model.isDirty()) { references.add(ref()); } else { references.clear(); From ee5ee9e8aa79e554c717983713ce3242caed815d Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 9 Sep 2020 12:03:46 -0700 Subject: [PATCH 52/71] chore, simply notebook text model event emitter --- .../common/model/notebookTextModel.ts | 140 ++++++------------ 1 file changed, 46 insertions(+), 94 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index 2bdff2d1a74..f8018095526 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -111,19 +111,14 @@ class DelayedEmitter { } - emit(data: { - contentChange: { value: NotebookTextModelChangedEvent } | undefined, - }) { + emit(data: NotebookTextModelChangedEvent) { this._increaseVersion(); - - if (data.contentChange) { - this._onDidChangeContent.fire( - { - ...data.contentChange.value, - versionId: this._textModel.versionId - } - ); - } + this._onDidChangeContent.fire( + { + ...data, + versionId: this._textModel.versionId + } + ); } } @@ -217,11 +212,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel for (let i = 0; i < mainCells.length; i++) { this._mapping.set(mainCells[i].handle, mainCells[i]); const dirtyStateListener = mainCells[i].onDidChangeContent(() => { - this._eventEmitter.emit({ - contentChange: { - value: { kind: NotebookCellsChangeType.ChangeCellContent, versionId: this.versionId, synchronous: true, transient: false } - } - }); + this._eventEmitter.emit({ kind: NotebookCellsChangeType.ChangeCellContent, versionId: this.versionId, synchronous: true, transient: false }); }); this._cellListeners.set(mainCells[i].handle, dirtyStateListener); @@ -312,9 +303,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this._modelService ); const dirtyStateListener = cell.onDidChangeContent(() => { - this._eventEmitter.emit({ - contentChange: { value: { kind: NotebookCellsChangeType.ChangeCellContent, versionId: this.versionId, synchronous: true, transient: false } } - }); + this._eventEmitter.emit({ kind: NotebookCellsChangeType.ChangeCellContent, versionId: this.versionId, synchronous: true, transient: false }); }); this._cellListeners.set(cell.handle, dirtyStateListener); this._mapping.set(cell.handle, cell); @@ -342,15 +331,11 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel // should be deferred this._eventEmitter.emit({ - contentChange: { - value: { - kind: NotebookCellsChangeType.ModelChange, - versionId: this._versionId, - changes: diffs, - synchronous, - transient: false - } - } + kind: NotebookCellsChangeType.ModelChange, + versionId: this._versionId, + changes: diffs, + synchronous, + transient: false }); } @@ -368,27 +353,19 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel }); this._eventEmitter.emit({ - contentChange: { - value: { - kind: NotebookCellsChangeType.Unknown, - transient: false, - synchronous: true, - versionId: this._versionId, - } - } + kind: NotebookCellsChangeType.Unknown, + transient: false, + synchronous: true, + versionId: this._versionId, }); } handleUnknownChange() { this._eventEmitter.emit({ - contentChange: { - value: { - kind: NotebookCellsChangeType.Unknown, - transient: false, - synchronous: true, - versionId: this._versionId, - } - } + kind: NotebookCellsChangeType.Unknown, + transient: false, + synchronous: true, + versionId: this._versionId, }); } @@ -413,18 +390,14 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel updateNotebookMetadata(metadata: NotebookDocumentMetadata) { this.metadata = metadata; - this._eventEmitter.emit({ - contentChange: { value: { kind: NotebookCellsChangeType.ChangeDocumentMetadata, versionId: this.versionId, metadata: this.metadata, synchronous: true, transient: false } } - }); + this._eventEmitter.emit({ kind: NotebookCellsChangeType.ChangeDocumentMetadata, versionId: this.versionId, metadata: this.metadata, synchronous: true, transient: false }); } private _insertNewCell(index: number, cells: NotebookCellTextModel[], synchronous: boolean, endSelections?: number[]): void { for (let i = 0; i < cells.length; i++) { this._mapping.set(cells[i].handle, cells[i]); const dirtyStateListener = cells[i].onDidChangeContent(() => { - this._eventEmitter.emit({ - contentChange: { value: { kind: NotebookCellsChangeType.ChangeCellContent, versionId: this.versionId, synchronous: true, transient: false } } - }); + this._eventEmitter.emit({ kind: NotebookCellsChangeType.ChangeCellContent, versionId: this.versionId, synchronous: true, transient: false }); }); this._cellListeners.set(cells[i].handle, dirtyStateListener); @@ -432,20 +405,16 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this._cells.splice(index, 0, ...cells); this._eventEmitter.emit({ - contentChange: { - value: { - kind: NotebookCellsChangeType.ModelChange, - versionId: this._versionId, changes: - [[ - index, - 0, - cells - ]], - synchronous, - endSelections: endSelections, - transient: false - } - } + kind: NotebookCellsChangeType.ModelChange, + versionId: this._versionId, changes: + [[ + index, + 0, + cells + ]], + synchronous, + endSelections: endSelections, + transient: false }); return; @@ -458,9 +427,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this._cellListeners.delete(cell.handle); } this._cells.splice(index, count); - this._eventEmitter.emit({ - contentChange: { value: { kind: NotebookCellsChangeType.ModelChange, versionId: this._versionId, changes: [[index, count, []]], synchronous, endSelections, transient: false } } - }); + this._eventEmitter.emit({ kind: NotebookCellsChangeType.ModelChange, versionId: this._versionId, changes: [[index, count, []]], synchronous, endSelections, transient: false }); } private _isCellMetadataChanged(a: NotebookCellMetadata, b: NotebookCellMetadata) { @@ -506,11 +473,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel cell.metadata = metadata; } - this._eventEmitter.emit({ - contentChange: { - value: { kind: NotebookCellsChangeType.ChangeCellMetadata, versionId: this._versionId, index: this._cells.indexOf(cell), metadata: cell.metadata, synchronous: true, transient: !triggerDirtyChange } - } - }); + this._eventEmitter.emit({ kind: NotebookCellsChangeType.ChangeCellMetadata, versionId: this._versionId, index: this._cells.indexOf(cell), metadata: cell.metadata, synchronous: true, transient: !triggerDirtyChange }); } private _changeCellLanguage(handle: number, languageId: string) { @@ -518,9 +481,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel if (cell && cell.language !== languageId) { cell.language = languageId; - this._eventEmitter.emit({ - contentChange: { value: { kind: NotebookCellsChangeType.ChangeLanguage, versionId: this._versionId, index: this._cells.indexOf(cell), language: languageId, synchronous: true, transient: false } } - }); + this._eventEmitter.emit({ kind: NotebookCellsChangeType.ChangeLanguage, versionId: this._versionId, index: this._cells.indexOf(cell), language: languageId, synchronous: true, transient: false }); } } @@ -531,19 +492,14 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel cell.spliceNotebookCellOutputs(splices); this._eventEmitter.emit({ - contentChange: { - value: { - kind: NotebookCellsChangeType.Output, - versionId: this.versionId, - index: this._cells.indexOf(cell), - outputs: cell.outputs ?? [], - transient: this.transientOptions.transientOutputs, - synchronous: true - } - } + kind: NotebookCellsChangeType.Output, + versionId: this.versionId, + index: this._cells.indexOf(cell), + outputs: cell.outputs ?? [], + transient: this.transientOptions.transientOutputs, + synchronous: true }); } - } private _assertIndex(index: number) { @@ -589,13 +545,9 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this._assertIndex(index); this._assertIndex(newIdx); - { - const cells = this._cells.splice(index, length); - this._cells.splice(newIdx, 0, ...cells); - this._eventEmitter.emit({ - contentChange: { value: { kind: NotebookCellsChangeType.Move, versionId: this._versionId, index, length, newIdx, cells, synchronous, endSelections, transient: false } } - }); - } + const cells = this._cells.splice(index, length); + this._cells.splice(newIdx, 0, ...cells); + this._eventEmitter.emit({ kind: NotebookCellsChangeType.Move, versionId: this._versionId, index, length, newIdx, cells, synchronous, endSelections, transient: false }); return true; } From 424a2bf17b722c2779bdc1a46a4f30891d3a11cf Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Wed, 9 Sep 2020 12:05:42 -0700 Subject: [PATCH 53/71] refs #106285 --- src/vs/base/browser/ui/menu/menubar.ts | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/vs/base/browser/ui/menu/menubar.ts b/src/vs/base/browser/ui/menu/menubar.ts index 65249946405..3a0c90d47e7 100644 --- a/src/vs/base/browser/ui/menu/menubar.ts +++ b/src/vs/base/browser/ui/menu/menubar.ts @@ -532,25 +532,31 @@ export class MenuBar extends Disposable { // Update the button label to reflect mnemonics if (this.options.enableMnemonics) { - let innerHtml = strings.escape(label); + let cleanLabel = strings.escape(label); // This is global so reset it MENU_ESCAPED_MNEMONIC_REGEX.lastIndex = 0; - let escMatch = MENU_ESCAPED_MNEMONIC_REGEX.exec(innerHtml); + let escMatch = MENU_ESCAPED_MNEMONIC_REGEX.exec(cleanLabel); // We can't use negative lookbehind so we match our negative and skip while (escMatch && escMatch[1]) { - escMatch = MENU_ESCAPED_MNEMONIC_REGEX.exec(innerHtml); + escMatch = MENU_ESCAPED_MNEMONIC_REGEX.exec(cleanLabel); } + const replaceDoubleEscapes = (str: string) => str.replace(/&&/g, '&'); + if (escMatch) { - innerHtml = `${innerHtml.substr(0, escMatch.index)}${innerHtml.substr(escMatch.index + escMatch[0].length)}`; + titleElement.innerText = ''; + titleElement.append( + strings.ltrim(replaceDoubleEscapes(cleanLabel.substr(0, escMatch.index)), ' '), + $('mnemonic', { 'aria-hidden': 'true' }, escMatch[3]), + strings.rtrim(replaceDoubleEscapes(cleanLabel.substr(escMatch.index + escMatch[0].length)), ' ') + ); + } else { + titleElement.innerText = replaceDoubleEscapes(cleanLabel).trim(); } - - innerHtml = innerHtml.replace(/&&/g, '&'); - titleElement.innerHTML = innerHtml; } else { - titleElement.innerHTML = cleanMenuLabel.replace(/&&/g, '&'); + titleElement.innerText = cleanMenuLabel.replace(/&&/g, '&'); } let mnemonicMatches = MENU_MNEMONIC_REGEX.exec(label); From ebff341de3aba9e9c48fbc605bbb69ad75169c28 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Wed, 9 Sep 2020 12:22:34 -0700 Subject: [PATCH 54/71] Add subscribers action --- .github/subscribers.json | 7 +++++++ .github/workflows/author-verified.yml | 2 +- .github/workflows/commands.yml | 2 +- .github/workflows/deep-classifier-monitor.yml | 2 +- .github/workflows/deep-classifier-runner.yml | 2 +- .github/workflows/deep-classifier-scraper.yml | 2 +- .github/workflows/english-please.yml | 2 +- .github/workflows/feature-request.yml | 2 +- .github/workflows/latest-release-monitor.yml | 2 +- .github/workflows/locker.yml | 2 +- .github/workflows/needs-more-info-closer.yml | 2 +- .github/workflows/on-label.yml | 9 ++++++++- .github/workflows/on-open.yml | 2 +- .github/workflows/release-pipeline-labeler.yml | 2 +- .github/workflows/test-plan-item-validator.yml | 2 +- 15 files changed, 28 insertions(+), 14 deletions(-) create mode 100644 .github/subscribers.json diff --git a/.github/subscribers.json b/.github/subscribers.json new file mode 100644 index 00000000000..89dee80d4a9 --- /dev/null +++ b/.github/subscribers.json @@ -0,0 +1,7 @@ +{ + "label-to-subscribe-to": [ + "list of usernames to subscribe", + "such as:", + "JacksonKearl" + ] +} diff --git a/.github/workflows/author-verified.yml b/.github/workflows/author-verified.yml index 167f27a5490..03cd7cedbef 100644 --- a/.github/workflows/author-verified.yml +++ b/.github/workflows/author-verified.yml @@ -17,7 +17,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v34 + ref: v35 path: ./actions - name: Install Actions if: github.event_name != 'issues' || contains(github.event.issue.labels.*.name, 'author-verification-requested') diff --git a/.github/workflows/commands.yml b/.github/workflows/commands.yml index 61c82aa73ca..f61d114b157 100644 --- a/.github/workflows/commands.yml +++ b/.github/workflows/commands.yml @@ -13,7 +13,7 @@ jobs: with: repository: 'microsoft/vscode-github-triage-actions' path: ./actions - ref: v34 + ref: v35 - name: Install Actions run: npm install --production --prefix ./actions - name: Run Commands diff --git a/.github/workflows/deep-classifier-monitor.yml b/.github/workflows/deep-classifier-monitor.yml index 9425ae0f1f2..9d4c54dc6be 100644 --- a/.github/workflows/deep-classifier-monitor.yml +++ b/.github/workflows/deep-classifier-monitor.yml @@ -11,7 +11,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v34 + ref: v35 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions diff --git a/.github/workflows/deep-classifier-runner.yml b/.github/workflows/deep-classifier-runner.yml index 2f598745e49..c3d6b292157 100644 --- a/.github/workflows/deep-classifier-runner.yml +++ b/.github/workflows/deep-classifier-runner.yml @@ -13,7 +13,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v34 + ref: v35 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions diff --git a/.github/workflows/deep-classifier-scraper.yml b/.github/workflows/deep-classifier-scraper.yml index b9cb6ec3cd3..22b23b5d6fe 100644 --- a/.github/workflows/deep-classifier-scraper.yml +++ b/.github/workflows/deep-classifier-scraper.yml @@ -11,7 +11,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v34 + ref: v35 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions diff --git a/.github/workflows/english-please.yml b/.github/workflows/english-please.yml index fdfd548291d..6cf4d65455c 100644 --- a/.github/workflows/english-please.yml +++ b/.github/workflows/english-please.yml @@ -13,7 +13,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v34 + ref: v35 path: ./actions - name: Install Actions if: contains(github.event.issue.labels.*.name, '*english-please') diff --git a/.github/workflows/feature-request.yml b/.github/workflows/feature-request.yml index 12af1423506..6cdf0bd6fc3 100644 --- a/.github/workflows/feature-request.yml +++ b/.github/workflows/feature-request.yml @@ -18,7 +18,7 @@ jobs: with: repository: 'microsoft/vscode-github-triage-actions' path: ./actions - ref: v34 + ref: v35 - name: Install Actions if: github.event_name != 'issues' || contains(github.event.issue.labels.*.name, 'feature-request') run: npm install --production --prefix ./actions diff --git a/.github/workflows/latest-release-monitor.yml b/.github/workflows/latest-release-monitor.yml index a5777e3a7f1..fb0a8320088 100644 --- a/.github/workflows/latest-release-monitor.yml +++ b/.github/workflows/latest-release-monitor.yml @@ -14,7 +14,7 @@ jobs: with: repository: 'microsoft/vscode-github-triage-actions' path: ./actions - ref: v34 + ref: v35 - name: Install Actions run: npm install --production --prefix ./actions - name: Install Storage Module diff --git a/.github/workflows/locker.yml b/.github/workflows/locker.yml index 64ac30d3717..2d21b96b833 100644 --- a/.github/workflows/locker.yml +++ b/.github/workflows/locker.yml @@ -14,7 +14,7 @@ jobs: with: repository: 'microsoft/vscode-github-triage-actions' path: ./actions - ref: v34 + ref: v35 - name: Install Actions run: npm install --production --prefix ./actions - name: Run Locker diff --git a/.github/workflows/needs-more-info-closer.yml b/.github/workflows/needs-more-info-closer.yml index 018caf24fe0..dae985ca35f 100644 --- a/.github/workflows/needs-more-info-closer.yml +++ b/.github/workflows/needs-more-info-closer.yml @@ -14,7 +14,7 @@ jobs: with: repository: 'microsoft/vscode-github-triage-actions' path: ./actions - ref: v34 + ref: v35 - name: Install Actions run: npm install --production --prefix ./actions - name: Run Needs More Info Closer diff --git a/.github/workflows/on-label.yml b/.github/workflows/on-label.yml index 446c041ebd3..e51b9ac740f 100644 --- a/.github/workflows/on-label.yml +++ b/.github/workflows/on-label.yml @@ -11,7 +11,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v34 + ref: v35 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions @@ -40,6 +40,13 @@ jobs: token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} config-path: commands + # only here. + - name: Run Subscribers + uses: ./actions/topic-subscribe + with: + appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} + token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} + config-path: subscribers # source of truth in ./feature-request.yml - name: Run Feature Request Manager diff --git a/.github/workflows/on-open.yml b/.github/workflows/on-open.yml index e4f0434dbdc..e985909b446 100644 --- a/.github/workflows/on-open.yml +++ b/.github/workflows/on-open.yml @@ -11,7 +11,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v34 + ref: v35 path: ./actions - name: Install Actions run: npm install --production --prefix ./actions diff --git a/.github/workflows/release-pipeline-labeler.yml b/.github/workflows/release-pipeline-labeler.yml index 14f74f581dd..a52b895e51c 100644 --- a/.github/workflows/release-pipeline-labeler.yml +++ b/.github/workflows/release-pipeline-labeler.yml @@ -13,7 +13,7 @@ jobs: uses: actions/checkout@v2 with: repository: 'microsoft/vscode-github-triage-actions' - ref: v34 + ref: v35 path: ./actions - name: Checkout Repo if: github.event_name != 'issues' diff --git a/.github/workflows/test-plan-item-validator.yml b/.github/workflows/test-plan-item-validator.yml index 4365afa03e5..7a173b0e2f3 100644 --- a/.github/workflows/test-plan-item-validator.yml +++ b/.github/workflows/test-plan-item-validator.yml @@ -14,7 +14,7 @@ jobs: with: repository: 'microsoft/vscode-github-triage-actions' path: ./actions - ref: v34 + ref: v35 - name: Install Actions if: contains(github.event.issue.labels.*.name, 'testplan-item') || contains(github.event.issue.labels.*.name, 'invalid-testplan-item') run: npm install --production --prefix ./actions From d55efd8041ac8048b6fba6f7e769c1b8f1ebd836 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 9 Sep 2020 12:40:58 -0700 Subject: [PATCH 55/71] Fix terminal ts 4.1 compile issues Part of #106358 --- src/vs/workbench/api/common/extHostTerminalService.ts | 6 +++--- .../contrib/terminal/browser/terminalProcessExtHostProxy.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index 7107003f8f7..01c4ece2795 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -120,7 +120,7 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi ) { super(proxy, id); this._creationOptions = Object.freeze(this._creationOptions); - this._pidPromise = new Promise(c => this._pidPromiseComplete = c); + this._pidPromise = new Promise(c => this._pidPromiseComplete = c); } public async create( @@ -316,7 +316,7 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ protected _terminalProcesses: { [id: number]: ITerminalChildProcess } = {}; protected _terminalProcessDisposables: { [id: number]: IDisposable } = {}; protected _extensionTerminalAwaitingStart: { [id: number]: { initialDimensions: ITerminalDimensionsDto | undefined } | undefined } = {}; - protected _getTerminalPromises: { [id: number]: Promise } = {}; + protected _getTerminalPromises: { [id: number]: Promise } = {}; protected _environmentVariableCollections: Map = new Map(); private readonly _bufferer: TerminalDataBufferer; @@ -670,7 +670,7 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ return this._getTerminalPromises[id]; } - private _createGetTerminalPromise(id: number, retries: number = 5): Promise { + private _createGetTerminalPromise(id: number, retries: number = 5): Promise { return new Promise(c => { if (retries === 0) { c(undefined); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy.ts index 03793e6a0e1..84f18d3c998 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy.ts @@ -43,9 +43,9 @@ export class TerminalProcessExtHostProxy extends Disposable implements ITerminal private readonly _onRequestLatency = this._register(new Emitter()); public readonly onRequestLatency: Event = this._onRequestLatency.event; - private _pendingInitialCwdRequests: ((value: string | Thenable) => void)[] = []; - private _pendingCwdRequests: ((value: string | Thenable) => void)[] = []; - private _pendingLatencyRequests: ((value: number | Thenable) => void)[] = []; + private _pendingInitialCwdRequests: ((value: string | PromiseLike) => void)[] = []; + private _pendingCwdRequests: ((value: string | PromiseLike) => void)[] = []; + private _pendingLatencyRequests: ((value: number | PromiseLike) => void)[] = []; constructor( public terminalId: number, From 6821292b4be0c63f1ba03f7edee05572de22a05d Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 9 Sep 2020 12:42:12 -0700 Subject: [PATCH 56/71] Fix ts 4.1 issues in terminal api tests --- .../vscode-api-tests/src/singlefolder-tests/terminal.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts index 8e2afa3ba13..01232a99c8c 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts @@ -349,7 +349,7 @@ import { doesNotThrow, equal, ok, deepEqual, throws } from 'assert'; term1Write.fire('write1'); // Wait until the data is written - await new Promise(resolve => { resolveOnceDataWritten = resolve; }); + await new Promise(resolve => { resolveOnceDataWritten = resolve; }); term1Close.fire(); From a037570775ff264fba26289e11d5c1683c0c5937 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Wed, 9 Sep 2020 13:54:51 -0700 Subject: [PATCH 57/71] Update Codicons: add 'graph-left' https://github.com/microsoft/vscode-codicons/commit/dd1edb245d2217efcfb931c293b77b78e634e59a --- .../browser/ui/codicons/codicon/codicon.ttf | Bin 61120 -> 61312 bytes src/vs/base/common/codicons.ts | 1 + 2 files changed, 1 insertion(+) diff --git a/src/vs/base/browser/ui/codicons/codicon/codicon.ttf b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf index 4a724c9d7d3c7dbd4440808379b2ddffb1e9102a..cb5265534337214cb6200dc7206ed83e3efb8834 100644 GIT binary patch delta 3655 zcmZA3dt8MZqY)I zE9c5gt(B=ODc4r1yOp)(oS8b)vaOXpzoO~P%6aMY^ZUp4ny=UU`99Bqb38oH_xt(% z4xe+c`q16s^{oJ;9YEEB=K7Y4pN@SD2;yaU)8eL;i#A3M+zUiq0}j~ zKRi!ajeH^KRJTN)zeYnFn_E|W+rl%*$3VBJrX>sNFGUYI0eG4KS4VUGiWb*=yuqLM z=P$+FQ{UV$_&Lv*)xg?$3DL9WMJS|M5Li?jI;G%HGanlGJUJE5Ls(Fg(5ctxts8 z1^2D9B-S24Kz9VVPV#-Bw>~4#+2FK0Z#r+8$!4#=I4HGYCqBi;Scg+G6h}nxigZJ! z6ygeQ;5zO`ERLZbkKva{7B8w0Dg{`M1^5WR$LlyK$=HN8{7B;Db~MOfxlQ^@Kj|Tn z5+eho4<3?U674@7^setGcmx~a$7cK#k75gchOPKH9>){dh9|KdJFp8+<5@h9J=lwV zcmXeBKRWReeuZD-H+YrL;}8zxxA-0Y$ou^k-oc-76o0{AaUAdB1m43*{0(RDKF;DC zKEQ{3qJPIf@K1b#3qE`X2L_+x3v}UMxQI*mH!kBVe2c6253b>+KwRRM011>J=_$by zBB2r{y(L`wN`!bMN(Ra>iIsRskl~UjNisr4N{XaPnxspnWJ$K<$SBE`Jjs_L86(9~ zBBfF$V=?O)`~uHmH;OO@6_|v{D3w8&h)RsdXhgz;m$4X4=#UIP!ColCQ}_eElp%N< z8}JRj#+%X|f5K_x%4nqE02=WyyCJYsdbq@gVA~$O*kbmdlmkwkVkf|%`<=}{%ooohnA4&ZRX1SpOf z4DY!W1VRrnka&)^Nq?hv}o312BCR69I;|%wjgcL@1^N3~#H&yzs&B z{#lF;7>{Cz!0`TAj1!nB#bAMnR*V>!fr?=RGe|LZU}6*l2!_wrVids)Q4A#*uf=v^ zKEZH8EW2`;VTzdr6RU8Tm5pRE$6(?W6AdOoG238KVagO!A7-p#{=<|jt^_dS6xRfp@rtVh%ml@C0%oG(iUCuhxOTuyQn<|LZzd}) zC@__ZOAE|Y#YF~Yn&NT;GhK1vftjIjlXa#N{ynBjiHr3PC2rPgB>}9n$j5^O@?^G> zAl5lbda~9i31*$EB!snANhs?)C1I>}N_w-_D+yE!$OO)6NXi;J(;Co6^ zS(hqFV_l{sowZeo?fT_PY}enb#CH9CN^IA!P-2^YrG5S@`PbX7zh8;%^aqsKPXE3V z+tCjyu^ru}#CEj(91=FOS*64_^lBxxq1PyGDPVq}FzgwgtX14*z^qf;aKNlr*zMoc zf4U6vf5PL-{@h4!NC$tiMM;MLuE>$Tr+D>*;^qTpo5Gi@PbzLmV74o6ON?=EW|I-5u8_t}1Rr+{yU7_@(i0C&VV)pKv7M+VB~P{Sqq@4<;og zwIm%+?w4GYyf69fWM{9Vu_5TuSYqTAX@ss*@IwR+rY1b}~IG zy*7PI`q2zzWM#Bv?90r{+?=%~>vVRX?6uilIn#1B=bRXoH>%P%YW=8Vxwq#g<<{h` z8XYit+~~7;cHFf#d7O@0bueVfKVG6K+h5 zsYt76sc5fwWs+x7!KBtn2PgYlCvTg4eoFk5$|-B7?3vFct$MubXq9tE#T|R9=U4BlK2Uw6`b_obv#Mro zn{{RO@Y$>8be}VOPRE=RH5oOlYL3@@GdF5(&D@UK-nAL?g6GxE+lK()=1unn_l3?Y zQ6&?78*m%K&==7dj0C$W3y%nj%gl}l8r)aHveP5Ng1p&b-mvWdIS&tuNY4(7^G5LX zwK;K(jd7W7@Ab+-55~kiSX=Pw|5aC4c3fjzV{As@^=4O2boBKjQ8_N|AM51xsHhte zB~dxD?#2VM~hu&f8;~ zV@72+FU;v+)tWhL&dRxR<;SpL}EX8@lbV9=tKbsJV>9LbvijOYeVo~W&oy6TYAF9BCOfdK)#PlRO^LhJ_uY3pOwF7BUk2mYR2 z_HSHnI{x+lYwtInUwGc&aUXctpYn^pBZaO}a$MRw&P%NK9^XMDeZKoNc!J;db7NWk z052bSd0cQge1}N8tI#*0<^Vp!-|;9eN*Lbc8($VLOpwXAga4ujs}O;AQH`JCDB@%c zDiA1{SdV4AE^qK!ye6^OjVAn9BBVcRBvksyP#Ge9WtiMAAu?>pe)n9Ve*Vq?+Y0?F?bh}K;9jC^6S%7t4zMm&yz_7`Q|tk_mn(Jy+%*apSyw1_ z2;3_b-el$9w`_a4A1JdM?!CZL-O^Qtn z_hyCNt`h-a&IlfDRcvgyA5*ZGrA0|7>oz5QSRYq1ly$q3A*?%<^kvEeGZ=5hfrIf;96cCc#UX^@ zIa?e@7@nKO!GvK{0L4j% z8K^k(F#I1aPCv|G#TVxqxBD`p!^jAF{cj8n`zm{`T+gNakjKp1B{H^exE8Lt?OFb>6t zgh@~gOPEB(*n~+^3{aS4#VCcDpctw!DT?t5Gf^>UVN#WonG2Jqn7%OSia883NimUO zG8D5JCQ~t`VJ0i)HB6Rba>HaRW;jfaVw%I`DtykGXYYR=|NAZ;ok06*i3caiqN`!T;5)al&C7!JF$jOa(b7#I1AJzp*{8+1$__Hol z62Q7hNk7)bN`hFIC>g+7tz;nUQYC{~mnq?RI?Zw(kOZ^VC>hSWLWv!>l}f@{Yn6;; zeL#twj0crOv93}Q&041u+< zX2hinj6E~rA_nF~#pMi)JxhWOjYEn{9GJt3iyfFFipw6Dc4Zep_N${IBv_o?5rZS< zN1TrIiA;^$8rdB+CTe5U#psym#_0AKub4A2J>yo7yAm53yE*nu?3Zzi;x@*$#uvx0 zh~E)^W_;rK>yB*4LyiNEw;Z<;A`)5?IunBuvlF)_o=XZ$DoScfI+pZVa(wcleyv=nDrbK0r2E9n90j`Ys-dy}?Lx|gvu<9ud*=9bL% z$pMq&Cud{@Wo^&u%+AQ(mE)JwnDbt4eD2cRwmgr#y1Wbdf%&QV^YdHty9x#t6c@A< z+$u~dd}K=Cl%^>?MMXvLObwbkYwF2jzv6AhM~lx-yH*lfl2KAya<=5=^ziAcOT$Z@ zrKP({FU`P=_qGxVZ<-p4F$`zH3mD?)!&-0mAI`4&fAI^8oe`SH+g6sub7F@4#RBf(0 zSaoh;z{2c>Z!EmN=)~fn#S9O40lmGRU7lSXJ|pv{b=;`kzg`MEwtlqTqhs)=U-@(d Ib=CWR3*W4vX8-^I diff --git a/src/vs/base/common/codicons.ts b/src/vs/base/common/codicons.ts index d4d83329f9f..926bfa41a35 100644 --- a/src/vs/base/common/codicons.ts +++ b/src/vs/base/common/codicons.ts @@ -478,6 +478,7 @@ export namespace Codicon { export const cloud = new Codicon('cloud', { character: '\\ebaa' }); export const merge = new Codicon('merge', { character: '\\ebab' }); export const exportIcon = new Codicon('export', { character: '\\ebac' }); + export const graphLeft = new Codicon('graph-left', { character: '\\ebad' }); } From 69f26324d46dfabe3574b3e33c411536e2ead4d7 Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 9 Sep 2020 12:27:25 -0700 Subject: [PATCH 58/71] initialize notebook text model data only through ctor. --- .../api/browser/mainThreadNotebook.ts | 27 ++------ .../notebook/browser/notebookServiceImpl.ts | 15 ++++- .../common/model/notebookTextModel.ts | 65 ++++++++++--------- .../notebook/common/notebookService.ts | 4 +- .../notebook/test/testNotebookEditor.ts | 7 +- 5 files changed, 59 insertions(+), 59 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index 7d22de8bc10..00b9d20b32f 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -458,27 +458,12 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo }); }); }, - createNotebook: async (textModel: NotebookTextModel, backupId?: string) => { - // open notebook document - const data = await this._proxy.$resolveNotebookData(textModel.viewType, textModel.uri, backupId); - textModel.updateLanguages(data.languages); - textModel.metadata = data.metadata; - textModel.transientOptions = options; - - if (data.cells.length) { - textModel.initialize(data!.cells); - } else if (textModel.uri.scheme === Schemas.untitled) { - textModel.initialize([{ - cellKind: CellKind.Code, - language: textModel.resolvedLanguages.length ? textModel.resolvedLanguages[0] : '', - outputs: [], - metadata: undefined, - source: '' - }]); - } - - this._proxy.$acceptDocumentPropertiesChanged(textModel.uri, { metadata: textModel.metadata }); - return; + resolveNotebookDocument: async (viewType: string, uri: URI, backupId?: string) => { + const data = await this._proxy.$resolveNotebookData(viewType, uri, backupId); + return { + data, + transientOptions: options + }; }, resolveNotebookEditor: async (viewType: string, uri: URI, editorId: string) => { await this._proxy.$resolveNotebookEditor(viewType, uri, editorId); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index d05617cf39a..92da7336506 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -9,6 +9,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Iterable } from 'vs/base/common/iterator'; import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { ResourceMap } from 'vs/base/common/map'; +import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; import { RedoCommand, UndoCommand } from 'vs/editor/browser/editorExtensions'; import { CopyAction, CutAction, PasteAction } from 'vs/editor/contrib/clipboard/clipboard'; @@ -26,7 +27,7 @@ import { NotebookKernelProviderAssociationRegistry, NotebookViewTypesExtensionRe import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, BUILTIN_RENDERER_ID, CellEditType, CellOutputKind, CellUri, DisplayOrderKey, ICellEditOperation, IDisplayOutput, INotebookKernelInfo2, INotebookKernelProvider, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, ITransformedDisplayOutputDto, mimeTypeSupportedByCore, NotebookCellOutputsSplice, notebookDocumentFilterMatch, NotebookEditorPriority, NOTEBOOK_DISPLAY_ORDER, sortMimeTypes } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, BUILTIN_RENDERER_ID, CellEditType, CellKind, CellOutputKind, CellUri, DisplayOrderKey, ICellEditOperation, IDisplayOutput, INotebookKernelInfo2, INotebookKernelProvider, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, ITransformedDisplayOutputDto, mimeTypeSupportedByCore, NotebookCellOutputsSplice, notebookDocumentFilterMatch, NotebookEditorPriority, NOTEBOOK_DISPLAY_ORDER, sortMimeTypes } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookOutputRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookOutputRenderer'; import { NotebookEditorDescriptor, NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider'; import { IMainNotebookController, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; @@ -622,8 +623,16 @@ export class NotebookService extends Disposable implements INotebookService, ICu return notebookModel; } else { - notebookModel = this._instantiationService.createInstance(NotebookTextModel, viewType, provider.controller.supportBackup, uri); - await provider.controller.createNotebook(notebookModel, backupId); + const dataDto = await provider.controller.resolveNotebookDocument(viewType, uri, backupId); + let cells = dataDto.data.cells.length ? dataDto.data.cells : (uri.scheme === Schemas.untitled ? [{ + cellKind: CellKind.Code, + language: dataDto.data.languages.length ? dataDto.data.languages[0] : '', + outputs: [], + metadata: undefined, + source: '' + }] : []); + + notebookModel = this._instantiationService.createInstance(NotebookTextModel, viewType, provider.controller.supportBackup, uri, cells, dataDto.data.languages, dataDto.data.metadata, dataDto.transientOptions); } // new notebook model created diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index f8018095526..6d9d8078e6e 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -153,16 +153,31 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel private _operationManager: NotebookOperationManager; private _eventEmitter: DelayedEmitter; + get cells(): readonly NotebookCellTextModel[] { + return this._cells; + } + + get versionId() { + return this._versionId; + } + constructor( readonly viewType: string, readonly supportBackup: boolean, readonly uri: URI, + cells: ICellDto2[], + languages: string[], + metadata: NotebookDocumentMetadata, + options: TransientOptions, @IUndoRedoService private _undoService: IUndoRedoService, @ITextModelService private _modelService: ITextModelService, @IModeService private readonly _modeService: IModeService, ) { super(); - this._cells = []; + this.transientOptions = options; + this.metadata = metadata; + this.updateLanguages(languages); + this._initialize(cells); this._operationManager = new NotebookOperationManager(this._undoService, uri); this._eventEmitter = new DelayedEmitter( @@ -172,34 +187,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel ); } - dispose() { - this._onWillDispose.fire(); - dispose(this._cellListeners.values()); - dispose(this._cells); - super.dispose(); - } - - get cells(): readonly NotebookCellTextModel[] { - return this._cells; - } - - get versionId() { - return this._versionId; - } - - createCellTextModel( - source: string, - language: string, - cellKind: CellKind, - outputs: IProcessedOutput[], - metadata: NotebookCellMetadata | undefined - ) { - const cellHandle = this._cellhandlePool++; - const cellUri = CellUri.generate(this.uri, cellHandle); - return new NotebookCellTextModel(cellUri, cellHandle, source, language, cellKind, outputs || [], metadata || {}, this.transientOptions, this._modelService); - } - - initialize(cells: ICellDto2[]) { + private _initialize(cells: ICellDto2[]) { this._cells = []; this._versionId = 0; @@ -222,6 +210,25 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this._increaseVersionId(); } + + dispose() { + this._onWillDispose.fire(); + dispose(this._cellListeners.values()); + dispose(this._cells); + super.dispose(); + } + createCellTextModel( + source: string, + language: string, + cellKind: CellKind, + outputs: IProcessedOutput[], + metadata: NotebookCellMetadata | undefined + ) { + const cellHandle = this._cellhandlePool++; + const cellUri = CellUri.generate(this.uri, cellHandle); + return new NotebookCellTextModel(cellUri, cellHandle, source, language, cellKind, outputs || [], metadata || {}, this.transientOptions, this._modelService); + } + pushStackElement(label: string) { this._operationManager.pushStackElement(label); } diff --git a/src/vs/workbench/contrib/notebook/common/notebookService.ts b/src/vs/workbench/contrib/notebook/common/notebookService.ts index 9b1704f0135..50de255b4e6 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookService.ts @@ -10,7 +10,7 @@ import { NotebookExtensionDescription } from 'vs/workbench/api/common/extHost.pr import { Event } from 'vs/base/common/event'; import { INotebookTextModel, INotebookRendererInfo, - IEditor, ICellEditOperation, NotebookCellOutputsSplice, INotebookKernelProvider, INotebookKernelInfo2, TransientMetadata + IEditor, ICellEditOperation, NotebookCellOutputsSplice, INotebookKernelProvider, INotebookKernelInfo2, TransientMetadata, NotebookDataDto, TransientOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -24,7 +24,7 @@ export const INotebookService = createDecorator('notebookServi export interface IMainNotebookController { supportBackup: boolean; options: { transientOutputs: boolean; transientMetadata: TransientMetadata; }; - createNotebook(textModel: NotebookTextModel, editorId?: string, backupId?: string): Promise; + resolveNotebookDocument(viewType: string, uri: URI, backupId?: string): Promise<{ data: NotebookDataDto, transientOptions: TransientOptions }>; reloadNotebook(mainthreadTextModel: NotebookTextModel): Promise; resolveNotebookEditor(viewType: string, uri: URI, editorId: string): Promise; onDidReceiveMessage(editorId: string, rendererType: string | undefined, message: any): void; diff --git a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts index 31eab292f31..d2386aab84c 100644 --- a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts @@ -18,7 +18,7 @@ import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/v import { CellViewModel, IModelDecorationsChangeAccessor, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { CellKind, CellUri, INotebookEditorModel, IProcessedOutput, NotebookCellMetadata, IInsetRenderOutput, ICellRange, INotebookKernelInfo2 } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, CellUri, INotebookEditorModel, IProcessedOutput, NotebookCellMetadata, IInsetRenderOutput, ICellRange, INotebookKernelInfo2, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { Webview } from 'vs/workbench/contrib/webview/browser/webview'; import { ICompositeCodeEditor, IEditor } from 'vs/editor/common/editorCommon'; import { NotImplementedError } from 'vs/base/common/errors'; @@ -397,8 +397,7 @@ export function withTestNotebook(instantiationService: TestInstantiationService, const viewType = 'notebook'; const editor = new TestNotebookEditor(); - const notebook = new NotebookTextModel(viewType, false, URI.parse('test'), undoRedoService, textModelService, modeService); - notebook.initialize(cells.map(cell => { + const notebook = new NotebookTextModel(viewType, false, URI.parse('test'), cells.map(cell => { return { source: cell[0], language: cell[1], @@ -406,7 +405,7 @@ export function withTestNotebook(instantiationService: TestInstantiationService, outputs: cell[3], metadata: cell[4] }; - })); + }), [], notebookDocumentMetadataDefaults, { transientMetadata: {}, transientOutputs: false }, undoRedoService, textModelService, modeService); const model = new NotebookEditorTestModel(notebook); const eventDispatcher = new NotebookEventDispatcher(); const viewModel = new NotebookViewModel(viewType, model.notebook, eventDispatcher, null, instantiationService, blukEditService, undoRedoService); From 889fe1787b6dc58da5755a943f9f547e6e62225e Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 9 Sep 2020 14:40:31 -0700 Subject: [PATCH 59/71] :lipstick: --- src/vs/workbench/api/browser/mainThreadNotebook.ts | 2 +- .../contrib/notebook/common/model/notebookTextModel.ts | 2 +- .../workbench/contrib/notebook/test/notebookViewModel.test.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index 00b9d20b32f..ae0e9662b64 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -18,7 +18,7 @@ import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookB import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService'; -import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellEditType, CellKind, DisplayOrderKey, ICellEditOperation, ICellRange, IEditor, IMainCellDto, INotebookDocumentFilter, NotebookCellMetadata, NotebookCellOutputsSplice, NotebookCellsChangeType, NotebookDocumentMetadata, NOTEBOOK_DISPLAY_ORDER, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellEditType, DisplayOrderKey, ICellEditOperation, ICellRange, IEditor, IMainCellDto, INotebookDocumentFilter, NotebookCellMetadata, NotebookCellOutputsSplice, NotebookCellsChangeType, NotebookDocumentMetadata, NOTEBOOK_DISPLAY_ORDER, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { IMainNotebookController, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index 6d9d8078e6e..a265a6d36aa 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -131,7 +131,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel private _cellhandlePool: number = 0; private _mapping: Map = new Map(); private _cellListeners: Map = new Map(); - private _cells: NotebookCellTextModel[]; + private _cells: NotebookCellTextModel[] = []; private _languages: string[] = []; private _allLanguages: boolean = false; diff --git a/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts b/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts index 92eae8a9b4a..a8d48a6870b 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; import { NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; -import { CellKind, NotebookCellMetadata, diff, ICellRange } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, NotebookCellMetadata, diff, ICellRange, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { withTestNotebook, TestCell, NotebookEditorTestModel, setupInstantiationService } from 'vs/workbench/contrib/notebook/test/testNotebookEditor'; import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; @@ -25,7 +25,7 @@ suite('NotebookViewModel', () => { const modeService = instantiationService.get(IModeService); test('ctor', function () { - const notebook = new NotebookTextModel('notebook', false, URI.parse('test'), undoRedoService, textModelService, modeService); + const notebook = new NotebookTextModel('notebook', false, URI.parse('test'), [], [], notebookDocumentMetadataDefaults, { transientMetadata: {}, transientOutputs: false }, undoRedoService, textModelService, modeService); const model = new NotebookEditorTestModel(notebook); const eventDispatcher = new NotebookEventDispatcher(); const viewModel = new NotebookViewModel('notebook', model.notebook, eventDispatcher, null, instantiationService, blukEditService, undoRedoService); From 6cf52e3828e6fdab666b0206cea68addcee0c514 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 9 Sep 2020 14:00:03 -0700 Subject: [PATCH 60/71] Mark property readonly --- src/vs/editor/common/modes/supports/tokenization.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/common/modes/supports/tokenization.ts b/src/vs/editor/common/modes/supports/tokenization.ts index 12566c3b00e..e28c23d39b4 100644 --- a/src/vs/editor/common/modes/supports/tokenization.ts +++ b/src/vs/editor/common/modes/supports/tokenization.ts @@ -395,7 +395,7 @@ export class ThemeTrieElement { } } -export function generateTokensCSSForColorMap(colorMap: Color[]): string { +export function generateTokensCSSForColorMap(colorMap: readonly Color[]): string { let rules: string[] = []; for (let i = 1, len = colorMap.length; i < len; i++) { let color = colorMap[i]; From 80b1a775d691612ce8334238d786a922d55e3544 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 9 Sep 2020 14:44:37 -0700 Subject: [PATCH 61/71] Enable webview commands for webview views Fixes #105670 Previously our webview commands assumed that webviews were always going to be in an editor. This is no longer true with webview views. To fix this, I've added an `activeWebview` to the `IWebviewService`. This tracks the currently focused webview. --- .../api/browser/mainThreadCustomEditors.ts | 2 +- .../api/browser/mainThreadWebviewPanels.ts | 2 +- .../api/browser/mainThreadWebviews.ts | 2 +- .../webview/browser/baseWebviewElement.ts | 7 +++- .../browser/dynamicWebviewEditorOverlay.ts | 6 +-- .../webview/browser/webview.contribution.ts | 33 ++++------------ .../contrib/webview/browser/webview.ts | 6 ++- .../contrib/webview/browser/webviewService.ts | 38 ++++++++++++++----- .../electron-browser/webviewService.ts | 27 ++++++------- .../sandbox.simpleservices.ts | 2 + 10 files changed, 66 insertions(+), 59 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadCustomEditors.ts b/src/vs/workbench/api/browser/mainThreadCustomEditors.ts index 9351d89b4bd..132d36358cd 100644 --- a/src/vs/workbench/api/browser/mainThreadCustomEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadCustomEditors.ts @@ -153,7 +153,7 @@ export class MainThreadCustomEditors extends Disposable implements extHostProtoc return; } - webviewInput.webview.onDispose(() => { + webviewInput.webview.onDidDispose(() => { // If the model is still dirty, make sure we have time to save it if (modelRef.object.isDirty()) { const sub = modelRef.object.onDidChangeDirty(() => { diff --git a/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts b/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts index 84107c980a3..f8541ed9a80 100644 --- a/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts +++ b/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts @@ -140,7 +140,7 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc this._webviewInputs.add(handle, input); this._mainThreadWebviews.addWebview(handle, input.webview); - input.webview.onDispose(() => { + input.webview.onDidDispose(() => { this._proxy.$onDidDisposeWebviewPanel(handle).finally(() => { this._webviewInputs.delete(handle); }); diff --git a/src/vs/workbench/api/browser/mainThreadWebviews.ts b/src/vs/workbench/api/browser/mainThreadWebviews.ts index 8180b9972ee..904279d527a 100644 --- a/src/vs/workbench/api/browser/mainThreadWebviews.ts +++ b/src/vs/workbench/api/browser/mainThreadWebviews.ts @@ -69,7 +69,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma disposables.add(webview.onMessage((message: any) => { this._proxy.$onMessage(handle, message); })); disposables.add(webview.onMissingCsp((extension: ExtensionIdentifier) => this._proxy.$onMissingCsp(handle, extension.value))); - disposables.add(webview.onDispose(() => { + disposables.add(webview.onDidDispose(() => { disposables.dispose(); this._webviews.delete(handle); })); diff --git a/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts b/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts index 4423555621a..04ebf3bfae0 100644 --- a/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts +++ b/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts @@ -171,8 +171,10 @@ export abstract class BaseWebview extends Disposable { if (this.element) { this.element.remove(); } - this._element = undefined; + + this._onDidDispose.fire(); + super.dispose(); } @@ -203,6 +205,9 @@ export abstract class BaseWebview extends Disposable { private readonly _onDidBlur = this._register(new Emitter()); public readonly onDidBlur = this._onDidBlur.event; + private readonly _onDidDispose = this._register(new Emitter()); + public readonly onDidDispose = this._onDidDispose.event; + public postMessage(data: any): void { this._send('message', data); } diff --git a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts index 7d2046676ff..72c446f7004 100644 --- a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts +++ b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts @@ -59,12 +59,12 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewOv return !!this._webview.value?.isFocused; } - private readonly _onDispose = this._register(new Emitter()); - public onDispose = this._onDispose.event; + private readonly _onDidDispose = this._register(new Emitter()); + public onDidDispose = this._onDidDispose.event; dispose() { this.container.remove(); - this._onDispose.fire(); + this._onDidDispose.fire(); super.dispose(); } diff --git a/src/vs/workbench/contrib/webview/browser/webview.contribution.ts b/src/vs/workbench/contrib/webview/browser/webview.contribution.ts index 25d4ea314fd..9144fa8eea0 100644 --- a/src/vs/workbench/contrib/webview/browser/webview.contribution.ts +++ b/src/vs/workbench/contrib/webview/browser/webview.contribution.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { MultiCommand, RedoCommand, SelectAllCommand, ServicesAccessor, UndoCommand } from 'vs/editor/browser/editorExtensions'; +import { MultiCommand, RedoCommand, SelectAllCommand, UndoCommand } from 'vs/editor/browser/editorExtensions'; import { CopyAction, CutAction, PasteAction } from 'vs/editor/contrib/clipboard/clipboard'; import { localize } from 'vs/nls'; import { registerAction2 } from 'vs/platform/actions/common/actions'; @@ -12,11 +12,12 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { Registry } from 'vs/platform/registry/common/platform'; import { EditorDescriptor, Extensions as EditorExtensions, IEditorRegistry } from 'vs/workbench/browser/editor'; import { Extensions as EditorInputExtensions, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; -import { Webview, WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview'; +import { IWebviewService, Webview } from 'vs/workbench/contrib/webview/browser/webview'; import { WebviewEditorInputFactory } from 'vs/workbench/contrib/webview/browser/webviewEditorInputFactory'; -import { getActiveWebviewEditor, HideWebViewEditorFindCommand, ReloadWebviewAction, ShowWebViewEditorFindWidgetAction, WebViewEditorFindNextCommand, WebViewEditorFindPreviousCommand } from '../browser/webviewCommands'; +import { HideWebViewEditorFindCommand, ReloadWebviewAction, ShowWebViewEditorFindWidgetAction, WebViewEditorFindNextCommand, WebViewEditorFindPreviousCommand } from '../browser/webviewCommands'; import { WebviewEditor } from './webviewEditor'; import { WebviewInput } from './webviewEditorInput'; +import { WebviewService } from './webviewService'; import { IWebviewWorkbenchService, WebviewEditorService } from './webviewWorkbenchService'; (Registry.as(EditorExtensions.Editors)).registerEditor(EditorDescriptor.create( @@ -29,6 +30,7 @@ Registry.as(EditorInputExtensions.EditorInputFactor WebviewEditorInputFactory.ID, WebviewEditorInputFactory); +registerSingleton(IWebviewService, WebviewService, true); registerSingleton(IWebviewWorkbenchService, WebviewEditorService, true); registerAction2(ShowWebViewEditorFindWidgetAction); @@ -38,32 +40,13 @@ registerAction2(WebViewEditorFindPreviousCommand); registerAction2(ReloadWebviewAction); -function getInnerActiveWebview(accessor: ServicesAccessor): Webview | undefined { - const webview = getActiveWebviewEditor(accessor); - if (!webview) { - return undefined; - } - - // Make sure we are really focused on the webview - if (!['WEBVIEW', 'IFRAME'].includes(document.activeElement?.tagName ?? '')) { - return undefined; - } - - if ('getInnerWebview' in (webview as WebviewOverlay)) { - const innerWebview = (webview as WebviewOverlay).getInnerWebview(); - return innerWebview; - } - - return webview; -} - - const PRIORITY = 100; function overrideCommandForWebview(command: MultiCommand | undefined, f: (webview: Webview) => void) { command?.addImplementation(PRIORITY, accessor => { - const webview = getInnerActiveWebview(accessor); - if (webview && webview.isFocused) { + const webviewService = accessor.get(IWebviewService); + const webview = webviewService.activeWebview; + if (webview?.isFocused) { f(webview); return true; } diff --git a/src/vs/workbench/contrib/webview/browser/webview.ts b/src/vs/workbench/contrib/webview/browser/webview.ts index 4df30f8be7b..61c085a241c 100644 --- a/src/vs/workbench/contrib/webview/browser/webview.ts +++ b/src/vs/workbench/contrib/webview/browser/webview.ts @@ -36,6 +36,8 @@ export interface WebviewIcons { export interface IWebviewService { readonly _serviceBrand: undefined; + readonly activeWebview: Webview | undefined; + createWebviewElement( id: string, options: WebviewOptions, @@ -100,6 +102,8 @@ export interface Webview extends IDisposable { readonly onDidFocus: Event; readonly onDidBlur: Event; + readonly onDidDispose: Event; + readonly onDidClickLink: Event; readonly onDidScroll: Event<{ scrollYPercentage: number }>; readonly onDidWheel: Event; @@ -142,8 +146,6 @@ export interface WebviewOverlay extends Webview { readonly container: HTMLElement; options: WebviewOptions; - readonly onDispose: Event; - claim(owner: any): void; release(owner: any): void; diff --git a/src/vs/workbench/contrib/webview/browser/webviewService.ts b/src/vs/workbench/contrib/webview/browser/webviewService.ts index 688b513948d..a26e64ab978 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewService.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewService.ts @@ -3,34 +3,39 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IWebviewService, WebviewContentOptions, WebviewOverlay, WebviewElement, WebviewIcons, WebviewOptions, WebviewExtensionDescription } from 'vs/workbench/contrib/webview/browser/webview'; -import { IFrameWebview } from 'vs/workbench/contrib/webview/browser/webviewElement'; import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/browser/themeing'; +import { IWebviewService, Webview, WebviewContentOptions, WebviewElement, WebviewExtensionDescription, WebviewIcons, WebviewOptions, WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview'; +import { IFrameWebview } from 'vs/workbench/contrib/webview/browser/webviewElement'; import { DynamicWebviewEditorOverlay } from './dynamicWebviewEditorOverlay'; import { WebviewIconManager } from './webviewIconManager'; export class WebviewService implements IWebviewService { declare readonly _serviceBrand: undefined; - private readonly _webviewThemeDataProvider: WebviewThemeDataProvider; + protected readonly _webviewThemeDataProvider: WebviewThemeDataProvider; + private readonly _iconManager: WebviewIconManager; constructor( - @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IInstantiationService protected readonly _instantiationService: IInstantiationService, ) { this._webviewThemeDataProvider = this._instantiationService.createInstance(WebviewThemeDataProvider); this._iconManager = this._instantiationService.createInstance(WebviewIconManager); } + private _activeWebview?: Webview; + public get activeWebview() { return this._activeWebview; } + createWebviewElement( id: string, options: WebviewOptions, contentOptions: WebviewContentOptions, extension: WebviewExtensionDescription | undefined, ): WebviewElement { - return this._instantiationService.createInstance(IFrameWebview, id, options, contentOptions, extension, this._webviewThemeDataProvider); + const webview = this._instantiationService.createInstance(IFrameWebview, id, options, contentOptions, extension, this._webviewThemeDataProvider); + this.addWebviewListeners(webview); + return webview; } createWebviewOverlay( @@ -39,12 +44,27 @@ export class WebviewService implements IWebviewService { contentOptions: WebviewContentOptions, extension: WebviewExtensionDescription | undefined, ): WebviewOverlay { - return this._instantiationService.createInstance(DynamicWebviewEditorOverlay, id, options, contentOptions, extension); + const webview = this._instantiationService.createInstance(DynamicWebviewEditorOverlay, id, options, contentOptions, extension); + this.addWebviewListeners(webview); + return webview; } setIcons(id: string, iconPath: WebviewIcons | undefined): void { this._iconManager.setIcons(id, iconPath); } -} -registerSingleton(IWebviewService, WebviewService, true); + protected addWebviewListeners(webview: Webview) { + webview.onDidFocus(() => { + this._activeWebview = webview; + }); + + const onBlur = () => { + if (this._activeWebview === webview) { + this._activeWebview = undefined; + } + }; + + webview.onDidBlur(onBlur); + webview.onDidDispose(onBlur); + } +} diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewService.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewService.ts index 44f3d38b9e9..480f2212768 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewService.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewService.ts @@ -6,24 +6,19 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { DynamicWebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay'; -import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/browser/themeing'; -import { IWebviewService, WebviewContentOptions, WebviewElement, WebviewExtensionDescription, WebviewIcons, WebviewOptions, WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview'; -import { WebviewIconManager } from 'vs/workbench/contrib/webview/browser/webviewIconManager'; +import { WebviewContentOptions, WebviewElement, WebviewExtensionDescription, WebviewOptions, WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview'; +import { WebviewService } from 'vs/workbench/contrib/webview/browser/webviewService'; import { ElectronIframeWebview } from 'vs/workbench/contrib/webview/electron-browser/iframeWebviewElement'; import { ElectronWebviewBasedWebview } from 'vs/workbench/contrib/webview/electron-browser/webviewElement'; -export class ElectronWebviewService implements IWebviewService { +export class ElectronWebviewService extends WebviewService { declare readonly _serviceBrand: undefined; - private readonly _webviewThemeDataProvider: WebviewThemeDataProvider; - private readonly _iconManager: WebviewIconManager; - constructor( - @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IInstantiationService instantiationService: IInstantiationService, @IConfigurationService private readonly _configService: IConfigurationService, ) { - this._webviewThemeDataProvider = this._instantiationService.createInstance(WebviewThemeDataProvider); - this._iconManager = this._instantiationService.createInstance(WebviewIconManager); + super(instantiationService); } createWebviewElement( @@ -33,7 +28,9 @@ export class ElectronWebviewService implements IWebviewService { extension: WebviewExtensionDescription | undefined, ): WebviewElement { const useIframes = this._configService.getValue('webview.experimental.useIframes'); - return this._instantiationService.createInstance(useIframes ? ElectronIframeWebview : ElectronWebviewBasedWebview, id, options, contentOptions, extension, this._webviewThemeDataProvider); + const webview = this._instantiationService.createInstance(useIframes ? ElectronIframeWebview : ElectronWebviewBasedWebview, id, options, contentOptions, extension, this._webviewThemeDataProvider); + this.addWebviewListeners(webview); + return webview; } createWebviewOverlay( @@ -42,10 +39,8 @@ export class ElectronWebviewService implements IWebviewService { contentOptions: WebviewContentOptions, extension: WebviewExtensionDescription | undefined, ): WebviewOverlay { - return this._instantiationService.createInstance(DynamicWebviewEditorOverlay, id, options, contentOptions, extension); - } - - setIcons(id: string, iconPath: WebviewIcons | undefined): void { - this._iconManager.setIcons(id, iconPath); + const webview = this._instantiationService.createInstance(DynamicWebviewEditorOverlay, id, options, contentOptions, extension); + this.addWebviewListeners(webview); + return webview; } } diff --git a/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts b/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts index 1159fe856e7..181d779e376 100644 --- a/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts +++ b/src/vs/workbench/electron-sandbox/sandbox.simpleservices.ts @@ -599,6 +599,8 @@ registerSingleton(IKeymapService, SimpleKeymapService); class SimpleWebviewService implements IWebviewService { declare readonly _serviceBrand: undefined; + readonly activeWebview = undefined; + createWebviewElement(id: string, options: WebviewOptions, contentOptions: WebviewContentOptions, extension: WebviewExtensionDescription | undefined): WebviewElement { throw new Error('Method not implemented.'); } createWebviewOverlay(id: string, options: WebviewOptions, contentOptions: WebviewContentOptions, extension: WebviewExtensionDescription | undefined): WebviewOverlay { throw new Error('Method not implemented.'); } setIcons(id: string, value: WebviewIcons | undefined): void { } From cceaf143236f352fc0eb429b5084aaf74545c323 Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Wed, 9 Sep 2020 15:37:10 -0700 Subject: [PATCH 62/71] #106358 --- src/vs/base/node/ps.ts | 11 ++++++++++- .../authentication/browser/authenticationService.ts | 7 ++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/vs/base/node/ps.ts b/src/vs/base/node/ps.ts index 0786b753dd2..31223d4a5d8 100644 --- a/src/vs/base/node/ps.ts +++ b/src/vs/base/node/ps.ts @@ -193,6 +193,11 @@ export function listProcesses(rootPid: number): Promise { processInfo.load = parseFloat(cpuUsage[i]); } + if (!rootItem) { + reject(new Error(`Root process ${rootPid} not found`)); + return; + } + resolve(rootItem); } }); @@ -228,7 +233,11 @@ export function listProcesses(rootPid: number): Promise { if (process.platform === 'linux') { calculateLinuxCpuUsage(); } else { - resolve(rootItem); + if (!rootItem) { + reject(new Error(`Root process ${rootPid} not found`)); + } else { + resolve(rootItem); + } } } }); diff --git a/src/vs/workbench/services/authentication/browser/authenticationService.ts b/src/vs/workbench/services/authentication/browser/authenticationService.ts index 7d058a717aa..db85f09a426 100644 --- a/src/vs/workbench/services/authentication/browser/authenticationService.ts +++ b/src/vs/workbench/services/authentication/browser/authenticationService.ts @@ -444,7 +444,12 @@ export class AuthenticationService extends Disposable implements IAuthentication const didRegister: Promise = new Promise((resolve, _) => { this.onDidRegisterAuthenticationProvider(e => { if (e.id === providerId) { - resolve(this._authenticationProviders.get(providerId)); + provider = this._authenticationProviders.get(providerId); + if (provider) { + resolve(provider); + } else { + throw new Error(`No authentication provider '${providerId}' is currently registered.`); + } } }); }); From 7e6273963931402426447bf243fe23c7b577f607 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 9 Sep 2020 16:41:48 -0700 Subject: [PATCH 63/71] debug: bump js-debug-companion --- product.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/product.json b/product.json index 7651fc50bb5..e79fb4baf5e 100644 --- a/product.json +++ b/product.json @@ -76,7 +76,7 @@ }, { "name": "ms-vscode.js-debug-companion", - "version": "1.0.7", + "version": "1.0.8", "repo": "https://github.com/microsoft/vscode-js-debug-companion", "metadata": { "id": "99cb0b7f-7354-4278-b8da-6cc79972169d", From 73a3c9ded30ca1eea2ec1e5d6121614e64e78f81 Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 9 Sep 2020 16:06:28 -0700 Subject: [PATCH 64/71] re #105735. --- .../src/notebook.test.ts | 7 ++++++ .../api/browser/mainThreadNotebook.ts | 17 +------------- .../workbench/api/common/extHost.protocol.ts | 1 - .../workbench/api/common/extHostNotebook.ts | 6 +++-- .../api/common/extHostNotebookDocument.ts | 23 ++++++++++++++----- 5 files changed, 29 insertions(+), 25 deletions(-) diff --git a/extensions/vscode-notebook-tests/src/notebook.test.ts b/extensions/vscode-notebook-tests/src/notebook.test.ts index 311455f7573..746d7a303b2 100644 --- a/extensions/vscode-notebook-tests/src/notebook.test.ts +++ b/extensions/vscode-notebook-tests/src/notebook.test.ts @@ -690,11 +690,18 @@ suite('notebook workflow', () => { await vscode.commands.executeCommand('notebook.focusTop'); const cell = editor.document.cells[0]; assert.equal(cell.outputs.length, 0); + + let metadataChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeCellMetadata); cell.metadata.runnable = false; + await metadataChangeEvent; + await vscode.commands.executeCommand('notebook.cell.execute'); assert.equal(cell.outputs.length, 0, 'should not execute'); // not runnable, didn't work + metadataChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeCellMetadata); cell.metadata.runnable = true; + await metadataChangeEvent; + await vscode.commands.executeCommand('notebook.cell.execute'); assert.equal(cell.outputs.length, 1, 'should execute'); // runnable, it worked diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index ae0e9662b64..b931d5b17aa 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -18,7 +18,7 @@ import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookB import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService'; -import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellEditType, DisplayOrderKey, ICellEditOperation, ICellRange, IEditor, IMainCellDto, INotebookDocumentFilter, NotebookCellMetadata, NotebookCellOutputsSplice, NotebookCellsChangeType, NotebookDocumentMetadata, NOTEBOOK_DISPLAY_ORDER, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellEditType, DisplayOrderKey, ICellEditOperation, ICellRange, IEditor, IMainCellDto, INotebookDocumentFilter, NotebookCellOutputsSplice, NotebookCellsChangeType, NotebookDocumentMetadata, NOTEBOOK_DISPLAY_ORDER, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { IMainNotebookController, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; @@ -566,21 +566,6 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo textModel?.updateNotebookMetadata(metadata); } - async $updateNotebookCellMetadata(viewType: string, resource: UriComponents, handle: number, metadata: NotebookCellMetadata): Promise { - this.logService.debug('MainThreadNotebooks#updateNotebookCellMetadata', resource.path, handle, metadata); - const textModel = this._notebookService.getNotebookTextModel(URI.from(resource)); - if (!textModel) { - return; - } - - const index = textModel.cells.findIndex(cell => cell.handle === handle); - if (index < 0) { - return; - } - - textModel.applyEdit(textModel.versionId, [{ editType: CellEditType.Metadata, index, metadata }], true); - } - async $spliceNotebookCellOutputs(viewType: string, resource: UriComponents, cellHandle: number, splices: NotebookCellOutputsSplice[]): Promise { this.logService.debug('MainThreadNotebooks#spliceNotebookCellOutputs', resource.path, cellHandle); const textModel = this._notebookService.getNotebookTextModel(URI.from(resource)); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index a6387986941..799bf64eb54 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -744,7 +744,6 @@ export interface MainThreadNotebookShape extends IDisposable { $tryApplyEdits(viewType: string, resource: UriComponents, modelVersionId: number, edits: ICellEditOperation[], metadata: NotebookDocumentMetadata | undefined): Promise; $updateNotebookLanguages(viewType: string, resource: UriComponents, languages: string[]): Promise; $updateNotebookMetadata(viewType: string, resource: UriComponents, metadata: NotebookDocumentMetadata): Promise; - $updateNotebookCellMetadata(viewType: string, resource: UriComponents, handle: number, metadata: NotebookCellMetadata | undefined): Promise; $spliceNotebookCellOutputs(viewType: string, resource: UriComponents, cellHandle: number, splices: NotebookCellOutputsSplice[]): Promise; $postMessage(editorId: string, forRendererId: string | undefined, value: any): Promise; $setStatusBarEntry(id: number, statusBarEntry: INotebookCellStatusBarEntryDto): Promise; diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index c1944e6064a..56be870c098 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -9,7 +9,7 @@ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; import * as UUID from 'vs/base/common/uuid'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { ExtHostNotebookShape, ICommandDto, IMainContext, IModelAddedData, INotebookDocumentPropertiesChangeData, INotebookDocumentsAndEditorsDelta, INotebookEditorPropertiesChangeData, MainContext, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostNotebookShape, ICommandDto, IMainContext, IModelAddedData, INotebookDocumentPropertiesChangeData, INotebookDocumentsAndEditorsDelta, INotebookEditorPropertiesChangeData, MainContext, MainThreadBulkEditsShape, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol'; import { ILogService } from 'vs/platform/log/common/log'; import { CommandsConverter, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; @@ -191,6 +191,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN private static _notebookKernelProviderHandlePool: number = 0; private readonly _proxy: MainThreadNotebookShape; + private readonly _mainThreadBulkEdits: MainThreadBulkEditsShape; private readonly _notebookContentProviders = new Map(); private readonly _notebookKernels = new Map(); private readonly _notebookKernelProviders = new Map(); @@ -246,6 +247,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN private readonly _extensionStoragePaths: IExtensionStoragePaths, ) { this._proxy = mainContext.getProxy(MainContext.MainThreadNotebook); + this._mainThreadBulkEdits = mainContext.getProxy(MainContext.MainThreadBulkEdits); this._commandsConverter = commands.converter; commands.registerArgumentProcessor({ @@ -679,7 +681,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } const that = this; - const document = new ExtHostNotebookDocument(this._proxy, this._documentsAndEditors, { + const document = new ExtHostNotebookDocument(this._proxy, this._documentsAndEditors, this._mainThreadBulkEdits, { emitModelChange(event: vscode.NotebookCellsChangeEvent): void { that._onDidChangeNotebookCells.fire(event); }, diff --git a/src/vs/workbench/api/common/extHostNotebookDocument.ts b/src/vs/workbench/api/common/extHostNotebookDocument.ts index 363d0eb98df..f7f77c9e9f2 100644 --- a/src/vs/workbench/api/common/extHostNotebookDocument.ts +++ b/src/vs/workbench/api/common/extHostNotebookDocument.ts @@ -11,9 +11,9 @@ import { joinPath } from 'vs/base/common/resources'; import { ISplice } from 'vs/base/common/sequence'; import { URI } from 'vs/base/common/uri'; import * as UUID from 'vs/base/common/uuid'; -import { CellKind, MainThreadNotebookShape, NotebookCellOutputsSplice } from 'vs/workbench/api/common/extHost.protocol'; +import { CellKind, IWorkspaceCellEditDto, MainThreadBulkEditsShape, MainThreadNotebookShape, NotebookCellOutputsSplice, WorkspaceEditType } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostDocumentsAndEditors, IExtHostModelAddedData } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; -import { CellOutputKind, diff, IMainCellDto, IProcessedOutput, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookCellsSplice2, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellEditType, CellOutputKind, diff, IMainCellDto, IProcessedOutput, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookCellsSplice2, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import * as vscode from 'vscode'; import { Cache } from './cache'; @@ -86,7 +86,7 @@ export class ExtHostCell extends Disposable { private _cell: vscode.NotebookCell | undefined; constructor( - private readonly _proxy: MainThreadNotebookShape, + private readonly _mainThreadBulkEdits: MainThreadBulkEditsShape, private readonly _notebook: ExtHostNotebookDocument, private readonly _extHostDocument: ExtHostDocumentsAndEditors, private readonly _cellData: IMainCellDto, @@ -181,8 +181,18 @@ export class ExtHostCell extends Disposable { })); } - private _updateMetadata(): Promise { - return this._proxy.$updateNotebookCellMetadata(this._notebook.notebookDocument.viewType, this._notebook.uri, this.handle, this._metadata); + private _updateMetadata(): Promise { + const index = this._notebook.notebookDocument.cells.indexOf(this.cell); + const edit: IWorkspaceCellEditDto = { + _type: WorkspaceEditType.Cell, + metadata: undefined, + resource: this._notebook.uri, + notebookVersionId: this._notebook.notebookDocument.version, + edit: { editType: CellEditType.Metadata, index, metadata: this._metadata } + }; + + console.log('_updateMetadata', this._metadata); + return this._mainThreadBulkEdits.$tryApplyWorkspaceEdit({ edits: [edit] }); } } @@ -222,6 +232,7 @@ export class ExtHostNotebookDocument extends Disposable { constructor( private readonly _proxy: MainThreadNotebookShape, private readonly _documentsAndEditors: ExtHostDocumentsAndEditors, + private readonly _mainThreadBulkEdits: MainThreadBulkEditsShape, private readonly _emitter: INotebookEventEmitter, private readonly _viewType: string, metadata: Required, @@ -339,7 +350,7 @@ export class ExtHostNotebookDocument extends Disposable { const cellDtos = splice[2]; const newCells = cellDtos.map(cell => { - const extCell = new ExtHostCell(this._proxy, this, this._documentsAndEditors, cell); + const extCell = new ExtHostCell(this._mainThreadBulkEdits, this, this._documentsAndEditors, cell); if (!initialization) { addedCellDocuments.push(ExtHostCell.asModelAddData(this.notebookDocument, cell)); From 03960a5aa1c21f8dc6db2211e0a62a736624ff71 Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 9 Sep 2020 16:38:52 -0700 Subject: [PATCH 65/71] re #105735. no more udpateMetadata api. --- .../src/notebook.test.ts | 6 ++ src/vs/vscode.proposed.d.ts | 5 ++ .../api/browser/mainThreadNotebook.ts | 19 ++-- .../workbench/api/common/extHost.api.impl.ts | 4 + .../workbench/api/common/extHost.protocol.ts | 1 - .../workbench/api/common/extHostNotebook.ts | 10 ++- .../api/common/extHostNotebookDocument.ts | 34 ++++++- .../api/common/extHostNotebookEditor.ts | 3 +- .../contrib/bulkEdit/browser/bulkCellEdits.ts | 7 +- .../common/model/notebookTextModel.ts | 89 +++++++++++-------- .../contrib/notebook/common/notebookCommon.ts | 10 ++- 11 files changed, 128 insertions(+), 60 deletions(-) diff --git a/extensions/vscode-notebook-tests/src/notebook.test.ts b/extensions/vscode-notebook-tests/src/notebook.test.ts index 746d7a303b2..0547012433a 100644 --- a/extensions/vscode-notebook-tests/src/notebook.test.ts +++ b/extensions/vscode-notebook-tests/src/notebook.test.ts @@ -718,11 +718,17 @@ suite('notebook workflow', () => { const cell = editor.document.cells[0]; assert.equal(cell.outputs.length, 0); + let metadataChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeNotebookDocumentMetadata); editor.document.metadata.runnable = false; + await metadataChangeEvent; + await vscode.commands.executeCommand('notebook.execute'); assert.equal(cell.outputs.length, 0, 'should not execute'); // not runnable, didn't work + metadataChangeEvent = getEventOncePromise(vscode.notebook.onDidChangeNotebookDocumentMetadata); editor.document.metadata.runnable = true; + await metadataChangeEvent; + await vscode.commands.executeCommand('notebook.execute'); assert.equal(cell.outputs.length, 1, 'should execute'); // runnable, it worked diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 5741ff5aa85..108a6515e42 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1470,6 +1470,10 @@ declare module 'vscode' { outputId: string; } + export interface NotebookDocumentMetadataChangeEvent { + readonly document: NotebookDocument; + } + export interface NotebookCellsChangeData { readonly start: number; readonly deletedCount: number; @@ -1747,6 +1751,7 @@ declare module 'vscode' { export const onDidChangeActiveNotebookEditor: Event; export const onDidChangeNotebookEditorSelection: Event; export const onDidChangeNotebookEditorVisibleRanges: Event; + export const onDidChangeNotebookDocumentMetadata: Event; export const onDidChangeNotebookCells: Event; export const onDidChangeCellOutputs: Event; export const onDidChangeCellLanguage: Event; diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index b931d5b17aa..582fd07d165 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -162,9 +162,13 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo return false; } this._notebookService.transformEditsOutputs(textModel, cellEdits); - //TODO@rebornix should this go into applyEdit? if (newMetadata) { - textModel.updateNotebookMetadata(newMetadata); + textModel.applyEdit(textModel.versionId, [ + { + editType: CellEditType.DocumentMetadata, + metadata: newMetadata + } + ], true); } return textModel.applyEdit(modelVersionId, cellEdits, true); } @@ -306,9 +310,10 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo * Since `e.transient` decides if the model should be dirty or not, we will use the same logic here. */ this._proxy.$acceptModelChanged(textModel.uri, data, !e.transient); - this._proxy.$acceptDocumentPropertiesChanged(textModel.uri, { metadata: null }); + if (e.kind === NotebookCellsChangeType.ChangeDocumentMetadata) { + this._proxy.$acceptDocumentPropertiesChanged(textModel.uri, { metadata: textModel.metadata }); + } })); - this._editorEventListenersMapping.set(textModel!.uri.toString(), disposableStore); } }; @@ -560,12 +565,6 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo textModel?.updateLanguages(languages); } - async $updateNotebookMetadata(viewType: string, resource: UriComponents, metadata: NotebookDocumentMetadata): Promise { - this.logService.debug('MainThreadNotebooks#updateNotebookMetadata', resource.path, metadata); - const textModel = this._notebookService.getNotebookTextModel(URI.from(resource)); - textModel?.updateNotebookMetadata(metadata); - } - async $spliceNotebookCellOutputs(viewType: string, resource: UriComponents, cellHandle: number, splices: NotebookCellOutputsSplice[]): Promise { this.logService.debug('MainThreadNotebooks#spliceNotebookCellOutputs', resource.path, cellHandle); const textModel = this._notebookService.getNotebookTextModel(URI.from(resource)); diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index e5e5b3cc88d..46a0b734f5b 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -971,6 +971,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I checkProposedApiEnabled(extension); return extHostNotebook.onDidChangeActiveNotebookEditor(listener, thisArgs, disposables); }, + onDidChangeNotebookDocumentMetadata(listener, thisArgs?, disposables?) { + checkProposedApiEnabled(extension); + return extHostNotebook.onDidChangeNotebookDocumentMetadata(listener, thisArgs, disposables); + }, onDidChangeNotebookCells(listener, thisArgs?, disposables?) { checkProposedApiEnabled(extension); return extHostNotebook.onDidChangeNotebookCells(listener, thisArgs, disposables); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 799bf64eb54..2f84c7625b1 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -743,7 +743,6 @@ export interface MainThreadNotebookShape extends IDisposable { $onNotebookKernelChange(handle: number, uri: UriComponents | undefined): void; $tryApplyEdits(viewType: string, resource: UriComponents, modelVersionId: number, edits: ICellEditOperation[], metadata: NotebookDocumentMetadata | undefined): Promise; $updateNotebookLanguages(viewType: string, resource: UriComponents, languages: string[]): Promise; - $updateNotebookMetadata(viewType: string, resource: UriComponents, metadata: NotebookDocumentMetadata): Promise; $spliceNotebookCellOutputs(viewType: string, resource: UriComponents, cellHandle: number, splices: NotebookCellOutputsSplice[]): Promise; $postMessage(editorId: string, forRendererId: string | undefined, value: any): Promise; $setStatusBarEntry(id: number, statusBarEntry: INotebookCellStatusBarEntryDto): Promise; diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 56be870c098..128cdec9c14 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -203,6 +203,8 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN readonly onDidChangeNotebookEditorSelection = this._onDidChangeNotebookEditorSelection.event; private readonly _onDidChangeNotebookEditorVisibleRanges = new Emitter(); readonly onDidChangeNotebookEditorVisibleRanges = this._onDidChangeNotebookEditorVisibleRanges.event; + private readonly _onDidChangeNotebookDocumentMetadata = new Emitter(); + readonly onDidChangeNotebookDocumentMetadata = this._onDidChangeNotebookDocumentMetadata.event; private readonly _onDidChangeNotebookCells = new Emitter(); readonly onDidChangeNotebookCells = this._onDidChangeNotebookCells.event; private readonly _onDidChangeCellOutputs = new Emitter(); @@ -604,10 +606,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } if (data.metadata) { - editor.editor.notebookData.notebookDocument.metadata = { - ...notebookDocumentMetadataDefaults, - ...data.metadata - }; + editor.editor.notebookData.acceptDocumentPropertiesChanged(data); } } @@ -693,6 +692,9 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN }, emitCellMetadataChange(event: vscode.NotebookCellMetadataChangeEvent): void { that._onDidChangeCellMetadata.fire(event); + }, + emitDocumentMetadataChange(event: vscode.NotebookDocumentMetadataChangeEvent): void { + that._onDidChangeNotebookDocumentMetadata.fire(event); } }, viewType, { ...notebookDocumentMetadataDefaults, ...modelData.metadata }, uri, storageRoot); diff --git a/src/vs/workbench/api/common/extHostNotebookDocument.ts b/src/vs/workbench/api/common/extHostNotebookDocument.ts index f7f77c9e9f2..ac83b9de093 100644 --- a/src/vs/workbench/api/common/extHostNotebookDocument.ts +++ b/src/vs/workbench/api/common/extHostNotebookDocument.ts @@ -11,7 +11,7 @@ import { joinPath } from 'vs/base/common/resources'; import { ISplice } from 'vs/base/common/sequence'; import { URI } from 'vs/base/common/uri'; import * as UUID from 'vs/base/common/uuid'; -import { CellKind, IWorkspaceCellEditDto, MainThreadBulkEditsShape, MainThreadNotebookShape, NotebookCellOutputsSplice, WorkspaceEditType } from 'vs/workbench/api/common/extHost.protocol'; +import { CellKind, INotebookDocumentPropertiesChangeData, IWorkspaceCellEditDto, MainThreadBulkEditsShape, MainThreadNotebookShape, NotebookCellOutputsSplice, WorkspaceEditType } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostDocumentsAndEditors, IExtHostModelAddedData } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import { CellEditType, CellOutputKind, diff, IMainCellDto, IProcessedOutput, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookCellsSplice2, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import * as vscode from 'vscode'; @@ -191,13 +191,13 @@ export class ExtHostCell extends Disposable { edit: { editType: CellEditType.Metadata, index, metadata: this._metadata } }; - console.log('_updateMetadata', this._metadata); return this._mainThreadBulkEdits.$tryApplyWorkspaceEdit({ edits: [edit] }); } } export interface INotebookEventEmitter { emitModelChange(events: vscode.NotebookCellsChangeEvent): void; + emitDocumentMetadataChange(event: vscode.NotebookDocumentMetadataChangeEvent): void; emitCellOutputsChange(event: vscode.NotebookCellOutputsChangeEvent): void; emitCellLanguageChange(event: vscode.NotebookCellLanguageChangeEvent): void; emitCellMetadataChange(event: vscode.NotebookCellMetadataChangeEvent): void; @@ -274,8 +274,17 @@ export class ExtHostNotebookDocument extends Disposable { } private _tryUpdateMetadata() { - this._proxy.$updateNotebookMetadata(this._viewType, this.uri, this._metadata); + const edit: IWorkspaceCellEditDto = { + _type: WorkspaceEditType.Cell, + metadata: undefined, + notebookMetadata: this._metadata, + resource: this.uri, + notebookVersionId: this.notebookDocument.version, + }; + + return this._mainThreadBulkEdits.$tryApplyWorkspaceEdit({ edits: [edit] }); } + get notebookDocument(): vscode.NotebookDocument { if (!this._notebook) { const that = this; @@ -319,6 +328,25 @@ export class ExtHostNotebookDocument extends Disposable { this._backup = undefined; } + acceptDocumentPropertiesChanged(data: INotebookDocumentPropertiesChangeData) { + const newMetadata = { + ...notebookDocumentMetadataDefaults, + ...data.metadata + }; + + if (this._metadataChangeListener) { + this._metadataChangeListener.dispose(); + } + + const observableMetadata = getObservable(newMetadata); + this._metadata = observableMetadata.proxy; + this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => { + this._tryUpdateMetadata(); + })); + + this._emitter.emitDocumentMetadataChange({ document: this.notebookDocument }); + } + acceptModelChanged(event: NotebookCellsChangedEventDto, isDirty: boolean): void { this._versionId = event.versionId; this._isDirty = isDirty; diff --git a/src/vs/workbench/api/common/extHostNotebookEditor.ts b/src/vs/workbench/api/common/extHostNotebookEditor.ts index f12f9f4f78e..4b98b86ae66 100644 --- a/src/vs/workbench/api/common/extHostNotebookEditor.ts +++ b/src/vs/workbench/api/common/extHostNotebookEditor.ts @@ -223,7 +223,8 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook const prev = compressedEdits[prevIndex]; if (prev.editType === CellEditType.Replace && editData.cellEdits[i].editType === CellEditType.Replace) { - if (prev.index === editData.cellEdits[i].index) { + const edit = editData.cellEdits[i]; + if (edit.editType !== CellEditType.DocumentMetadata && prev.index === edit.index) { prev.cells.push(...(editData.cellEdits[i] as ICellReplaceEdit).cells); prev.count += (editData.cellEdits[i] as ICellReplaceEdit).count; continue; diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkCellEdits.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkCellEdits.ts index 41ab95fd25e..6db8054a5f3 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkCellEdits.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkCellEdits.ts @@ -9,7 +9,7 @@ import { URI } from 'vs/base/common/uri'; import { ResourceEdit } from 'vs/editor/browser/services/bulkEditService'; import { WorkspaceEditMetadata } from 'vs/editor/common/modes'; import { IProgress } from 'vs/platform/progress/common/progress'; -import { ICellEditOperation, NotebookDocumentMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellEditType, ICellEditOperation, NotebookDocumentMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; @@ -60,7 +60,10 @@ export class BulkCellEdits { // set metadata if (newMetadata) { - ref.object.notebook.updateNotebookMetadata(newMetadata); + edits.push({ + editType: CellEditType.DocumentMetadata, + metadata: newMetadata + }); } // apply edits this._notebookService.transformEditsOutputs(ref.object.notebook, edits); diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index a265a6d36aa..132928efb9c 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -210,13 +210,13 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this._increaseVersionId(); } - dispose() { this._onWillDispose.fire(); dispose(this._cellListeners.values()); dispose(this._cells); super.dispose(); } + createCellTextModel( source: string, language: string, @@ -251,10 +251,21 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel const edits = rawEdits.map((edit, index) => { return { edit, - end: edit.editType === CellEditType.Replace ? edit.index + edit.count : edit.index, + end: + edit.editType === CellEditType.DocumentMetadata + ? undefined + : (edit.editType === CellEditType.Replace ? edit.index + edit.count : edit.index), originalIndex: index, }; }).sort((a, b) => { + if (a.end === undefined) { + return -1; + } + + if (b.end === undefined) { + return -1; + } + return b.end - a.end || b.originalIndex - a.originalIndex; }); @@ -278,12 +289,50 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this._assertIndex(edit.index); this._changeCellLanguage(this._cells[edit.index].handle, edit.language); break; + case CellEditType.DocumentMetadata: + this._updateNotebookMetadata(edit.metadata); + break; } } return true; } + + handleUnknownEdit(label: string | undefined, undo: () => void, redo: () => void): void { + this._operationManager.pushEditOperation({ + type: UndoRedoElementType.Resource, + resource: this.uri, + label: label ?? nls.localize('defaultEditLabel', "Edit"), + undo: async () => { + undo(); + }, + redo: async () => { + redo(); + }, + }); + + this._eventEmitter.emit({ + kind: NotebookCellsChangeType.Unknown, + transient: false, + synchronous: true, + versionId: this._versionId, + }); + } + + handleUnknownChange() { + this._eventEmitter.emit({ + kind: NotebookCellsChangeType.Unknown, + transient: false, + synchronous: true, + versionId: this._versionId, + }); + } + + createSnapshot(preserveBOM?: boolean): ITextSnapshot { + return new NotebookTextModelSnapshot(this); + } + private _replaceCells(index: number, count: number, cellDtos: ICellDto2[], synchronous: boolean): void { if (count === 0 && cellDtos.length === 0) { @@ -346,40 +395,6 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel }); } - handleUnknownEdit(label: string | undefined, undo: () => void, redo: () => void): void { - this._operationManager.pushEditOperation({ - type: UndoRedoElementType.Resource, - resource: this.uri, - label: label ?? nls.localize('defaultEditLabel', "Edit"), - undo: async () => { - undo(); - }, - redo: async () => { - redo(); - }, - }); - - this._eventEmitter.emit({ - kind: NotebookCellsChangeType.Unknown, - transient: false, - synchronous: true, - versionId: this._versionId, - }); - } - - handleUnknownChange() { - this._eventEmitter.emit({ - kind: NotebookCellsChangeType.Unknown, - transient: false, - synchronous: true, - versionId: this._versionId, - }); - } - - createSnapshot(preserveBOM?: boolean): ITextSnapshot { - return new NotebookTextModelSnapshot(this); - } - private _increaseVersionId(): void { this._versionId = this._versionId + 1; } @@ -395,7 +410,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel } } - updateNotebookMetadata(metadata: NotebookDocumentMetadata) { + private _updateNotebookMetadata(metadata: NotebookDocumentMetadata) { this.metadata = metadata; this._eventEmitter.emit({ kind: NotebookCellsChangeType.ChangeDocumentMetadata, versionId: this.versionId, metadata: this.metadata, synchronous: true, transient: false }); } diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 444d228c1f2..d409aacb92e 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -418,7 +418,8 @@ export const enum CellEditType { Replace = 1, Output = 2, Metadata = 3, - CellLanguage = 4 + CellLanguage = 4, + DocumentMetadata = 5, } export interface ICellDto2 { @@ -455,7 +456,12 @@ export interface ICellLanguageEdit { language: string; } -export type ICellEditOperation = ICellReplaceEdit | ICellOutputEdit | ICellMetadataEdit | ICellLanguageEdit; +export interface IDocumentMetadataEdit { + editType: CellEditType.DocumentMetadata; + metadata: NotebookDocumentMetadata; +} + +export type ICellEditOperation = ICellReplaceEdit | ICellOutputEdit | ICellMetadataEdit | ICellLanguageEdit | IDocumentMetadataEdit; export interface INotebookEditData { documentVersionId: number; From 8e5eed12991c8af7a599b3f68fb9ca723b953316 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 9 Sep 2020 21:44:36 -0500 Subject: [PATCH 66/71] Fix #106303 --- .../workbench/contrib/preferences/browser/settingsEditor2.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index 44ad8bb9512..df153f089e6 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -343,7 +343,10 @@ export class SettingsEditor2 extends EditorPane { super.setEditorVisible(visible, group); if (!visible) { - this.searchWidget.onHide(); + // Wait for editor to be removed from DOM #106303 + process.nextTick(() => { + this.searchWidget.onHide(); + }); } } From 3f8f022200b8e227059569a5f5a928a9582de2bd Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 9 Sep 2020 15:49:06 -0700 Subject: [PATCH 67/71] Use destructuring to make code more clear --- .../services/editor/common/editorOpenWith.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/services/editor/common/editorOpenWith.ts b/src/vs/workbench/services/editor/common/editorOpenWith.ts index a824ac2e408..807464f51f8 100644 --- a/src/vs/workbench/services/editor/common/editorOpenWith.ts +++ b/src/vs/workbench/services/editor/common/editorOpenWith.ts @@ -62,13 +62,13 @@ export async function openEditorWith( // Prompt const resourceExt = extname(resource); - const items: (IQuickPickItem & { handler: IOpenEditorOverrideHandler })[] = allEditorOverrides.map((override) => { + const items: (IQuickPickItem & { handler: IOpenEditorOverrideHandler })[] = allEditorOverrides.map(([handler, entry]) => { return { - handler: override[0], - id: override[1].id, - label: override[1].label, - description: override[1].active ? nls.localize('promptOpenWith.currentlyActive', 'Currently Active') : undefined, - detail: override[1].detail, + handler: handler, + id: entry.id, + label: entry.label, + description: entry.active ? nls.localize('promptOpenWith.currentlyActive', 'Currently Active') : undefined, + detail: entry.detail, buttons: resourceExt ? [{ iconClass: 'codicon-settings-gear', tooltip: nls.localize('promptOpenWith.setDefaultTooltip', "Set as default editor for '{0}' files", resourceExt) From cf38af84ff2d0cdfa293c41a9016dc410dfda68c Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 9 Sep 2020 21:37:16 -0700 Subject: [PATCH 68/71] Revert "Fix #106303" This reverts commit 8e5eed12991c8af7a599b3f68fb9ca723b953316. this is causing a layer check issue --- .../workbench/contrib/preferences/browser/settingsEditor2.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index df153f089e6..44ad8bb9512 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -343,10 +343,7 @@ export class SettingsEditor2 extends EditorPane { super.setEditorVisible(visible, group); if (!visible) { - // Wait for editor to be removed from DOM #106303 - process.nextTick(() => { - this.searchWidget.onHide(); - }); + this.searchWidget.onHide(); } } From ec8016f7d1f29ce0ce682a38c7aac41d9ed4ecc0 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 9 Sep 2020 21:51:22 -0700 Subject: [PATCH 69/71] Cache webview view title across reloads Fixes #105867 --- .../api/browser/mainThreadWebviewViews.ts | 2 +- .../workbench/api/common/extHost.protocol.ts | 2 +- .../api/common/extHostWebviewView.ts | 5 +++- .../webviewView/browser/webviewViewPane.ts | 30 +++++++++++++++---- 4 files changed, 30 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadWebviewViews.ts b/src/vs/workbench/api/browser/mainThreadWebviewViews.ts index eae46c714d0..d43016bcd07 100644 --- a/src/vs/workbench/api/browser/mainThreadWebviewViews.ts +++ b/src/vs/workbench/api/browser/mainThreadWebviewViews.ts @@ -78,7 +78,7 @@ export class MainThreadWebviewsViews extends Disposable implements extHostProtoc }); try { - await this._proxy.$resolveWebviewView(handle, viewType, state, cancellation); + await this._proxy.$resolveWebviewView(handle, viewType, webviewView.title, state, cancellation); } catch (error) { onUnexpectedError(error); webviewView.webview.html = this.mainThreadWebviews.getWebviewResolvedFailedContent(viewType); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 2f84c7625b1..dd896025d43 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -686,7 +686,7 @@ export interface ExtHostCustomEditorsShape { } export interface ExtHostWebviewViewsShape { - $resolveWebviewView(webviewHandle: WebviewHandle, viewType: string, state: any, cancellation: CancellationToken): Promise; + $resolveWebviewView(webviewHandle: WebviewHandle, viewType: string, title: string | undefined, state: any, cancellation: CancellationToken): Promise; $onDidChangeWebviewViewVisibility(webviewHandle: WebviewHandle, visible: boolean): void; diff --git a/src/vs/workbench/api/common/extHostWebviewView.ts b/src/vs/workbench/api/common/extHostWebviewView.ts index dcb76f3afd3..5122096832e 100644 --- a/src/vs/workbench/api/common/extHostWebviewView.ts +++ b/src/vs/workbench/api/common/extHostWebviewView.ts @@ -29,12 +29,14 @@ class ExtHostWebviewView extends Disposable implements vscode.WebviewView { handle: extHostProtocol.WebviewHandle, proxy: extHostProtocol.MainThreadWebviewViewsShape, viewType: string, + title: string | undefined, webview: ExtHostWebview, isVisible: boolean, ) { super(); this.#viewType = viewType; + this.#title = title; this.#handle = handle; this.#proxy = proxy; this.#webview = webview; @@ -153,6 +155,7 @@ export class ExtHostWebviewViews implements extHostProtocol.ExtHostWebviewViewsS async $resolveWebviewView( webviewHandle: string, viewType: string, + title: string | undefined, state: any, cancellation: CancellationToken, ): Promise { @@ -164,7 +167,7 @@ export class ExtHostWebviewViews implements extHostProtocol.ExtHostWebviewViewsS const { provider, extension } = entry; const webview = this._extHostWebview.createNewWebview(webviewHandle, { /* todo */ }, extension); - const revivedView = new ExtHostWebviewView(webviewHandle, this._proxy, viewType, webview, true); + const revivedView = new ExtHostWebviewView(webviewHandle, this._proxy, viewType, title, webview, true); this._webviewViews.set(webviewHandle, revivedView); diff --git a/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts b/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts index ad5ba7e2796..8e2849173f7 100644 --- a/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts +++ b/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts @@ -28,7 +28,10 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten declare const ResizeObserver: any; -const webviewStateKey = 'webviewState'; +const storageKeys = { + webviewState: 'webviewState', + title: 'title' +} as const; export class WebviewViewPane extends ViewPane { @@ -38,6 +41,9 @@ export class WebviewViewPane extends ViewPane { private _container?: HTMLElement; private _resizeObserver?: any; + private readonly defaultTitle: string; + private setTitle: string | undefined; + private readonly memento: Memento; private readonly viewState: MementoObject; @@ -60,10 +66,16 @@ export class WebviewViewPane extends ViewPane { @IViewsService private readonly viewService: IViewsService, ) { super({ ...options, titleMenuId: MenuId.ViewTitle }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); + this.defaultTitle = this.title; this.memento = new Memento(`webviewView.${this.id}`, storageService); this.viewState = this.memento.getMemento(StorageScope.WORKSPACE); + const storedTitle = this.viewState[storageKeys.title]; + if (typeof storedTitle === 'string') { + this.updateTitle(storedTitle); + } + this._register(this.onDidChangeBodyVisibility(() => this.updateTreeVisibility())); this.updateTreeVisibility(); } @@ -108,8 +120,9 @@ export class WebviewViewPane extends ViewPane { public saveState() { if (this._webview) { - this.viewState[webviewStateKey] = this._webview.state; + this.viewState[storageKeys.webviewState] = this._webview.state; } + this.viewState[storageKeys.title] = this.setTitle; this.memento.saveMemento(); super.saveState(); @@ -142,7 +155,7 @@ export class WebviewViewPane extends ViewPane { const webviewId = `webviewView-${this.id.replace(/[^a-z0-9]/gi, '-')}`.toLowerCase(); const webview = this.webviewService.createWebviewOverlay(webviewId, {}, {}, undefined); - webview.state = this.viewState['webviewState']; + webview.state = this.viewState[storageKeys.webviewState]; this._webview = webview; this._register(toDisposable(() => { @@ -150,7 +163,7 @@ export class WebviewViewPane extends ViewPane { })); this._register(webview.onDidUpdateState(() => { - this.viewState[webviewStateKey] = webview.state; + this.viewState[storageKeys.webviewState] = webview.state; })); const source = this._register(new CancellationTokenSource()); @@ -164,8 +177,8 @@ export class WebviewViewPane extends ViewPane { onDidChangeVisibility: this.onDidChangeBodyVisibility, onDispose: this.onDispose, - get title() { return self.title; }, - set title(value: string) { self.updateTitle(value); }, + get title(): string | undefined { return self.setTitle; }, + set title(value: string | undefined) { self.updateTitle(value); }, get description(): string | undefined { return self.titleDescription; }, set description(value: string | undefined) { self.updateTitleDescription(value); }, @@ -180,6 +193,11 @@ export class WebviewViewPane extends ViewPane { } } + protected updateTitle(value: string | undefined) { + this.setTitle = value; + super.updateTitle(typeof value === 'string' ? value : this.defaultTitle); + } + private async withProgress(task: () => Promise): Promise { return this.progressService.withProgress({ location: this.id, delay: 500 }, task); } From 3fb8967173fd16d9d63b98efdd264cfe2fc5be7b Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 10 Sep 2020 08:49:08 +0200 Subject: [PATCH 70/71] fix some TS 4.1 errors (#106358) --- src/vs/workbench/api/browser/mainThreadMessageService.ts | 4 ++-- src/vs/workbench/services/progress/browser/progressService.ts | 4 ++-- .../workspaces/browser/abstractWorkspaceEditingService.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadMessageService.ts b/src/vs/workbench/api/browser/mainThreadMessageService.ts index 74c4109631f..c956da47b95 100644 --- a/src/vs/workbench/api/browser/mainThreadMessageService.ts +++ b/src/vs/workbench/api/browser/mainThreadMessageService.ts @@ -39,9 +39,9 @@ export class MainThreadMessageService implements MainThreadMessageServiceShape { } } - private _showMessage(severity: Severity, message: string, commands: { title: string; isCloseAffordance: boolean; handle: number; }[], extension: IExtensionDescription | undefined): Promise { + private _showMessage(severity: Severity, message: string, commands: { title: string; isCloseAffordance: boolean; handle: number; }[], extension: IExtensionDescription | undefined): Promise { - return new Promise(resolve => { + return new Promise(resolve => { const primaryActions: MessageItemAction[] = []; diff --git a/src/vs/workbench/services/progress/browser/progressService.ts b/src/vs/workbench/services/progress/browser/progressService.ts index d1a79df4f13..89f4eafc97e 100644 --- a/src/vs/workbench/services/progress/browser/progressService.ts +++ b/src/vs/workbench/services/progress/browser/progressService.ts @@ -224,9 +224,9 @@ export class ProgressService extends Disposable implements IProgressService { // Create a promise that we can resolve as needed // when the outside calls dispose on us let promiseResolve: () => void; - const promise = new Promise(resolve => promiseResolve = resolve); + const promise = new Promise(resolve => promiseResolve = resolve); - this.withWindowProgress({ + this.withWindowProgress({ location: ProgressLocation.Window, title: options.title ? parseLinkedText(options.title).toString() : undefined, // convert markdown links => string command: 'notifications.showList' diff --git a/src/vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService.ts b/src/vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService.ts index 067eae83070..479d04fbade 100644 --- a/src/vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService.ts +++ b/src/vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService.ts @@ -307,7 +307,7 @@ export abstract class AbstractWorkspaceEditingService implements IWorkspaceEditi ); } - abstract async enterWorkspace(path: URI): Promise; + abstract enterWorkspace(path: URI): Promise; protected async doEnterWorkspace(path: URI): Promise { if (!!this.environmentService.extensionTestsLocationURI) { From 76e7cbb0dabcfbea50d1c97178215662d9aff119 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 10 Sep 2020 08:53:24 +0200 Subject: [PATCH 71/71] fix some TS 4.1 errors (#106358) --- src/vs/base/node/terminalEncoding.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/base/node/terminalEncoding.ts b/src/vs/base/node/terminalEncoding.ts index fdfc268c48a..32ff35ea028 100644 --- a/src/vs/base/node/terminalEncoding.ts +++ b/src/vs/base/node/terminalEncoding.ts @@ -40,7 +40,7 @@ const JSCHARDET_TO_ICONV_ENCODINGS: { [name: string]: string } = { const UTF8 = 'utf8'; export async function resolveTerminalEncoding(verbose?: boolean): Promise { - let rawEncodingPromise: Promise; + let rawEncodingPromise: Promise; // Support a global environment variable to win over other mechanics const cliEncodingEnv = process.env['VSCODE_CLI_ENCODING']; @@ -54,7 +54,7 @@ export async function resolveTerminalEncoding(verbose?: boolean): Promise(resolve => { + rawEncodingPromise = new Promise(resolve => { if (verbose) { console.log('Running "chcp" to detect terminal encoding...'); }