diff --git a/build/package.json b/build/package.json
index 23d1948000..7e813a381b 100644
--- a/build/package.json
+++ b/build/package.json
@@ -49,7 +49,7 @@
"rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-node-resolve": "^5.2.0",
"terser": "4.3.8",
- "typescript": "^4.0.0-dev.20200715",
+ "typescript": "^4.0.0-dev.20200722",
"vsce": "1.48.0",
"vscode-telemetry-extractor": "^1.6.0",
"xml2js": "^0.4.17"
diff --git a/build/yarn.lock b/build/yarn.lock
index 01a79de5d4..c1d6b399dd 100644
--- a/build/yarn.lock
+++ b/build/yarn.lock
@@ -3539,10 +3539,10 @@ typescript@^3.0.1:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977"
integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==
-typescript@^4.0.0-dev.20200715:
- version "4.0.0-dev.20200715"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.0-dev.20200715.tgz#d65961a5a6f13fde95a6f4db5f5946f15e4c59bc"
- integrity sha512-gmPXoWktfXeutmWTM6el9U4vIn5kqOHGI1OESSOhPtLWrxodKqLfFuMygQtOUTtGjKLFQRFAJhHEwUhHZNOURA==
+typescript@^4.0.0-dev.20200722:
+ version "4.0.0-dev.20200722"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.0-dev.20200722.tgz#b59dd5a3cd84a98d5aae0e4f3a3c58f0c81a3b9b"
+ integrity sha512-MmJ1YyPNK3JYeKLiTg5sQXdeZaMgt99Fg4BMRZhJmhoq1/x2V1cpXMYvE1rtIYl9K7NvmTDdU3WDW7ZOD6ybaw==
typical@^4.0.0:
version "4.0.0"
diff --git a/extensions/git/package.json b/extensions/git/package.json
index e96ef36aba..d9df45a10b 100644
--- a/extensions/git/package.json
+++ b/extensions/git/package.json
@@ -1801,7 +1801,7 @@
"Ignore",
"ignore"
],
- "filenames": [
+ "extensions": [
".gitignore_global",
".gitignore"
],
diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts
index 8102413024..16ac8a51cc 100644
--- a/extensions/git/src/main.ts
+++ b/extensions/git/src/main.ts
@@ -74,7 +74,7 @@ async function createModel(context: ExtensionContext, outputChannel: OutputChann
new GitTimelineProvider(model)
);
- // await checkGitVersion(info); {{SQL CARBON EDIT}} Don't check git version
+ // checkGitVersion(info); {{SQL CARBON EDIT}} Don't check git version
return model;
}
diff --git a/extensions/package.json b/extensions/package.json
index 7c668c9744..69f5ea275b 100644
--- a/extensions/package.json
+++ b/extensions/package.json
@@ -3,7 +3,7 @@
"version": "0.0.1",
"description": "Dependencies shared by all extensions",
"dependencies": {
- "typescript": "3.9.6"
+ "typescript": "3.9.7"
},
"scripts": {
"postinstall": "node ./postinstall"
diff --git a/extensions/yarn.lock b/extensions/yarn.lock
index d41a4ab48b..86223e7721 100644
--- a/extensions/yarn.lock
+++ b/extensions/yarn.lock
@@ -76,10 +76,10 @@ rimraf@^3.0.2:
dependencies:
glob "^7.1.3"
-typescript@3.9.6:
- version "3.9.6"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.6.tgz#8f3e0198a34c3ae17091b35571d3afd31999365a"
- integrity sha512-Pspx3oKAPJtjNwE92YS05HQoY7z2SFyOpHo9MqJor3BXAGNaPUs83CuVp9VISFkSjyRfiTpmKuAYGJB7S7hOxw==
+typescript@3.9.7:
+ version "3.9.7"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa"
+ integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==
wrappy@1:
version "1.0.2"
diff --git a/scripts/test-integration.bat b/scripts/test-integration.bat
index 1396749802..52513c6c22 100755
--- a/scripts/test-integration.bat
+++ b/scripts/test-integration.bat
@@ -37,9 +37,6 @@ if "%INTEGRATION_TEST_ELECTRON_PATH%"=="" (
:: Tests in the extension host
-REM call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-notebook-tests\test --enable-proposed-api=vscode.vscode-notebook-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-notebook-tests --extensionTestsPath=%~dp0\..\extensions\vscode-notebook-tests\out --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR%
-REM if %errorlevel% neq 0 exit /b %errorlevel%
-
REM call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-api-tests\testWorkspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\singlefolder-tests --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR%
REM if %errorlevel% neq 0 exit /b %errorlevel%
@@ -61,6 +58,9 @@ REM if %errorlevel% neq 0 exit /b %errorlevel%
call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\azurecore\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\azurecore --extensionTestsPath=%~dp0\..\extensions\azurecore\out\test --no-cached-data --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR%
if %errorlevel% neq 0 exit /b %errorlevel%
+REM call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-notebook-tests\test --enable-proposed-api=vscode.vscode-notebook-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-notebook-tests --extensionTestsPath=%~dp0\..\extensions\vscode-notebook-tests\out --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR%
+REM if %errorlevel% neq 0 exit /b %errorlevel%
+
for /f "delims=" %%i in ('node -p "require('fs').realpathSync.native(require('os').tmpdir())"') do set TEMPDIR=%%i
set GITWORKSPACE=%TEMPDIR%\git-%RANDOM%
mkdir %GITWORKSPACE%
diff --git a/scripts/test-integration.sh b/scripts/test-integration.sh
index d1932f47e8..d188ed7dff 100755
--- a/scripts/test-integration.sh
+++ b/scripts/test-integration.sh
@@ -48,7 +48,7 @@ fi
# "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/typescript-language-features/test-workspace --extensionDevelopmentPath=$ROOT/extensions/typescript-language-features --extensionTestsPath=$ROOT/extensions/typescript-language-features/out/test --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR
# "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/emmet/out/test/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR
"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $(mktemp -d 2>/dev/null) --enable-proposed-api=vscode.git --extensionDevelopmentPath=$ROOT/extensions/git --extensionTestsPath=$ROOT/extensions/git/out/test --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR
-# "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-notebook-tests/test --enable-proposed-api=vscode.vscode-notebook-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-notebook-tests --extensionTestsPath=$ROOT/extensions/vscode-notebook-tests/out/ --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR
+"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-notebook-tests/test --enable-proposed-api=vscode.vscode-notebook-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-notebook-tests --extensionTestsPath=$ROOT/extensions/vscode-notebook-tests/out/ --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR
"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/azurecore/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/azurecore --extensionTestsPath=$ROOT/extensions/azurecore/out/test --no-cached-data --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR
diff --git a/src/vs/base/browser/contextmenu.ts b/src/vs/base/browser/contextmenu.ts
index 5e61d17445..5cd028f8c5 100644
--- a/src/vs/base/browser/contextmenu.ts
+++ b/src/vs/base/browser/contextmenu.ts
@@ -34,5 +34,5 @@ export interface IContextMenuDelegate {
actionRunner?: IActionRunner;
autoSelectFirstItem?: boolean;
anchorAlignment?: AnchorAlignment;
- anchorAsContainer?: boolean;
+ domForShadowRoot?: HTMLElement;
}
diff --git a/src/vs/base/browser/ui/actionbar/actionbar.css b/src/vs/base/browser/ui/actionbar/actionbar.css
index da795d4656..04fa9dc01e 100644
--- a/src/vs/base/browser/ui/actionbar/actionbar.css
+++ b/src/vs/base/browser/ui/actionbar/actionbar.css
@@ -5,7 +5,6 @@
.monaco-action-bar {
text-align: right;
- overflow: hidden;
white-space: nowrap;
}
diff --git a/src/vs/base/browser/ui/codicons/codiconStyles.ts b/src/vs/base/browser/ui/codicons/codiconStyles.ts
index 1fdeaa6f07..274e9de74c 100644
--- a/src/vs/base/browser/ui/codicons/codiconStyles.ts
+++ b/src/vs/base/browser/ui/codicons/codiconStyles.ts
@@ -28,7 +28,7 @@ function initialize() {
delayer.schedule();
}
-function formatRule(c: Codicon) {
+export function formatRule(c: Codicon) {
let def = c.definition;
while (def instanceof Codicon) {
def = def.definition;
diff --git a/src/vs/base/browser/ui/contextview/contextview.ts b/src/vs/base/browser/ui/contextview/contextview.ts
index b09b736630..f6b1bae867 100644
--- a/src/vs/base/browser/ui/contextview/contextview.ts
+++ b/src/vs/base/browser/ui/contextview/contextview.ts
@@ -10,6 +10,12 @@ import { IDisposable, toDisposable, Disposable, DisposableStore } from 'vs/base/
import { Range } from 'vs/base/common/range';
import { BrowserFeatures } from 'vs/base/browser/canIUse';
+export const enum ContextViewDOMPosition {
+ ABSOLUTE = 1,
+ FIXED,
+ FIXED_SHADOW
+}
+
export interface IAnchor {
x: number;
y: number;
@@ -105,32 +111,62 @@ export class ContextView extends Disposable {
private container: HTMLElement | null = null;
private view: HTMLElement;
private useFixedPosition: boolean;
+ private useShadowDOM: boolean;
private delegate: IDelegate | null = null;
private toDisposeOnClean: IDisposable = Disposable.None;
private toDisposeOnSetContainer: IDisposable = Disposable.None;
+ private shadowRoot: ShadowRoot | null = null;
+ private shadowRootHostElement: HTMLElement | null = null;
- constructor(container: HTMLElement, useFixedPosition: boolean) {
+ constructor(container: HTMLElement, domPosition: ContextViewDOMPosition) {
super();
this.view = DOM.$('.context-view');
this.useFixedPosition = false;
+ this.useShadowDOM = false;
DOM.hide(this.view);
- this.setContainer(container, useFixedPosition);
+ this.setContainer(container, domPosition);
- this._register(toDisposable(() => this.setContainer(null, false)));
+ this._register(toDisposable(() => this.setContainer(null, ContextViewDOMPosition.ABSOLUTE)));
}
- setContainer(container: HTMLElement | null, useFixedPosition: boolean): void {
+ setContainer(container: HTMLElement | null, domPosition: ContextViewDOMPosition): void {
if (this.container) {
this.toDisposeOnSetContainer.dispose();
- this.container.removeChild(this.view);
+
+ if (this.shadowRoot) {
+ this.shadowRoot.removeChild(this.view);
+ this.shadowRoot = null;
+ DOM.removeNode(this.shadowRootHostElement!);
+ this.shadowRootHostElement = null;
+ } else {
+ this.container.removeChild(this.view);
+ }
+
this.container = null;
}
if (container) {
this.container = container;
- this.container.appendChild(this.view);
+
+ this.useFixedPosition = domPosition !== ContextViewDOMPosition.ABSOLUTE;
+ this.useShadowDOM = domPosition === ContextViewDOMPosition.FIXED_SHADOW;
+
+ if (this.useShadowDOM) {
+ this.shadowRootHostElement = DOM.$('.shadow-root-host');
+ this.container.appendChild(this.shadowRootHostElement);
+ this.shadowRoot = this.shadowRootHostElement.attachShadow({ mode: 'closed' });
+ this.shadowRoot.innerHTML = `
+
+ `;
+ this.shadowRoot.appendChild(this.view);
+ this.shadowRoot.appendChild(DOM.$('slot'));
+ } else {
+ this.container.appendChild(this.view);
+ }
const toDisposeOnSetContainer = new DisposableStore();
@@ -148,8 +184,6 @@ export class ContextView extends Disposable {
this.toDisposeOnSetContainer = toDisposeOnSetContainer;
}
-
- this.useFixedPosition = useFixedPosition;
}
show(delegate: IDelegate): void {
@@ -162,6 +196,7 @@ export class ContextView extends Disposable {
this.view.className = 'context-view';
this.view.style.top = '0px';
this.view.style.left = '0px';
+ this.view.style.zIndex = '2500';
this.view.style.position = this.useFixedPosition ? 'fixed' : 'absolute';
DOM.show(this.view);
@@ -301,3 +336,45 @@ export class ContextView extends Disposable {
super.dispose();
}
}
+
+let SHADOW_ROOT_CSS = /* css */ `
+ :host {
+ all: initial; /* 1st rule so subsequent properties are reset. */
+ }
+
+ @font-face {
+ font-family: "codicon";
+ src: url("./codicon.ttf?5d4d76ab2ce5108968ad644d591a16a6") format("truetype");
+ }
+
+ .codicon[class*='codicon-'] {
+ font: normal normal normal 16px/1 codicon;
+ display: inline-block;
+ text-decoration: none;
+ text-rendering: auto;
+ text-align: center;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ user-select: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ }
+
+ :host-context(.mac) { font-family: -apple-system, BlinkMacSystemFont, sans-serif; }
+ :host-context(.mac:lang(zh-Hans)) { font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB", sans-serif; }
+ :host-context(.mac:lang(zh-Hant)) { font-family: -apple-system, BlinkMacSystemFont, "PingFang TC", sans-serif; }
+ :host-context(.mac:lang(ja)) { font-family: -apple-system, BlinkMacSystemFont, "Hiragino Kaku Gothic Pro", sans-serif; }
+ :host-context(.mac:lang(ko)) { font-family: -apple-system, BlinkMacSystemFont, "Nanum Gothic", "Apple SD Gothic Neo", "AppleGothic", sans-serif; }
+
+ :host-context(.windows) { font-family: "Segoe WPC", "Segoe UI", sans-serif; }
+ :host-context(.windows:lang(zh-Hans)) { font-family: "Segoe WPC", "Segoe UI", "Microsoft YaHei", sans-serif; }
+ :host-context(.windows:lang(zh-Hant)) { font-family: "Segoe WPC", "Segoe UI", "Microsoft Jhenghei", sans-serif; }
+ :host-context(.windows:lang(ja)) { font-family: "Segoe WPC", "Segoe UI", "Yu Gothic UI", "Meiryo UI", sans-serif; }
+ :host-context(.windows:lang(ko)) { font-family: "Segoe WPC", "Segoe UI", "Malgun Gothic", "Dotom", sans-serif; }
+
+ :host-context(.mac).linux) { font-family: system-ui, "Ubuntu", "Droid Sans", sans-serif; }
+ :host-context(.mac).linux:lang(zh-Hans)) { font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans SC", "Source Han Sans CN", "Source Han Sans", sans-serif; }
+ :host-context(.mac).linux:lang(zh-Hant)) { font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans TC", "Source Han Sans TW", "Source Han Sans", sans-serif; }
+ :host-context(.mac).linux:lang(ja)) { font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans J", "Source Han Sans JP", "Source Han Sans", sans-serif; }
+ :host-context(.mac).linux:lang(ko)) { font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans K", "Source Han Sans JR", "Source Han Sans", "UnDotum", "FBaekmuk Gulim", sans-serif; }
+`;
diff --git a/src/vs/base/browser/ui/dropdown/dropdown.ts b/src/vs/base/browser/ui/dropdown/dropdown.ts
index 725ce0a706..b9c33e6506 100644
--- a/src/vs/base/browser/ui/dropdown/dropdown.ts
+++ b/src/vs/base/browser/ui/dropdown/dropdown.ts
@@ -270,7 +270,7 @@ export class DropdownMenu extends BaseDropdown {
onHide: () => this.onHide(),
actionRunner: this.menuOptions ? this.menuOptions.actionRunner : undefined,
anchorAlignment: this.menuOptions ? this.menuOptions.anchorAlignment : AnchorAlignment.LEFT,
- anchorAsContainer: this.menuAsChild
+ domForShadowRoot: this.menuAsChild ? this.element : undefined
});
}
@@ -297,15 +297,17 @@ export class DropdownMenuActionViewItem extends BaseActionViewItem {
private _onDidChangeVisibility = this._register(new Emitter());
readonly onDidChangeVisibility = this._onDidChangeVisibility.event;
- constructor(action: IAction, menuActions: ReadonlyArray, contextMenuProvider: IContextMenuProvider, actionViewItemProvider: IActionViewItemProvider | undefined, actionRunner: IActionRunner, keybindings: ((action: IAction) => ResolvedKeybinding | undefined) | undefined, clazz: string | undefined, anchorAlignmentProvider?: () => AnchorAlignment, menuAsChild?: boolean);
- constructor(action: IAction, actionProvider: IActionProvider, contextMenuProvider: IContextMenuProvider, actionViewItemProvider: IActionViewItemProvider | undefined, actionRunner: IActionRunner, keybindings: ((action: IAction) => ResolvedKeybinding) | undefined, clazz: string | undefined, anchorAlignmentProvider?: () => AnchorAlignment, menuAsChild?: boolean);
- constructor(action: IAction, menuActionsOrProvider: ReadonlyArray | IActionProvider, contextMenuProvider: IContextMenuProvider, actionViewItemProvider: IActionViewItemProvider | undefined, actionRunner: IActionRunner, keybindings: ((action: IAction) => ResolvedKeybinding | undefined) | undefined, clazz: string | undefined, anchorAlignmentProvider?: () => AnchorAlignment, menuAsChild?: boolean) {
+ constructor(action: IAction, menuActions: ReadonlyArray, contextMenuProvider: IContextMenuProvider, actionViewItemProvider: IActionViewItemProvider | undefined, actionRunner: IActionRunner | undefined, keybindings: ((action: IAction) => ResolvedKeybinding | undefined) | undefined, clazz: string | undefined, anchorAlignmentProvider?: () => AnchorAlignment, menuAsChild?: boolean);
+ constructor(action: IAction, actionProvider: IActionProvider, contextMenuProvider: IContextMenuProvider, actionViewItemProvider: IActionViewItemProvider | undefined, actionRunner: IActionRunner | undefined, keybindings: ((action: IAction) => ResolvedKeybinding) | undefined, clazz: string | undefined, anchorAlignmentProvider?: () => AnchorAlignment, menuAsChild?: boolean);
+ constructor(action: IAction, menuActionsOrProvider: ReadonlyArray | IActionProvider, contextMenuProvider: IContextMenuProvider, actionViewItemProvider: IActionViewItemProvider | undefined, actionRunner: IActionRunner | undefined, keybindings: ((action: IAction) => ResolvedKeybinding | undefined) | undefined, clazz: string | undefined, anchorAlignmentProvider?: () => AnchorAlignment, menuAsChild?: boolean) {
super(null, action);
this.menuActionsOrProvider = menuActionsOrProvider;
this.contextMenuProvider = contextMenuProvider;
this.actionViewItemProvider = actionViewItemProvider;
- this.actionRunner = actionRunner;
+ if (actionRunner) {
+ this.actionRunner = actionRunner;
+ }
this.keybindings = keybindings;
this.clazz = clazz;
this.anchorAlignmentProvider = anchorAlignmentProvider;
diff --git a/src/vs/base/browser/ui/menu/menu.css b/src/vs/base/browser/ui/menu/menu.css
deleted file mode 100644
index de8e55b969..0000000000
--- a/src/vs/base/browser/ui/menu/menu.css
+++ /dev/null
@@ -1,225 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the Source EULA. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-.monaco-menu .monaco-action-bar.vertical {
- margin-left: 0;
- overflow: visible;
-}
-
-.monaco-menu .monaco-action-bar.vertical .actions-container {
- display: block;
-}
-
-.monaco-menu .monaco-action-bar.vertical .action-item {
- padding: 0;
- transform: none;
- display: flex;
-}
-
-.monaco-menu .monaco-action-bar.vertical .action-item.active {
- transform: none;
-}
-
-.monaco-menu .monaco-action-bar.vertical .action-menu-item {
- flex: 1 1 auto;
- display: flex;
- height: 2em;
- align-items: center;
- position: relative;
-}
-
-.monaco-menu .monaco-action-bar.vertical .action-label {
- flex: 1 1 auto;
- text-decoration: none;
- padding: 0 1em;
- background: none;
- font-size: 12px;
- line-height: 1;
-}
-
-.monaco-menu .monaco-action-bar.vertical .keybinding,
-.monaco-menu .monaco-action-bar.vertical .submenu-indicator {
- display: inline-block;
- flex: 2 1 auto;
- padding: 0 1em;
- text-align: right;
- font-size: 12px;
- line-height: 1;
-}
-
-.monaco-menu .monaco-action-bar.vertical .submenu-indicator {
- height: 100%;
-}
-
-.monaco-menu .monaco-action-bar.vertical .submenu-indicator.codicon {
- font-size: 16px !important;
- display: flex;
- align-items: center;
-}
-
-.monaco-menu .monaco-action-bar.vertical .submenu-indicator.codicon::before {
- margin-left: auto;
- margin-right: -20px;
-}
-
-.monaco-menu .monaco-action-bar.vertical .action-item.disabled .keybinding,
-.monaco-menu .monaco-action-bar.vertical .action-item.disabled .submenu-indicator {
- opacity: 0.4;
-}
-
-.monaco-menu .monaco-action-bar.vertical .action-label:not(.separator) {
- display: inline-block;
- box-sizing: border-box;
- margin: 0;
-}
-
-.monaco-menu .monaco-action-bar.vertical .action-item {
- position: static;
- overflow: visible;
-}
-
-.monaco-menu .monaco-action-bar.vertical .action-item .monaco-submenu {
- position: absolute;
-}
-
-.monaco-menu .monaco-action-bar.vertical .action-label.separator {
- padding: 0.5em 0 0 0;
- margin-bottom: 0.5em;
- width: 100%;
- height: 0px !important;
- margin-left: .8em !important;
- margin-right: .8em !important;
-}
-
-.monaco-menu .monaco-action-bar.vertical .action-label.separator.text {
- padding: 0.7em 1em 0.1em 1em;
- font-weight: bold;
- opacity: 1;
-}
-
-.monaco-menu .monaco-action-bar.vertical .action-label:hover {
- color: inherit;
-}
-
-.monaco-menu .monaco-action-bar.vertical .menu-item-check {
- position: absolute;
- visibility: hidden;
- width: 1em;
- height: 100%;
-}
-
-.monaco-menu .monaco-action-bar.vertical .action-menu-item.checked .menu-item-check {
- visibility: visible;
- display: flex;
- align-items: center;
- justify-content: center;
-}
-
-/* Context Menu */
-
-.context-view.monaco-menu-container {
- outline: 0;
- border: none;
- animation: fadeIn 0.083s linear;
-}
-
-.context-view.monaco-menu-container :focus,
-.context-view.monaco-menu-container .monaco-action-bar.vertical:focus,
-.context-view.monaco-menu-container .monaco-action-bar.vertical :focus {
- outline: 0;
-}
-
-.monaco-menu .monaco-action-bar.vertical .action-item {
- border: thin solid transparent; /* prevents jumping behaviour on hover or focus */
-}
-
-
-/* High Contrast Theming */
-.hc-black .context-view.monaco-menu-container {
- box-shadow: none;
-}
-
-.hc-black .monaco-menu .monaco-action-bar.vertical .action-item.focused {
- background: none;
-}
-
-/* Menubar styles */
-
-.menubar {
- display: flex;
- flex-shrink: 1;
- box-sizing: border-box;
- height: 30px;
- overflow: hidden;
- flex-wrap: wrap;
-}
-
-.fullscreen .menubar:not(.compact) {
- margin: 0px;
- padding: 0px 5px;
-}
-
-.menubar > .menubar-menu-button {
- align-items: center;
- box-sizing: border-box;
- padding: 0px 8px;
- cursor: default;
- -webkit-app-region: no-drag;
- zoom: 1;
- white-space: nowrap;
- outline: 0;
-}
-
-.menubar.compact {
- flex-shrink: 0;
- overflow: visible; /* to avoid the compact menu to be repositioned when clicking */
-}
-
-.menubar.compact > .menubar-menu-button {
- width: 100%;
- height: 100%;
- padding: 0px;
-}
-
-.menubar .menubar-menu-items-holder {
- position: absolute;
- left: 0px;
- opacity: 1;
- z-index: 2000;
-}
-
-.menubar .menubar-menu-items-holder.monaco-menu-container {
- outline: 0;
- border: none;
-}
-
-.menubar .menubar-menu-items-holder.monaco-menu-container :focus {
- outline: 0;
-}
-
-.menubar .toolbar-toggle-more {
- width: 20px;
- height: 100%;
-}
-
-.menubar.compact .toolbar-toggle-more {
- position: relative;
- left: 0px;
- top: 0px;
- cursor: pointer;
- width: 100%;
- display: flex;
- align-items: center;
- justify-content: center;
-}
-
-.menubar .toolbar-toggle-more {
- padding: 0;
- vertical-align: sub;
-}
-
-.menubar.compact .toolbar-toggle-more::before {
- content: "\eb94" !important;
-}
diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts
index aa54df4c43..4035da836c 100644
--- a/src/vs/base/browser/ui/menu/menu.ts
+++ b/src/vs/base/browser/ui/menu/menu.ts
@@ -3,13 +3,12 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import 'vs/css!./menu';
import * as nls from 'vs/nls';
import * as strings from 'vs/base/common/strings';
import { IActionRunner, IAction, Action } from 'vs/base/common/actions';
import { ActionBar, IActionViewItemProvider, ActionsOrientation, Separator, ActionViewItem, IActionViewItemOptions, BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { ResolvedKeybinding, KeyCode } from 'vs/base/common/keyCodes';
-import { addClass, EventType, EventHelper, EventLike, removeTabIndexAndUpdateFocus, isAncestor, hasClass, addDisposableListener, removeClass, append, $, addClasses, removeClasses, clearNode } from 'vs/base/browser/dom';
+import { addClass, EventType, EventHelper, EventLike, removeTabIndexAndUpdateFocus, isAncestor, hasClass, addDisposableListener, removeClass, append, $, addClasses, removeClasses, clearNode, createStyleSheet, isInShadowDOM } from 'vs/base/browser/dom';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { RunOnceScheduler } from 'vs/base/common/async';
import { DisposableStore } from 'vs/base/common/lifecycle';
@@ -20,6 +19,7 @@ import { Event } from 'vs/base/common/event';
import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview';
import { isLinux, isMacintosh } from 'vs/base/common/platform';
import { Codicon, registerIcon, stripCodicons } from 'vs/base/common/codicons';
+import { formatRule } from 'vs/base/browser/ui/codicons/codiconStyles';
export const MENU_MNEMONIC_REGEX = /\(&([^\s&])\)|(^|[^&])&([^\s&])/;
export const MENU_ESCAPED_MNEMONIC_REGEX = /(&)?(&)([^\s&])/g;
@@ -71,6 +71,8 @@ export class Menu extends ActionBar {
private readonly menuDisposables: DisposableStore;
private scrollableElement: DomScrollableElement;
private menuElement: HTMLElement;
+ static globalStyleSheet: HTMLStyleElement;
+ protected styleSheet: HTMLStyleElement | undefined;
constructor(container: HTMLElement, actions: ReadonlyArray, options: IMenuOptions = {}) {
addClass(container, 'monaco-menu-container');
@@ -96,6 +98,8 @@ export class Menu extends ActionBar {
this.menuDisposables = this._register(new DisposableStore());
+ this.initializeStyleSheet(container);
+
addDisposableListener(menuElement, EventType.KEY_DOWN, (e) => {
const event = new StandardKeyboardEvent(e);
@@ -215,6 +219,20 @@ export class Menu extends ActionBar {
});
}
+ private initializeStyleSheet(container: HTMLElement): void {
+ if (isInShadowDOM(container)) {
+ this.styleSheet = createStyleSheet(container);
+ this.styleSheet.innerHTML = MENU_WIDGET_CSS;
+ } else {
+ if (!Menu.globalStyleSheet) {
+ Menu.globalStyleSheet = createStyleSheet();
+ Menu.globalStyleSheet.innerHTML = MENU_WIDGET_CSS;
+ }
+
+ this.styleSheet = Menu.globalStyleSheet;
+ }
+ }
+
style(style: IMenuStyles): void {
const container = this.getContainer();
@@ -877,3 +895,298 @@ export function cleanMnemonic(label: string): string {
return label.replace(regex, mnemonicInText ? '$2$3' : '').trim();
}
+
+let MENU_WIDGET_CSS: string = /* css */`
+.monaco-menu {
+ font-size: 13px;
+
+}
+
+${formatRule(menuSelectionIcon)}
+${formatRule(menuSubmenuIcon)}
+
+.monaco-action-bar {
+ text-align: right;
+ overflow: hidden;
+ white-space: nowrap;
+}
+
+.monaco-action-bar .actions-container {
+ display: flex;
+ margin: 0 auto;
+ padding: 0;
+ width: 100%;
+ justify-content: flex-end;
+}
+
+.monaco-action-bar.vertical .actions-container {
+ display: inline-block;
+}
+
+.monaco-action-bar.reverse .actions-container {
+ flex-direction: row-reverse;
+}
+
+.monaco-action-bar .action-item {
+ cursor: pointer;
+ display: inline-block;
+ transition: transform 50ms ease;
+ position: relative; /* DO NOT REMOVE - this is the key to preventing the ghosting icon bug in Chrome 42 */
+}
+
+.monaco-action-bar .action-item.disabled {
+ cursor: default;
+}
+
+.monaco-action-bar.animated .action-item.active {
+ transform: scale(1.272019649, 1.272019649); /* 1.272019649 = √φ */
+}
+
+.monaco-action-bar .action-item .icon,
+.monaco-action-bar .action-item .codicon {
+ display: inline-block;
+}
+
+.monaco-action-bar .action-item .codicon {
+ display: flex;
+ align-items: center;
+}
+
+.monaco-action-bar .action-label {
+ font-size: 11px;
+ margin-right: 4px;
+}
+
+.monaco-action-bar .action-item.disabled .action-label,
+.monaco-action-bar .action-item.disabled .action-label:hover {
+ opacity: 0.4;
+}
+
+/* Vertical actions */
+
+.monaco-action-bar.vertical {
+ text-align: left;
+}
+
+.monaco-action-bar.vertical .action-item {
+ display: block;
+}
+
+.monaco-action-bar.vertical .action-label.separator {
+ display: block;
+ border-bottom: 1px solid #bbb;
+ padding-top: 1px;
+ margin-left: .8em;
+ margin-right: .8em;
+}
+
+.monaco-action-bar.animated.vertical .action-item.active {
+ transform: translate(5px, 0);
+}
+
+.secondary-actions .monaco-action-bar .action-label {
+ margin-left: 6px;
+}
+
+/* Action Items */
+.monaco-action-bar .action-item.select-container {
+ overflow: hidden; /* somehow the dropdown overflows its container, we prevent it here to not push */
+ flex: 1;
+ max-width: 170px;
+ min-width: 60px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-right: 10px;
+}
+
+.monaco-menu .monaco-action-bar.vertical {
+ margin-left: 0;
+ overflow: visible;
+}
+
+.monaco-menu .monaco-action-bar.vertical .actions-container {
+ display: block;
+}
+
+.monaco-menu .monaco-action-bar.vertical .action-item {
+ padding: 0;
+ transform: none;
+ display: flex;
+}
+
+.monaco-menu .monaco-action-bar.vertical .action-item.active {
+ transform: none;
+}
+
+.monaco-menu .monaco-action-bar.vertical .action-menu-item {
+ flex: 1 1 auto;
+ display: flex;
+ height: 2em;
+ align-items: center;
+ position: relative;
+}
+
+.monaco-menu .monaco-action-bar.vertical .action-label {
+ flex: 1 1 auto;
+ text-decoration: none;
+ padding: 0 1em;
+ background: none;
+ font-size: 12px;
+ line-height: 1;
+}
+
+.monaco-menu .monaco-action-bar.vertical .keybinding,
+.monaco-menu .monaco-action-bar.vertical .submenu-indicator {
+ display: inline-block;
+ flex: 2 1 auto;
+ padding: 0 1em;
+ text-align: right;
+ font-size: 12px;
+ line-height: 1;
+}
+
+.monaco-menu .monaco-action-bar.vertical .submenu-indicator {
+ height: 100%;
+}
+
+.monaco-menu .monaco-action-bar.vertical .submenu-indicator.codicon {
+ font-size: 16px !important;
+ display: flex;
+ align-items: center;
+}
+
+.monaco-menu .monaco-action-bar.vertical .submenu-indicator.codicon::before {
+ margin-left: auto;
+ margin-right: -20px;
+}
+
+.monaco-menu .monaco-action-bar.vertical .action-item.disabled .keybinding,
+.monaco-menu .monaco-action-bar.vertical .action-item.disabled .submenu-indicator {
+ opacity: 0.4;
+}
+
+.monaco-menu .monaco-action-bar.vertical .action-label:not(.separator) {
+ display: inline-block;
+ box-sizing: border-box;
+ margin: 0;
+}
+
+.monaco-menu .monaco-action-bar.vertical .action-item {
+ position: static;
+ overflow: visible;
+}
+
+.monaco-menu .monaco-action-bar.vertical .action-item .monaco-submenu {
+ position: absolute;
+}
+
+.monaco-menu .monaco-action-bar.vertical .action-label.separator {
+ padding: 0.5em 0 0 0;
+ margin-bottom: 0.5em;
+ width: 100%;
+ height: 0px !important;
+ margin-left: .8em !important;
+ margin-right: .8em !important;
+}
+
+.monaco-menu .monaco-action-bar.vertical .action-label.separator.text {
+ padding: 0.7em 1em 0.1em 1em;
+ font-weight: bold;
+ opacity: 1;
+}
+
+.monaco-menu .monaco-action-bar.vertical .action-label:hover {
+ color: inherit;
+}
+
+.monaco-menu .monaco-action-bar.vertical .menu-item-check {
+ position: absolute;
+ visibility: hidden;
+ width: 1em;
+ height: 100%;
+}
+
+.monaco-menu .monaco-action-bar.vertical .action-menu-item.checked .menu-item-check {
+ visibility: visible;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+/* Context Menu */
+
+.context-view.monaco-menu-container {
+ outline: 0;
+ border: none;
+ animation: fadeIn 0.083s linear;
+}
+
+.context-view.monaco-menu-container :focus,
+.context-view.monaco-menu-container .monaco-action-bar.vertical:focus,
+.context-view.monaco-menu-container .monaco-action-bar.vertical :focus {
+ outline: 0;
+}
+
+.monaco-menu .monaco-action-bar.vertical .action-item {
+ border: thin solid transparent; /* prevents jumping behaviour on hover or focus */
+}
+
+
+/* High Contrast Theming */
+.hc-black .context-view.monaco-menu-container {
+ box-shadow: none;
+}
+
+.hc-black .monaco-menu .monaco-action-bar.vertical .action-item.focused {
+ background: none;
+}
+
+/* Vertical Action Bar Styles */
+
+.monaco-menu .monaco-action-bar.vertical {
+ padding: .5em 0;
+}
+
+.monaco-menu .monaco-action-bar.vertical .action-menu-item {
+ height: 1.8em;
+}
+
+.monaco-menu .monaco-action-bar.vertical .action-label:not(.separator),
+.monaco-menu .monaco-action-bar.vertical .keybinding {
+ font-size: inherit;
+ padding: 0 2em;
+}
+
+.monaco-menu .monaco-action-bar.vertical .menu-item-check {
+ font-size: inherit;
+ width: 2em;
+}
+
+.monaco-menu .monaco-action-bar.vertical .action-label.separator {
+ font-size: inherit;
+ padding: 0.2em 0 0 0;
+ margin-bottom: 0.2em;
+}
+
+linux .monaco-menu .monaco-action-bar.vertical .action-label.separator {
+ margin-left: 0;
+ margin-right: 0;
+}
+
+.monaco-menu .monaco-action-bar.vertical .submenu-indicator {
+ font-size: 60%;
+ padding: 0 1.8em;
+}
+
+:host-context(.linux) .monaco-menu .monaco-action-bar.vertical .submenu-indicator {
+ height: 100%;
+ mask-size: 10px 10px;
+ -webkit-mask-size: 10px 10px;
+}
+
+.monaco-menu .action-item {
+ cursor: default;
+}
+
+`;
diff --git a/src/vs/base/browser/ui/menu/menubar.css b/src/vs/base/browser/ui/menu/menubar.css
new file mode 100644
index 0000000000..abd299fa01
--- /dev/null
+++ b/src/vs/base/browser/ui/menu/menubar.css
@@ -0,0 +1,83 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the Source EULA. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+/* Menubar styles */
+
+.menubar {
+ display: flex;
+ flex-shrink: 1;
+ box-sizing: border-box;
+ height: 30px;
+ overflow: hidden;
+ flex-wrap: wrap;
+}
+
+.fullscreen .menubar:not(.compact) {
+ margin: 0px;
+ padding: 0px 5px;
+}
+
+.menubar > .menubar-menu-button {
+ align-items: center;
+ box-sizing: border-box;
+ padding: 0px 8px;
+ cursor: default;
+ -webkit-app-region: no-drag;
+ zoom: 1;
+ white-space: nowrap;
+ outline: 0;
+}
+
+.menubar.compact {
+ flex-shrink: 0;
+ overflow: visible; /* to avoid the compact menu to be repositioned when clicking */
+}
+
+.menubar.compact > .menubar-menu-button {
+ width: 100%;
+ height: 100%;
+ padding: 0px;
+}
+
+.menubar .menubar-menu-items-holder {
+ position: absolute;
+ left: 0px;
+ opacity: 1;
+ z-index: 2000;
+}
+
+.menubar .menubar-menu-items-holder.monaco-menu-container {
+ outline: 0;
+ border: none;
+}
+
+.menubar .menubar-menu-items-holder.monaco-menu-container :focus {
+ outline: 0;
+}
+
+.menubar .toolbar-toggle-more {
+ width: 20px;
+ height: 100%;
+}
+
+.menubar.compact .toolbar-toggle-more {
+ position: relative;
+ left: 0px;
+ top: 0px;
+ cursor: pointer;
+ width: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.menubar .toolbar-toggle-more {
+ padding: 0;
+ vertical-align: sub;
+}
+
+.menubar.compact .toolbar-toggle-more::before {
+ content: "\eb94" !important;
+}
diff --git a/src/vs/base/browser/ui/menu/menubar.ts b/src/vs/base/browser/ui/menu/menubar.ts
index bf14a48e8c..7fa6a5984f 100644
--- a/src/vs/base/browser/ui/menu/menubar.ts
+++ b/src/vs/base/browser/ui/menu/menubar.ts
@@ -3,6 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
+import 'vs/css!./menubar';
import * as browser from 'vs/base/browser/browser';
import * as DOM from 'vs/base/browser/dom';
import * as strings from 'vs/base/common/strings';
diff --git a/src/vs/base/common/marshalling.ts b/src/vs/base/common/marshalling.ts
index d0c93662e2..d5267bff98 100644
--- a/src/vs/base/common/marshalling.ts
+++ b/src/vs/base/common/marshalling.ts
@@ -3,8 +3,9 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { URI } from 'vs/base/common/uri';
+import { VSBuffer } from 'vs/base/common/buffer';
import { regExpFlags } from 'vs/base/common/strings';
+import { URI } from 'vs/base/common/uri';
export function stringify(obj: any): string {
return JSON.stringify(obj, replacer);
@@ -44,10 +45,23 @@ export function revive(obj: any, depth = 0): any {
case 2: return new RegExp(obj.source, obj.flags);
}
- // walk object (or array)
- for (let key in obj) {
- if (Object.hasOwnProperty.call(obj, key)) {
- obj[key] = revive(obj[key], depth + 1);
+ if (
+ obj instanceof VSBuffer
+ || obj instanceof Uint8Array
+ ) {
+ return obj;
+ }
+
+ if (Array.isArray(obj)) {
+ for (let i = 0; i < obj.length; ++i) {
+ obj[i] = revive(obj[i], depth + 1);
+ }
+ } else {
+ // walk object
+ for (const key in obj) {
+ if (Object.hasOwnProperty.call(obj, key)) {
+ obj[key] = revive(obj[key], depth + 1);
+ }
}
}
}
diff --git a/src/vs/base/parts/ipc/common/ipc.net.ts b/src/vs/base/parts/ipc/common/ipc.net.ts
index effadd30fb..93dde1871c 100644
--- a/src/vs/base/parts/ipc/common/ipc.net.ts
+++ b/src/vs/base/parts/ipc/common/ipc.net.ts
@@ -16,6 +16,7 @@ export interface ISocket extends IDisposable {
onEnd(listener: () => void): IDisposable;
write(buffer: VSBuffer): void;
end(): void;
+ drain(): Promise;
}
let emptyBuffer: VSBuffer | null = null;
@@ -277,6 +278,11 @@ class ProtocolWriter {
this._isDisposed = true;
}
+ public drain(): Promise {
+ this.flush();
+ return this._socket.drain();
+ }
+
public flush(): void {
// flush
this._writeNow();
@@ -372,6 +378,10 @@ export class Protocol extends Disposable implements IMessagePassingProtocol {
this._register(this._socket.onClose(() => this._onClose.fire()));
}
+ drain(): Promise {
+ return this._socketWriter.drain();
+ }
+
getSocket(): ISocket {
return this._socket;
}
@@ -619,6 +629,10 @@ export class PersistentProtocol implements IMessagePassingProtocol {
this._socketDisposables = dispose(this._socketDisposables);
}
+ drain(): Promise {
+ return this._socketWriter.drain();
+ }
+
sendDisconnect(): void {
const msg = new ProtocolMessage(ProtocolMessageType.Disconnect, 0, 0, getEmptyBuffer());
this._socketWriter.write(msg);
diff --git a/src/vs/base/parts/ipc/common/ipc.ts b/src/vs/base/parts/ipc/common/ipc.ts
index 5a37a46ca9..8f21ebee69 100644
--- a/src/vs/base/parts/ipc/common/ipc.ts
+++ b/src/vs/base/parts/ipc/common/ipc.ts
@@ -70,6 +70,10 @@ interface IHandler {
export interface IMessagePassingProtocol {
send(buffer: VSBuffer): void;
onMessage: Event;
+ /**
+ * Wait for the write buffer (if applicable) to become empty.
+ */
+ drain?(): Promise;
}
enum State {
@@ -482,10 +486,7 @@ export class ChannelClient implements IChannelClient, IDisposable {
return e(errors.canceled());
}
- let uninitializedPromise: CancelablePromise | null = createCancelablePromise(_ => this.whenInitialized());
- uninitializedPromise.then(() => {
- uninitializedPromise = null;
-
+ const doRequest = () => {
const handler: IHandler = response => {
switch (response.type) {
case ResponseType.PromiseSuccess:
@@ -510,7 +511,18 @@ export class ChannelClient implements IChannelClient, IDisposable {
this.handlers.set(id, handler);
this.sendRequest(request);
- });
+ };
+
+ let uninitializedPromise: CancelablePromise | null = null;
+ if (this.state === State.Idle) {
+ doRequest();
+ } else {
+ uninitializedPromise = createCancelablePromise(_ => this.whenInitialized());
+ uninitializedPromise.then(() => {
+ uninitializedPromise = null;
+ doRequest();
+ });
+ }
const cancel = () => {
if (uninitializedPromise) {
diff --git a/src/vs/base/parts/ipc/node/ipc.net.ts b/src/vs/base/parts/ipc/node/ipc.net.ts
index 1a96d81731..c9f0140ab6 100644
--- a/src/vs/base/parts/ipc/node/ipc.net.ts
+++ b/src/vs/base/parts/ipc/node/ipc.net.ts
@@ -12,6 +12,7 @@ import { generateUuid } from 'vs/base/common/uuid';
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { VSBuffer } from 'vs/base/common/buffer';
import { ISocket, Protocol, Client, ChunkStream } from 'vs/base/parts/ipc/common/ipc.net';
+import { onUnexpectedError } from 'vs/base/common/errors';
export class NodeSocket implements ISocket {
public readonly socket: Socket;
@@ -57,12 +58,47 @@ export class NodeSocket implements ISocket {
// > https://nodejs.org/api/stream.html#stream_writable_write_chunk_encoding_callback
// > However, the false return value is only advisory and the writable stream will unconditionally
// > accept and buffer chunk even if it has not been allowed to drain.
- this.socket.write(buffer.buffer);
+ try {
+ this.socket.write(buffer.buffer);
+ } catch (err) {
+ if (err.code === 'EPIPE') {
+ // An EPIPE exception at the wrong time can lead to a renderer process crash
+ // so ignore the error since the socket will fire the close event soon anyways:
+ // > https://nodejs.org/api/errors.html#errors_common_system_errors
+ // > EPIPE (Broken pipe): A write on a pipe, socket, or FIFO for which there is no
+ // > process to read the data. Commonly encountered at the net and http layers,
+ // > indicative that the remote side of the stream being written to has been closed.
+ return;
+ }
+ onUnexpectedError(err);
+ }
}
public end(): void {
this.socket.end();
}
+
+ public drain(): Promise {
+ return new Promise((resolve, reject) => {
+ if (this.socket.bufferSize === 0) {
+ resolve();
+ return;
+ }
+ const finished = () => {
+ this.socket.off('close', finished);
+ this.socket.off('end', finished);
+ this.socket.off('error', finished);
+ this.socket.off('timeout', finished);
+ this.socket.off('drain', finished);
+ resolve();
+ };
+ this.socket.on('close', finished);
+ this.socket.on('end', finished);
+ this.socket.on('error', finished);
+ this.socket.on('timeout', finished);
+ this.socket.on('drain', finished);
+ });
+ }
}
const enum Constants {
@@ -229,6 +265,10 @@ export class WebSocketNodeSocket extends Disposable implements ISocket {
}
}
}
+
+ public drain(): Promise {
+ return this.socket.drain();
+ }
}
function unmask(buffer: VSBuffer, mask: number): void {
diff --git a/src/vs/editor/contrib/codeAction/codeActionMenu.ts b/src/vs/editor/contrib/codeAction/codeActionMenu.ts
index a073034b41..9eeb38d33a 100644
--- a/src/vs/editor/contrib/codeAction/codeActionMenu.ts
+++ b/src/vs/editor/contrib/codeAction/codeActionMenu.ts
@@ -90,6 +90,7 @@ export class CodeActionMenu extends Disposable {
const resolver = this._keybindingResolver.getResolver();
this._contextMenuService.showContextMenu({
+ domForShadowRoot: this._editor.getDomNode()!,
getAnchor: () => anchor,
getActions: () => menuActions,
onHide: () => {
diff --git a/src/vs/editor/contrib/contextmenu/contextmenu.ts b/src/vs/editor/contrib/contextmenu/contextmenu.ts
index cc0334ed97..dda6683316 100644
--- a/src/vs/editor/contrib/contextmenu/contextmenu.ts
+++ b/src/vs/editor/contrib/contextmenu/contextmenu.ts
@@ -205,6 +205,8 @@ export class ContextMenuController implements IEditorContribution {
// Show menu
this._contextMenuIsBeingShownCount++;
this._contextMenuService.showContextMenu({
+ domForShadowRoot: this._editor.getDomNode(),
+
getAnchor: () => anchor!,
getActions: () => actions,
diff --git a/src/vs/editor/contrib/dnd/dnd.ts b/src/vs/editor/contrib/dnd/dnd.ts
index f7fc0f3f26..e0b6e69106 100644
--- a/src/vs/editor/contrib/dnd/dnd.ts
+++ b/src/vs/editor/contrib/dnd/dnd.ts
@@ -54,6 +54,7 @@ export class DragAndDropController extends Disposable implements IEditorContribu
this._register(this._editor.onKeyDown((e: IKeyboardEvent) => this.onEditorKeyDown(e)));
this._register(this._editor.onKeyUp((e: IKeyboardEvent) => this.onEditorKeyUp(e)));
this._register(this._editor.onDidBlurEditorWidget(() => this.onEditorBlur()));
+ this._register(this._editor.onDidBlurEditorText(() => this.onEditorBlur()));
this._dndDecorationIds = [];
this._mouseDown = false;
this._modifierPressed = false;
diff --git a/src/vs/editor/contrib/find/findWidget.css b/src/vs/editor/contrib/find/findWidget.css
index 35aef59279..adfec7b508 100644
--- a/src/vs/editor/contrib/find/findWidget.css
+++ b/src/vs/editor/contrib/find/findWidget.css
@@ -6,7 +6,7 @@
/* Find widget */
.monaco-editor .find-widget {
position: absolute;
- z-index: 10;
+ z-index: 20;
height: 33px;
overflow: hidden;
line-height: 19px;
diff --git a/src/vs/platform/contextview/browser/contextMenuHandler.ts b/src/vs/platform/contextview/browser/contextMenuHandler.ts
index d2b63d5f03..ce5b484ce7 100644
--- a/src/vs/platform/contextview/browser/contextMenuHandler.ts
+++ b/src/vs/platform/contextview/browser/contextMenuHandler.ts
@@ -50,7 +50,7 @@ export class ContextMenuHandler {
let menu: Menu | undefined;
- const anchor = delegate.getAnchor();
+ let shadowRootElement = isHTMLElement(delegate.domForShadowRoot) ? delegate.domForShadowRoot : undefined;
this.contextViewService.showContextView({
getAnchor: () => delegate.getAnchor(),
canRelayout: false,
@@ -133,7 +133,7 @@ export class ContextMenuHandler {
this.focusToReturn.focus();
}
}
- }, !!delegate.anchorAsContainer && isHTMLElement(anchor) ? anchor : undefined);
+ }, shadowRootElement, !!shadowRootElement);
}
private onActionRun(e: IRunEvent): void {
diff --git a/src/vs/platform/contextview/browser/contextView.ts b/src/vs/platform/contextview/browser/contextView.ts
index 328916929b..1e907d13b9 100644
--- a/src/vs/platform/contextview/browser/contextView.ts
+++ b/src/vs/platform/contextview/browser/contextView.ts
@@ -15,7 +15,7 @@ export interface IContextViewService extends IContextViewProvider {
readonly _serviceBrand: undefined;
- showContextView(delegate: IContextViewDelegate, container?: HTMLElement): IDisposable;
+ showContextView(delegate: IContextViewDelegate, container?: HTMLElement, shadowRoot?: boolean): IDisposable;
hideContextView(data?: any): void;
layout(): void;
anchorAlignment?: AnchorAlignment;
diff --git a/src/vs/platform/contextview/browser/contextViewService.ts b/src/vs/platform/contextview/browser/contextViewService.ts
index 1875694c0b..6ee3353055 100644
--- a/src/vs/platform/contextview/browser/contextViewService.ts
+++ b/src/vs/platform/contextview/browser/contextViewService.ts
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { IContextViewService, IContextViewDelegate } from './contextView';
-import { ContextView } from 'vs/base/browser/ui/contextview/contextview';
+import { ContextView, ContextViewDOMPosition } from 'vs/base/browser/ui/contextview/contextview';
import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
@@ -21,7 +21,7 @@ export class ContextViewService extends Disposable implements IContextViewServic
super();
this.container = layoutService.container;
- this.contextView = this._register(new ContextView(this.container, false));
+ this.contextView = this._register(new ContextView(this.container, ContextViewDOMPosition.ABSOLUTE));
this.layout();
this._register(layoutService.onLayout(() => this.layout()));
@@ -29,20 +29,20 @@ export class ContextViewService extends Disposable implements IContextViewServic
// ContextView
- setContainer(container: HTMLElement, useFixedPosition?: boolean): void {
- this.contextView.setContainer(container, !!useFixedPosition);
+ setContainer(container: HTMLElement, domPosition?: ContextViewDOMPosition): void {
+ this.contextView.setContainer(container, domPosition || ContextViewDOMPosition.ABSOLUTE);
}
- showContextView(delegate: IContextViewDelegate, container?: HTMLElement): IDisposable {
+ showContextView(delegate: IContextViewDelegate, container?: HTMLElement, shadowRoot?: boolean): IDisposable {
if (container) {
if (container !== this.container) {
this.container = container;
- this.setContainer(container, true);
+ this.setContainer(container, shadowRoot ? ContextViewDOMPosition.FIXED_SHADOW : ContextViewDOMPosition.FIXED);
}
} else {
if (this.container !== this.layoutService.container) {
this.container = this.layoutService.container;
- this.setContainer(this.container, false);
+ this.setContainer(this.container, ContextViewDOMPosition.ABSOLUTE);
}
}
diff --git a/src/vs/platform/extensions/common/extensions.ts b/src/vs/platform/extensions/common/extensions.ts
index b18ed3c7fd..28ca5155ae 100644
--- a/src/vs/platform/extensions/common/extensions.ts
+++ b/src/vs/platform/extensions/common/extensions.ts
@@ -149,8 +149,25 @@ export interface IExtensionIdentifier {
uuid?: string;
}
-export const EXTENSION_CATEGORIES = ['Programming Languages', 'Snippets', 'Linters', 'Themes', 'Debuggers', 'Other', 'Keymaps', 'Formatters', 'Extension Packs',
- 'SCM Providers', 'Azure', 'Language Packs', 'Data Science', 'Machine Learning', 'Visualization', 'Testing', 'Notebooks'];
+export const EXTENSION_CATEGORIES = [
+ 'Azure',
+ 'Data Science',
+ 'Debuggers',
+ 'Extension Packs',
+ 'Formatters',
+ 'Keymaps',
+ 'Language Packs',
+ 'Linters',
+ 'Machine Learning',
+ 'Notebooks',
+ 'Programming Languages',
+ 'SCM Providers',
+ 'Snippets',
+ 'Themes',
+ 'Testing',
+ 'Visualization',
+ 'Other',
+];
export interface IExtensionManifest {
readonly name: string;
diff --git a/src/vs/platform/remote/browser/browserSocketFactory.ts b/src/vs/platform/remote/browser/browserSocketFactory.ts
index ff112be60a..abb47b08d6 100644
--- a/src/vs/platform/remote/browser/browserSocketFactory.ts
+++ b/src/vs/platform/remote/browser/browserSocketFactory.ts
@@ -194,6 +194,9 @@ class BrowserSocket implements ISocket {
this.socket.close();
}
+ public drain(): Promise {
+ return Promise.resolve();
+ }
}
diff --git a/src/vs/platform/undoRedo/common/undoRedoService.ts b/src/vs/platform/undoRedo/common/undoRedoService.ts
index 4431ca936c..67e5d6502f 100644
--- a/src/vs/platform/undoRedo/common/undoRedoService.ts
+++ b/src/vs/platform/undoRedo/common/undoRedoService.ts
@@ -82,10 +82,19 @@ class RemovedResources {
let messages: string[] = [];
if (externalRemoval.length > 0) {
- messages.push(nls.localize('externalRemoval', "The following files have been closed and modified on disk: {0}.", externalRemoval.join(', ')));
+ messages.push(
+ nls.localize(
+ { key: 'externalRemoval', comment: ['{0} is a list of filenames'] },
+ "The following files have been closed and modified on disk: {0}.", externalRemoval.join(', ')
+ )
+ );
}
if (noParallelUniverses.length > 0) {
- messages.push(nls.localize('noParallelUniverses', "The following files have been modified in an incompatible way: {0}.", noParallelUniverses.join(', ')));
+ messages.push(
+ nls.localize(
+ { key: 'noParallelUniverses', comment: ['{0} is a list of filenames'] },
+ "The following files have been modified in an incompatible way: {0}.", noParallelUniverses.join(', ')
+ ));
}
return messages.join('\n');
}
@@ -771,10 +780,26 @@ export class UndoRedoService implements IUndoRedoService {
private _checkWorkspaceUndo(strResource: string, element: WorkspaceStackElement, editStackSnapshot: EditStackSnapshot, checkInvalidatedResources: boolean): WorkspaceVerificationError | null {
if (element.removedResources) {
- return this._tryToSplitAndUndo(strResource, element, element.removedResources, nls.localize('cannotWorkspaceUndo', "Could not undo '{0}' across all files. {1}", element.label, element.removedResources.createMessage()));
+ return this._tryToSplitAndUndo(
+ strResource,
+ element,
+ element.removedResources,
+ nls.localize(
+ { key: 'cannotWorkspaceUndo', comment: ['{0} is a label for an operation. {1} is another message.'] },
+ "Could not undo '{0}' across all files. {1}", element.label, element.removedResources.createMessage()
+ )
+ );
}
if (checkInvalidatedResources && element.invalidatedResources) {
- return this._tryToSplitAndUndo(strResource, element, element.invalidatedResources, nls.localize('cannotWorkspaceUndo', "Could not undo '{0}' across all files. {1}", element.label, element.invalidatedResources.createMessage()));
+ return this._tryToSplitAndUndo(
+ strResource,
+ element,
+ element.invalidatedResources,
+ nls.localize(
+ { key: 'cannotWorkspaceUndo', comment: ['{0} is a label for an operation. {1} is another message.'] },
+ "Could not undo '{0}' across all files. {1}", element.label, element.invalidatedResources.createMessage()
+ )
+ );
}
// this must be the last past element in all the impacted resources!
@@ -785,7 +810,15 @@ export class UndoRedoService implements IUndoRedoService {
}
}
if (cannotUndoDueToResources.length > 0) {
- return this._tryToSplitAndUndo(strResource, element, null, nls.localize('cannotWorkspaceUndoDueToChanges', "Could not undo '{0}' across all files because changes were made to {1}", element.label, cannotUndoDueToResources.join(', ')));
+ return this._tryToSplitAndUndo(
+ strResource,
+ element,
+ null,
+ nls.localize(
+ { key: 'cannotWorkspaceUndoDueToChanges', comment: ['{0} is a label for an operation. {1} is a list of filenames.'] },
+ "Could not undo '{0}' across all files because changes were made to {1}", element.label, cannotUndoDueToResources.join(', ')
+ )
+ );
}
const cannotLockDueToResources: string[] = [];
@@ -795,12 +828,28 @@ export class UndoRedoService implements IUndoRedoService {
}
}
if (cannotLockDueToResources.length > 0) {
- return this._tryToSplitAndUndo(strResource, element, null, nls.localize('cannotWorkspaceUndoDueToInProgressUndoRedo', "Could not undo '{0}' across all files because there is already an undo or redo operation running on {1}", element.label, cannotLockDueToResources.join(', ')));
+ return this._tryToSplitAndUndo(
+ strResource,
+ element,
+ null,
+ nls.localize(
+ { key: 'cannotWorkspaceUndoDueToInProgressUndoRedo', comment: ['{0} is a label for an operation. {1} is a list of filenames.'] },
+ "Could not undo '{0}' across all files because there is already an undo or redo operation running on {1}", element.label, cannotLockDueToResources.join(', ')
+ )
+ );
}
// check if new stack elements were added in the meantime...
if (!editStackSnapshot.isValid()) {
- return this._tryToSplitAndUndo(strResource, element, null, nls.localize('cannotWorkspaceUndoDueToInMeantimeUndoRedo', "Could not undo '{0}' across all files because an undo or redo operation occurred in the meantime", element.label));
+ return this._tryToSplitAndUndo(
+ strResource,
+ element,
+ null,
+ nls.localize(
+ { key: 'cannotWorkspaceUndoDueToInMeantimeUndoRedo', comment: ['{0} is a label for an operation. {1} is a list of filenames.'] },
+ "Could not undo '{0}' across all files because an undo or redo operation occurred in the meantime", element.label
+ )
+ );
}
return null;
@@ -881,7 +930,10 @@ export class UndoRedoService implements IUndoRedoService {
return;
}
if (editStack.locked) {
- const message = nls.localize('cannotResourceUndoDueToInProgressUndoRedo', "Could not undo '{0}' because there is already an undo or redo operation running.", element.label);
+ const message = nls.localize(
+ { key: 'cannotResourceUndoDueToInProgressUndoRedo', comment: ['{0} is a label for an operation.'] },
+ "Could not undo '{0}' because there is already an undo or redo operation running.", element.label
+ );
this._notificationService.info(message);
return;
}
@@ -942,10 +994,26 @@ export class UndoRedoService implements IUndoRedoService {
private _checkWorkspaceRedo(strResource: string, element: WorkspaceStackElement, editStackSnapshot: EditStackSnapshot, checkInvalidatedResources: boolean): WorkspaceVerificationError | null {
if (element.removedResources) {
- return this._tryToSplitAndRedo(strResource, element, element.removedResources, nls.localize('cannotWorkspaceRedo', "Could not redo '{0}' across all files. {1}", element.label, element.removedResources.createMessage()));
+ return this._tryToSplitAndRedo(
+ strResource,
+ element,
+ element.removedResources,
+ nls.localize(
+ { key: 'cannotWorkspaceRedo', comment: ['{0} is a label for an operation. {1} is another message.'] },
+ "Could not redo '{0}' across all files. {1}", element.label, element.removedResources.createMessage()
+ )
+ );
}
if (checkInvalidatedResources && element.invalidatedResources) {
- return this._tryToSplitAndRedo(strResource, element, element.invalidatedResources, nls.localize('cannotWorkspaceRedo', "Could not redo '{0}' across all files. {1}", element.label, element.invalidatedResources.createMessage()));
+ return this._tryToSplitAndRedo(
+ strResource,
+ element,
+ element.invalidatedResources,
+ nls.localize(
+ { key: 'cannotWorkspaceRedo', comment: ['{0} is a label for an operation. {1} is another message.'] },
+ "Could not redo '{0}' across all files. {1}", element.label, element.invalidatedResources.createMessage()
+ )
+ );
}
// this must be the last future element in all the impacted resources!
@@ -956,7 +1024,15 @@ export class UndoRedoService implements IUndoRedoService {
}
}
if (cannotRedoDueToResources.length > 0) {
- return this._tryToSplitAndRedo(strResource, element, null, nls.localize('cannotWorkspaceRedoDueToChanges', "Could not redo '{0}' across all files because changes were made to {1}", element.label, cannotRedoDueToResources.join(', ')));
+ return this._tryToSplitAndRedo(
+ strResource,
+ element,
+ null,
+ nls.localize(
+ { key: 'cannotWorkspaceRedoDueToChanges', comment: ['{0} is a label for an operation. {1} is a list of filenames.'] },
+ "Could not redo '{0}' across all files because changes were made to {1}", element.label, cannotRedoDueToResources.join(', ')
+ )
+ );
}
const cannotLockDueToResources: string[] = [];
@@ -966,12 +1042,28 @@ export class UndoRedoService implements IUndoRedoService {
}
}
if (cannotLockDueToResources.length > 0) {
- return this._tryToSplitAndRedo(strResource, element, null, nls.localize('cannotWorkspaceRedoDueToInProgressUndoRedo', "Could not redo '{0}' across all files because there is already an undo or redo operation running on {1}", element.label, cannotLockDueToResources.join(', ')));
+ return this._tryToSplitAndRedo(
+ strResource,
+ element,
+ null,
+ nls.localize(
+ { key: 'cannotWorkspaceRedoDueToInProgressUndoRedo', comment: ['{0} is a label for an operation. {1} is a list of filenames.'] },
+ "Could not redo '{0}' across all files because there is already an undo or redo operation running on {1}", element.label, cannotLockDueToResources.join(', ')
+ )
+ );
}
// check if new stack elements were added in the meantime...
if (!editStackSnapshot.isValid()) {
- return this._tryToSplitAndRedo(strResource, element, null, nls.localize('cannotWorkspaceRedoDueToInMeantimeUndoRedo', "Could not redo '{0}' across all files because an undo or redo operation occurred in the meantime", element.label));
+ return this._tryToSplitAndRedo(
+ strResource,
+ element,
+ null,
+ nls.localize(
+ { key: 'cannotWorkspaceRedoDueToInMeantimeUndoRedo', comment: ['{0} is a label for an operation. {1} is a list of filenames.'] },
+ "Could not redo '{0}' across all files because an undo or redo operation occurred in the meantime", element.label
+ )
+ );
}
return null;
@@ -1015,7 +1107,10 @@ export class UndoRedoService implements IUndoRedoService {
return;
}
if (editStack.locked) {
- const message = nls.localize('cannotResourceRedoDueToInProgressUndoRedo', "Could not redo '{0}' because there is already an undo or redo operation running.", element.label);
+ const message = nls.localize(
+ { key: 'cannotResourceRedoDueToInProgressUndoRedo', comment: ['{0} is a label for an operation.'] },
+ "Could not redo '{0}' because there is already an undo or redo operation running.", element.label
+ );
this._notificationService.info(message);
return;
}
diff --git a/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts b/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts
index fc4607e39a..e8141f1192 100644
--- a/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts
+++ b/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts
@@ -32,6 +32,7 @@ type AutoSyncErrorClassification = {
const enablementKey = 'sync.enable';
const disableMachineEventuallyKey = 'sync.disableMachineEventually';
const sessionIdKey = 'sync.sessionId';
+const storeUrlKey = 'sync.storeUrl';
export class UserDataAutoSyncEnablementService extends Disposable {
@@ -97,15 +98,20 @@ export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService i
this.syncTriggerDelayer = this._register(new Delayer(0));
if (userDataSyncStoreService.userDataSyncStore) {
+
+ storageService.store(storeUrlKey, userDataSyncStoreService.userDataSyncStore.url.toString(), StorageScope.GLOBAL);
+
if (this.isEnabled()) {
this.logService.info('Auto Sync is enabled.');
} else {
this.logService.info('Auto Sync is disabled.');
}
this.updateAutoSync();
+
if (this.hasToDisableMachineEventually()) {
this.disableMachineEventually();
}
+
this._register(userDataSyncAccountService.onDidChangeAccount(() => this.updateAutoSync()));
this._register(userDataSyncStoreService.onDidChangeDonotMakeRequestsUntil(() => this.updateAutoSync()));
this._register(Event.debounce(userDataSyncService.onDidChangeLocal, (last, source) => last ? [...last, source] : [source], 1000)(sources => this.triggerSync(sources, false)));
diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts
index 0f5256fa87..7ea0506c10 100644
--- a/src/vs/vscode.proposed.d.ts
+++ b/src/vs/vscode.proposed.d.ts
@@ -868,22 +868,6 @@ declare module 'vscode' {
debugAdapterExecutable?(folder: WorkspaceFolder | undefined, token?: CancellationToken): ProviderResult;
}
- export interface DebugSession {
-
- /**
- * Terminates the session.
- */
- terminate(): Thenable;
- }
-
- export interface DebugSession {
-
- /**
- * Terminates the session.
- */
- terminate(): Thenable;
- }
-
export namespace debug {
/**
diff --git a/src/vs/workbench/api/browser/mainThreadDebugService.ts b/src/vs/workbench/api/browser/mainThreadDebugService.ts
index ebdb130dd4..4c25b4e80f 100644
--- a/src/vs/workbench/api/browser/mainThreadDebugService.ts
+++ b/src/vs/workbench/api/browser/mainThreadDebugService.ts
@@ -262,14 +262,6 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
return Promise.reject(new Error('debug session not found'));
}
- public $terminateDebugSession(sessionId: DebugSessionUUID): Promise {
- const session = this.debugService.getModel().getSession(sessionId, true);
- if (session) {
- return session.terminate();
- }
- return Promise.reject(new Error('debug session not found'));
- }
-
public $stopDebugging(sessionId: DebugSessionUUID | undefined): Promise {
if (sessionId) {
const session = this.debugService.getModel().getSession(sessionId, true);
diff --git a/src/vs/workbench/api/browser/mainThreadEditor.ts b/src/vs/workbench/api/browser/mainThreadEditor.ts
index 15585a6029..1f2fec5f86 100644
--- a/src/vs/workbench/api/browser/mainThreadEditor.ts
+++ b/src/vs/workbench/api/browser/mainThreadEditor.ts
@@ -269,6 +269,14 @@ export class MainThreadTextEditor {
}
}));
+ const isValidCodeEditor = () => {
+ // Due to event timings, it is possible that there is a model change event not yet delivered to us.
+ // > e.g. a model change event is emitted to a listener which then decides to update editor options
+ // > In this case the editor configuration change event reaches us first.
+ // So simply check that the model is still attached to this code editor
+ return (this._codeEditor && this._codeEditor.getModel() === this._model);
+ };
+
const updateProperties = (selectionChangeSource: string | null) => {
// Some editor events get delivered faster than model content changes. This is
// problematic, as this leads to editor properties reaching the extension host
@@ -287,18 +295,30 @@ export class MainThreadTextEditor {
this._codeEditorListeners.add(this._codeEditor.onDidChangeCursorSelection((e) => {
// selection
+ if (!isValidCodeEditor()) {
+ return;
+ }
updateProperties(e.source);
}));
- this._codeEditorListeners.add(this._codeEditor.onDidChangeConfiguration(() => {
+ this._codeEditorListeners.add(this._codeEditor.onDidChangeConfiguration((e) => {
// options
+ if (!isValidCodeEditor()) {
+ return;
+ }
updateProperties(null);
}));
this._codeEditorListeners.add(this._codeEditor.onDidLayoutChange(() => {
// visibleRanges
+ if (!isValidCodeEditor()) {
+ return;
+ }
updateProperties(null);
}));
this._codeEditorListeners.add(this._codeEditor.onDidScrollChange(() => {
// visibleRanges
+ if (!isValidCodeEditor()) {
+ return;
+ }
updateProperties(null);
}));
this._updatePropertiesNow(null);
diff --git a/src/vs/workbench/api/browser/mainThreadExtensionService.ts b/src/vs/workbench/api/browser/mainThreadExtensionService.ts
index 2153926289..e43eab305e 100644
--- a/src/vs/workbench/api/browser/mainThreadExtensionService.ts
+++ b/src/vs/workbench/api/browser/mainThreadExtensionService.ts
@@ -128,7 +128,7 @@ export class MainThreadExtensionService implements MainThreadExtensionServiceSha
}
}
- $onExtensionHostExit(code: number): void {
+ async $onExtensionHostExit(code: number): Promise {
this._extensionService._onExtensionHostExit(code);
}
}
diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts
index 54c9232b02..76175cb3ef 100644
--- a/src/vs/workbench/api/common/extHost.protocol.ts
+++ b/src/vs/workbench/api/common/extHost.protocol.ts
@@ -799,7 +799,7 @@ export interface MainThreadExtensionServiceShape extends IDisposable {
$onDidActivateExtension(extensionId: ExtensionIdentifier, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationReason: ExtensionActivationReason): void;
$onExtensionActivationError(extensionId: ExtensionIdentifier, error: ExtensionActivationError): Promise;
$onExtensionRuntimeError(extensionId: ExtensionIdentifier, error: SerializedError): void;
- $onExtensionHostExit(code: number): void;
+ $onExtensionHostExit(code: number): Promise;
}
export interface SCMProviderFeatures {
@@ -882,7 +882,6 @@ export interface MainThreadDebugServiceShape extends IDisposable {
$stopDebugging(sessionId: DebugSessionUUID | undefined): Promise;
$setDebugSessionName(id: DebugSessionUUID, name: string): void;
$customDebugAdapterRequest(id: DebugSessionUUID, command: string, args: any): Promise;
- $terminateDebugSession(id: DebugSessionUUID): Promise;
$appendDebugConsole(value: string): void;
$startBreakpointEvents(): void;
$registerBreakpoints(breakpoints: Array): Promise;
diff --git a/src/vs/workbench/api/common/extHostDebugService.ts b/src/vs/workbench/api/common/extHostDebugService.ts
index a6b98be0a3..cd184fc1f8 100644
--- a/src/vs/workbench/api/common/extHostDebugService.ts
+++ b/src/vs/workbench/api/common/extHostDebugService.ts
@@ -957,10 +957,6 @@ export class ExtHostDebugSession implements vscode.DebugSession {
public customRequest(command: string, args: any): Promise {
return this._debugServiceProxy.$customDebugAdapterRequest(this._id, command, args);
}
-
- public terminate(): Promise {
- return this._debugServiceProxy.$terminateDebugSession(this._id);
- }
}
export class ExtHostDebugConsole implements vscode.DebugConsole {
diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts
index 197031c565..3fd03a3778 100644
--- a/src/vs/workbench/api/common/extHostExtensionService.ts
+++ b/src/vs/workbench/api/common/extHostExtensionService.ts
@@ -557,7 +557,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
}
// after tests have run, we shutdown the host
- this._gracefulExit(error || (typeof failures === 'number' && failures > 0) ? 1 /* ERROR */ : 0 /* OK */);
+ this._testRunnerExit(error || (typeof failures === 'number' && failures > 0) ? 1 /* ERROR */ : 0 /* OK */);
};
const runResult = testRunner!.run(extensionTestsPath, oldTestRunnerCallback);
@@ -567,11 +567,11 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
runResult
.then(() => {
c();
- this._gracefulExit(0);
+ this._testRunnerExit(0);
})
.catch((err: Error) => {
e(err.toString());
- this._gracefulExit(1);
+ this._testRunnerExit(1);
});
}
});
@@ -579,24 +579,20 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
// Otherwise make sure to shutdown anyway even in case of an error
else {
- this._gracefulExit(1 /* ERROR */);
+ this._testRunnerExit(1 /* ERROR */);
}
return Promise.reject(new Error(requireError ? requireError.toString() : nls.localize('extensionTestError', "Path {0} does not point to a valid extension test runner.", extensionTestsPath)));
}
- private _gracefulExit(code: number): void {
- // to give the PH process a chance to flush any outstanding console
- // messages to the main process, we delay the exit() by some time
- setTimeout(() => {
- // If extension tests are running, give the exit code to the renderer
- if (this._initData.remote.isRemote && !!this._initData.environment.extensionTestsLocationURI) {
- this._mainThreadExtensionsProxy.$onExtensionHostExit(code);
- return;
- }
-
+ private _testRunnerExit(code: number): void {
+ // wait at most 5000ms for the renderer to confirm our exit request and for the renderer socket to drain
+ // (this is to ensure all outstanding messages reach the renderer)
+ const exitPromise = this._mainThreadExtensionsProxy.$onExtensionHostExit(code);
+ const drainPromise = this._extHostContext.drain();
+ Promise.race([Promise.all([exitPromise, drainPromise]), timeout(5000)]).then(() => {
this._hostUtils.exit(code);
- }, 500);
+ });
}
private _startExtensionHost(): Promise {
diff --git a/src/vs/workbench/api/common/extHostRpcService.ts b/src/vs/workbench/api/common/extHostRpcService.ts
index ec0b601a6d..531bb02786 100644
--- a/src/vs/workbench/api/common/extHostRpcService.ts
+++ b/src/vs/workbench/api/common/extHostRpcService.ts
@@ -18,12 +18,12 @@ export class ExtHostRpcService implements IExtHostRpcService {
readonly getProxy: (identifier: ProxyIdentifier) => T;
readonly set: (identifier: ProxyIdentifier, instance: R) => R;
readonly assertRegistered: (identifiers: ProxyIdentifier[]) => void;
+ readonly drain: () => Promise;
constructor(rpcProtocol: IRPCProtocol) {
this.getProxy = rpcProtocol.getProxy.bind(rpcProtocol);
this.set = rpcProtocol.set.bind(rpcProtocol);
this.assertRegistered = rpcProtocol.assertRegistered.bind(rpcProtocol);
-
+ this.drain = rpcProtocol.drain.bind(rpcProtocol);
}
-
}
diff --git a/src/vs/workbench/api/worker/extHostExtensionService.ts b/src/vs/workbench/api/worker/extHostExtensionService.ts
index 31a39761d3..594d3c35db 100644
--- a/src/vs/workbench/api/worker/extHostExtensionService.ts
+++ b/src/vs/workbench/api/worker/extHostExtensionService.ts
@@ -51,7 +51,10 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService {
// fetch JS sources as text and create a new function around it
const source = await response.text();
- const initFn = new Function('module', 'exports', 'require', `${source}\n//# sourceURL=${module.toString(true)}`);
+ // Here we append #vscode-extension to serve as a marker, such that source maps
+ // can be adjusted for the extra wrapping function.
+ const sourceURL = `${module.toString(true)}#vscode-extension`;
+ const initFn = new Function('module', 'exports', 'require', `${source}\n//# sourceURL=${sourceURL}`);
// define commonjs globals: `module`, `exports`, and `require`
const _exports = {};
diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts
index bb72d95d0b..e89b6aea4b 100644
--- a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts
+++ b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts
@@ -33,6 +33,7 @@ import { ContextSubMenu } from 'vs/base/browser/contextmenu';
import { IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService';
import { AuthenticationSession } from 'vs/editor/common/modes';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
+import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
export class ViewContainerActivityAction extends ActivityAction {
@@ -98,6 +99,8 @@ export class ViewContainerActivityAction extends ActivityAction {
}
}
+export const ACCOUNTS_VISIBILITY_PREFERENCE_KEY = 'workbench.activity.showAccounts';
+
export class AccountsActionViewItem extends ActivityActionViewItem {
constructor(
action: ActivityAction,
@@ -107,7 +110,8 @@ export class AccountsActionViewItem extends ActivityActionViewItem {
@IMenuService protected menuService: IMenuService,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IAuthenticationService private readonly authenticationService: IAuthenticationService,
- @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService
+ @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
+ @IStorageService private readonly storageService: IStorageService
) {
super(action, { draggable: false, colors, icon: true }, themeService);
}
@@ -190,6 +194,15 @@ export class AccountsActionViewItem extends ActivityActionViewItem {
}
});
+ if (menus.length) {
+ menus.push(new Separator());
+ }
+
+ menus.push(new Action('hide', nls.localize('hide', "Hide"), undefined, true, _ => {
+ this.storageService.store(ACCOUNTS_VISIBILITY_PREFERENCE_KEY, false, StorageScope.GLOBAL);
+ return Promise.resolve();
+ }));
+
return menus;
}
diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts
index 849eb76a1a..b5ed80a630 100644
--- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts
+++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts
@@ -5,11 +5,10 @@
import 'vs/css!./media/activitybarpart';
import * as nls from 'vs/nls';
-import { ActionsOrientation, ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
+import { ActionsOrientation, ActionBar, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { GLOBAL_ACTIVITY_ID, IActivity, ACCOUNTS_ACTIIVTY_ID } from 'vs/workbench/common/activity';
import { Part } from 'vs/workbench/browser/part';
-import { GlobalActivityActionViewItem, ViewContainerActivityAction, PlaceHolderToggleCompositePinnedAction, PlaceHolderViewContainerActivityAction, HomeAction, HomeActionViewItem } from 'vs/workbench/browser/parts/activitybar/activitybarActions';
-import { AccountsActionViewItem } from 'sql/workbench/browser/parts/activitybar/activitybarActions'; // {{ SQL CARBON EDIT }} - use the ADS account management action
+import { GlobalActivityActionViewItem, ViewContainerActivityAction, PlaceHolderToggleCompositePinnedAction, PlaceHolderViewContainerActivityAction, HomeAction, HomeActionViewItem, ACCOUNTS_VISIBILITY_PREFERENCE_KEY } from 'vs/workbench/browser/parts/activitybar/activitybarActions';
import { IBadge, NumberBadge } from 'vs/workbench/services/activity/common/activity';
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@@ -43,6 +42,8 @@ import { Event } from 'vs/base/common/event';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
+import { AccountsActionViewItem } from 'sql/workbench/browser/parts/activitybar/activitybarActions'; // {{ SQL CARBON EDIT }} - use the ADS account management action
+
interface IPlaceholderViewContainer {
id: string;
name?: string;
@@ -76,7 +77,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
static readonly PINNED_VIEW_CONTAINERS = 'workbench.activity.pinnedViewlets2';
private static readonly PLACEHOLDER_VIEW_CONTAINERS = 'workbench.activity.placeholderViewlets';
private static readonly HOME_BAR_VISIBILITY_PREFERENCE = 'workbench.activity.showHomeIndicator';
-
+ private static readonly ACCOUNTS_ACTION_INDEX = 0;
//#region IView
readonly minimumWidth: number = 48;
@@ -165,6 +166,18 @@ export class ActivitybarPart extends Part implements IActivityBarService {
actions.push(this.instantiationService.createInstance(ToggleMenuBarAction, ToggleMenuBarAction.ID, menuBarVisibility === 'compact' ? nls.localize('hideMenu', "Hide Menu") : nls.localize('showMenu', "Show Menu")));
}
+ const toggleAccountsVisibilityAction = new Action(
+ 'toggleAccountsVisibility',
+ nls.localize('accounts', "Accounts"),
+ undefined,
+ true,
+ async () => { this.accountsVisibilityPreference = !this.accountsVisibilityPreference; }
+ );
+
+ toggleAccountsVisibilityAction.checked = !!this.accountsActivityAction;
+ actions.push(toggleAccountsVisibilityAction);
+ actions.push(new Separator());
+
actions.push(new Action(
ToggleActivityBarVisibilityAction.ID,
nls.localize('hideActivitBar', "Hide Activity Bar"),
@@ -588,17 +601,35 @@ export class ActivitybarPart extends Part implements IActivityBarService {
cssClass: Codicon.settingsGear.classNames
});
- this.accountsActivityAction = new ActivityAction({
- id: 'workbench.actions.accounts',
- name: nls.localize('accounts', "Accounts"),
- cssClass: Codicon.account.classNames
- });
+ if (this.accountsVisibilityPreference) {
+ this.accountsActivityAction = new ActivityAction({
+ id: 'workbench.actions.accounts',
+ name: nls.localize('accounts', "Accounts"),
+ cssClass: Codicon.account.classNames
+ });
- this.globalActivityActionBar.push(this.accountsActivityAction);
+ this.globalActivityActionBar.push(this.accountsActivityAction, { index: ActivitybarPart.ACCOUNTS_ACTION_INDEX });
+ }
this.globalActivityActionBar.push(this.globalActivityAction);
}
+ private toggleAccountsActivity() {
+ if (this.globalActivityActionBar) {
+ if (this.accountsActivityAction) {
+ this.globalActivityActionBar.pull(ActivitybarPart.ACCOUNTS_ACTION_INDEX);
+ this.accountsActivityAction = undefined;
+ } else {
+ this.accountsActivityAction = new ActivityAction({
+ id: 'workbench.actions.accounts',
+ name: nls.localize('accounts', "Accounts"),
+ cssClass: Codicon.account.classNames
+ });
+ this.globalActivityActionBar.push(this.accountsActivityAction, { index: ActivitybarPart.ACCOUNTS_ACTION_INDEX });
+ }
+ }
+ }
+
private getCompositeActions(compositeId: string): { activityAction: ViewContainerActivityAction, pinnedAction: ToggleCompositePinnedAction } {
let compositeActions = this.compositeActions.get(compositeId);
if (!compositeActions) {
@@ -828,6 +859,10 @@ export class ActivitybarPart extends Part implements IActivityBarService {
if (e.key === ActivitybarPart.HOME_BAR_VISIBILITY_PREFERENCE && e.scope === StorageScope.GLOBAL) {
this.onDidChangeHomeBarVisibility();
}
+
+ if (e.key === ACCOUNTS_VISIBILITY_PREFERENCE_KEY && e.scope === StorageScope.GLOBAL) {
+ this.toggleAccountsActivity();
+ }
}
private saveCachedViewContainers(): void {
@@ -965,6 +1000,14 @@ export class ActivitybarPart extends Part implements IActivityBarService {
this.storageService.store(ActivitybarPart.HOME_BAR_VISIBILITY_PREFERENCE, value, StorageScope.GLOBAL);
}
+ private get accountsVisibilityPreference(): boolean {
+ return this.storageService.getBoolean(ACCOUNTS_VISIBILITY_PREFERENCE_KEY, StorageScope.GLOBAL, true);
+ }
+
+ private set accountsVisibilityPreference(value: boolean) {
+ this.storageService.store(ACCOUNTS_VISIBILITY_PREFERENCE_KEY, value, StorageScope.GLOBAL);
+ }
+
private migrateFromOldCachedViewContainersValue(): void {
const value = this.storageService.get('workbench.activity.pinnedViewlets', StorageScope.GLOBAL);
if (value !== undefined) {
diff --git a/src/vs/workbench/browser/viewlet.ts b/src/vs/workbench/browser/viewlet.ts
index 38030cba3f..7570fe87ef 100644
--- a/src/vs/workbench/browser/viewlet.ts
+++ b/src/vs/workbench/browser/viewlet.ts
@@ -68,18 +68,18 @@ export abstract class Viewlet extends PaneComposite implements IViewlet {
}
getSecondaryActions(): IAction[] {
- const viewSecondaryActions = this.viewPaneContainer.getViewsVisibilityActions();
+ const viewVisibilityActions = this.viewPaneContainer.getViewsVisibilityActions();
const secondaryActions = this.viewPaneContainer.getSecondaryActions();
- if (viewSecondaryActions.length <= 1) {
+ if (viewVisibilityActions.length <= 1 || viewVisibilityActions.every(({ enabled }) => !enabled)) {
return secondaryActions;
}
if (secondaryActions.length === 0) {
- return viewSecondaryActions;
+ return viewVisibilityActions;
}
return [
- new ContextSubMenu(nls.localize('views', "Views"), viewSecondaryActions),
+ new ContextSubMenu(nls.localize('views', "Views"), viewVisibilityActions),
new Separator(),
...secondaryActions
];
diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts
index c494ff9428..225c971bc8 100644
--- a/src/vs/workbench/contrib/debug/common/debugModel.ts
+++ b/src/vs/workbench/contrib/debug/common/debugModel.ts
@@ -689,7 +689,7 @@ export class Breakpoint extends BaseBreakpoint implements IBreakpoint {
toJSON(): any {
const result = super.toJSON();
- result.uri = this.uri;
+ result.uri = this._uri;
result.lineNumber = this._lineNumber;
result.column = this._column;
result.adapterData = this.adapterData;
diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts
index 8395a4c3ba..d0f002a21e 100644
--- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts
+++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts
@@ -1775,6 +1775,29 @@ export class ShowPopularExtensionsAction extends Action {
}
}
+export class RecentlyPublishedExtensionsAction extends Action {
+
+ static readonly ID = 'workbench.extensions.action.recentlyPublishedExtensions';
+ static readonly LABEL = localize('recentlyPublishedExtensions', "Recently Published Extensions");
+
+ constructor(
+ id: string,
+ label: string,
+ @IViewletService private readonly viewletService: IViewletService
+ ) {
+ super(id, label, undefined, true);
+ }
+
+ run(): Promise {
+ return this.viewletService.openViewlet(VIEWLET_ID, true)
+ .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer)
+ .then(viewlet => {
+ viewlet.search('@sort:publishedDate ');
+ viewlet.focus();
+ });
+ }
+}
+
export class ShowRecommendedExtensionsAction extends Action {
static readonly ID = 'workbench.extensions.action.showRecommendedExtensions';
@@ -2050,6 +2073,27 @@ export class ShowAzureExtensionsAction extends Action {
}
}
+export class SearchCategoryAction extends Action {
+
+ constructor(
+ id: string,
+ label: string,
+ private readonly category: string,
+ @IViewletService private readonly viewletService: IViewletService
+ ) {
+ super(id, label, undefined, true);
+ }
+
+ run(): Promise {
+ return this.viewletService.openViewlet(VIEWLET_ID, true)
+ .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer)
+ .then(viewlet => {
+ viewlet.search(`@category:"${this.category.toLowerCase()}"`);
+ viewlet.focus();
+ });
+ }
+}
+
export class ChangeSortAction extends Action {
private query: Query;
diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts
index 2c89be7afd..cd7f965a66 100644
--- a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts
+++ b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts
@@ -11,23 +11,23 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle';
import { Event as EventOf, Emitter } from 'vs/base/common/event';
import { IAction, Action } from 'vs/base/common/actions';
-import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
+import { Separator, IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { IViewlet } from 'vs/workbench/common/viewlet';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { append, $, addClass, toggleClass, Dimension, hide, show } from 'vs/base/browser/dom';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
-import { IExtensionsWorkbenchService, IExtensionsViewPaneContainer, VIEWLET_ID, AutoUpdateConfigurationKey, ShowRecommendationsOnlyOnDemandKey, CloseExtensionDetailsOnViewChangeKey } from '../common/extensions';
+import { IExtensionsWorkbenchService, IExtensionsViewPaneContainer, VIEWLET_ID, AutoUpdateConfigurationKey, CloseExtensionDetailsOnViewChangeKey } from '../common/extensions';
import {
- ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowRecommendedExtensionsAction, /*ShowPopularExtensionsAction,*/ ShowDisabledExtensionsAction,
- ShowOutdatedExtensionsAction, ClearExtensionsInputAction, ChangeSortAction, UpdateAllAction, CheckForUpdatesAction, DisableAllAction, EnableAllAction,
- EnableAutoUpdateAction, DisableAutoUpdateAction, ShowBuiltInExtensionsAction, InstallVSIXAction
+ ShowRecommendedExtensionsAction, /*ShowPopularExtensionsAction,*/
+ ClearExtensionsInputAction, ChangeSortAction, UpdateAllAction, CheckForUpdatesAction, DisableAllAction, EnableAllAction,
+ EnableAutoUpdateAction, DisableAutoUpdateAction, ShowBuiltInExtensionsAction, InstallVSIXAction, SearchCategoryAction, /*RecentlyPublishedExtensionsAction, */ShowInstalledExtensionsAction, ShowOutdatedExtensionsAction, ShowDisabledExtensionsAction, ShowEnabledExtensionsAction
} from 'vs/workbench/contrib/extensions/browser/extensionsActions';
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IWorkbenchExtensionEnablementService, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput';
-import { ExtensionsListView, EnabledExtensionsView, DisabledExtensionsView, RecommendedExtensionsView, WorkspaceRecommendedExtensionsView, BuiltInExtensionsView, BuiltInThemesExtensionsView, BuiltInBasicsExtensionsView, ServerExtensionsView, DefaultRecommendedExtensionsView } from 'vs/workbench/contrib/extensions/browser/extensionsViews';
+import { ExtensionsListView, EnabledExtensionsView, DisabledExtensionsView, RecommendedExtensionsView, WorkspaceRecommendedExtensionsView, BuiltInFeatureExtensionsView, BuiltInThemesExtensionsView, BuiltInProgrammingLanguageExtensionsView, ServerExtensionsView, DefaultRecommendedExtensionsView, OutdatedExtensionsView, InstalledExtensionsView, SearchBuiltInExtensionsView } from 'vs/workbench/contrib/extensions/browser/extensionsViews';
import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import Severity from 'vs/base/common/severity';
@@ -50,9 +50,8 @@ import { SuggestEnabledInput, attachSuggestEnabledInputBoxStyler } from 'vs/work
import { alert } from 'vs/base/browser/ui/aria/aria';
import { createErrorWithActions } from 'vs/base/common/errorsWithActions';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
-import { ExtensionType } from 'vs/platform/extensions/common/extensions';
+import { ExtensionType, EXTENSION_CATEGORIES } from 'vs/platform/extensions/common/extensions';
import { Registry } from 'vs/platform/registry/common/platform';
-import { RemoteNameContext } from 'vs/workbench/browser/contextkeys';
import { ILabelService } from 'vs/platform/label/common/label';
import { MementoObject } from 'vs/workbench/common/memento';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
@@ -60,6 +59,8 @@ import { IPreferencesService } from 'vs/workbench/services/preferences/common/pr
import { DragAndDropObserver } from 'vs/workbench/browser/dnd';
import { URI } from 'vs/base/common/uri';
import { SIDE_BAR_DRAG_AND_DROP_BACKGROUND } from 'vs/workbench/common/theme';
+import { ContextSubMenu } from 'vs/base/browser/contextmenu';
+import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdown';
const NonEmptyWorkspaceContext = new RawContextKey('nonEmptyWorkspace', false);
const DefaultViewsContext = new RawContextKey('defaultExtensionViews', true);
@@ -69,25 +70,9 @@ const SearchOutdatedExtensionsContext = new RawContextKey('searchOutdat
const SearchEnabledExtensionsContext = new RawContextKey('searchEnabledExtensions', false);
const SearchDisabledExtensionsContext = new RawContextKey('searchDisabledExtensions', false);
const HasInstalledExtensionsContext = new RawContextKey('hasInstalledExtensions', true);
+const BuiltInExtensionsContext = new RawContextKey('builtInExtensions', false);
const SearchBuiltInExtensionsContext = new RawContextKey('searchBuiltInExtensions', false);
const RecommendedExtensionsContext = new RawContextKey('recommendedExtensions', false);
-const DefaultRecommendedExtensionsContext = new RawContextKey('defaultRecommendedExtensions', false);
-const viewIdNameMappings: { [id: string]: string } = {
- 'extensions.listView': localize('marketPlace', "Marketplace"),
- 'extensions.enabledExtensionList': localize('enabledExtensions', "Enabled"),
- 'extensions.enabledExtensionList2': localize('enabledExtensions', "Enabled"),
- 'extensions.disabledExtensionList': localize('disabledExtensions', "Disabled"),
- 'extensions.disabledExtensionList2': localize('disabledExtensions', "Disabled"),
- // {{SQL CARBON EDIT}}
- // 'extensions.popularExtensionsList': localize('popularExtensions', "Popular"),
- 'extensions.recommendedList': localize('recommendedExtensions', "Recommended"),
- 'extensions.otherrecommendedList': localize('otherRecommendedExtensions', "Other Recommendations"),
- 'extensions.workspaceRecommendedList': localize('workspaceRecommendedExtensions', "Workspace Recommendations"),
- 'extensions.builtInExtensionsList': localize('builtInExtensions', "Features"),
- 'extensions.builtInThemesExtensionsList': localize('builtInThemesExtensions', "Themes"),
- 'extensions.builtInBasicsExtensionsList': localize('builtInBasicsExtensions', "Programming Languages"),
- 'extensions.syncedExtensionsList': localize('syncedExtensions', "My Account"),
-};
export class ExtensionsViewletViewsContribution implements IWorkbenchContribution {
@@ -103,223 +88,233 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
}
private registerViews(): void {
- let viewDescriptors: IViewDescriptor[] = [];
- viewDescriptors.push(this.createMarketPlaceExtensionsListViewDescriptor());
- viewDescriptors.push(this.createDefaultEnabledExtensionsListViewDescriptor());
- viewDescriptors.push(this.createDefaultDisabledExtensionsListViewDescriptor());
- // {{SQL CARBON EDIT}}
- // viewDescriptors.push(this.createDefaultPopularExtensionsListViewDescriptor());
- viewDescriptors.push(this.createEnabledExtensionsListViewDescriptor());
- viewDescriptors.push(this.createDisabledExtensionsListViewDescriptor());
- viewDescriptors.push(this.createBuiltInExtensionsListViewDescriptor());
- viewDescriptors.push(this.createBuiltInBasicsExtensionsListViewDescriptor());
- viewDescriptors.push(this.createBuiltInThemesExtensionsListViewDescriptor());
- viewDescriptors.push(this.createDefaultRecommendedExtensionsListViewDescriptor());
- viewDescriptors.push(this.createOtherRecommendedExtensionsListViewDescriptor());
- viewDescriptors.push(this.createWorkspaceRecommendedExtensionsListViewDescriptor());
+ const viewDescriptors: IViewDescriptor[] = [];
- if (this.extensionManagementServerService.localExtensionManagementServer) {
- viewDescriptors.push(...this.createExtensionsViewDescriptorsForServer(this.extensionManagementServerService.localExtensionManagementServer));
- }
- if (this.extensionManagementServerService.remoteExtensionManagementServer) {
- viewDescriptors.push(...this.createExtensionsViewDescriptorsForServer(this.extensionManagementServerService.remoteExtensionManagementServer));
- }
+ /* Default views */
+ viewDescriptors.push(...this.createDefaultExtensionsViewDescriptors());
+
+ /* Search views */
+ viewDescriptors.push(...this.createSearchExtensionsViewDescriptors());
+
+ /* Recommendations views */
+ viewDescriptors.push(...this.createRecommendedExtensionsViewDescriptors());
+
+ /* Built-in extensions views */
+ viewDescriptors.push(...this.createBuiltinExtensionsViewDescriptors());
Registry.as(Extensions.ViewsRegistry).registerViews(viewDescriptors, this.container);
}
- // View used for any kind of searching
- private createMarketPlaceExtensionsListViewDescriptor(): IViewDescriptor {
- const id = 'extensions.listView';
- return {
- id,
- name: viewIdNameMappings[id],
+ private createDefaultExtensionsViewDescriptors(): IViewDescriptor[] {
+ const viewDescriptors: IViewDescriptor[] = [];
+
+ /*
+ * Default popular extensions view
+ * Separate view for popular extensions required as we need to show popular and recommended sections
+ * in the default view when there is no search text, and user has no installed extensions.
+ */
+ // viewDescriptors.push({ {{SQL CARBON EDIT}} remove popular
+ // id: 'workbench.views.extensions.popular',
+ // name: localize('popularExtensions', "Popular"),
+ // ctorDescriptor: new SyncDescriptor(ExtensionsListView),
+ // when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.not('hasInstalledExtensions')),
+ // weight: 60,
+ // order: 1,
+ // });
+
+ /*
+ * Default installed extensions views - Shows all user installed extensions.
+ */
+ const servers: IExtensionManagementServer[] = [];
+ if (this.extensionManagementServerService.localExtensionManagementServer) {
+ servers.push(this.extensionManagementServerService.localExtensionManagementServer);
+ }
+ if (this.extensionManagementServerService.remoteExtensionManagementServer) {
+ servers.push(this.extensionManagementServerService.remoteExtensionManagementServer);
+ }
+ const getViewName = (viewTitle: string, server: IExtensionManagementServer): string => {
+ return servers.length > 1 ? `${server.label} - ${viewTitle}` : viewTitle;
+ };
+ for (const server of servers) {
+ const getInstalledViewName = (): string => getViewName(localize('installed', "Installed"), server);
+ const onDidChangeServerLabel: EventOf = EventOf.map(this.labelService.onDidChangeFormatters, () => undefined);
+ viewDescriptors.push({
+ id: servers.length > 1 ? `workbench.views.extensions.${server.id}.installed` : `workbench.views.extensions.installed`,
+ get name() { return getInstalledViewName(); },
+ ctorDescriptor: new SyncDescriptor(ServerExtensionsView, [server, EventOf.map(onDidChangeServerLabel, () => getInstalledViewName())]),
+ when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions')),
+ weight: 100,
+ order: 2,
+ /* Installed extensions views shall not be hidden when there are more than one server */
+ canToggleVisibility: servers.length === 1
+ });
+ }
+
+ /*
+ * Default recommended extensions view
+ * When user has installed extensions, this is shown along with the views for enabled & disabled extensions
+ * When user has no installed extensions, this is shown along with the view for popular extensions
+ */
+ viewDescriptors.push({
+ id: 'extensions.recommendedList',
+ name: localize('recommendedExtensions', "Recommended"),
+ ctorDescriptor: new SyncDescriptor(DefaultRecommendedExtensionsView),
+ when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.not('config.extensions.showRecommendationsOnlyOnDemand')),
+ weight: 40,
+ order: 3,
+ canToggleVisibility: true
+ });
+
+ /* Installed views shall be default in multi server window */
+ if (servers.length === 1) {
+ /*
+ * Default enabled extensions view - Shows all user installed enabled extensions.
+ * Hidden by default
+ */
+ viewDescriptors.push({
+ id: 'workbench.views.extensions.enabled',
+ name: localize('enabledExtensions', "Enabled"),
+ ctorDescriptor: new SyncDescriptor(EnabledExtensionsView),
+ when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions')),
+ hideByDefault: true,
+ weight: 40,
+ order: 4,
+ canToggleVisibility: true
+ });
+
+ /*
+ * Default disabled extensions view - Shows all disabled extensions.
+ * Hidden by default
+ */
+ viewDescriptors.push({
+ id: 'workbench.views.extensions.disabled',
+ name: localize('disabledExtensions', "Disabled"),
+ ctorDescriptor: new SyncDescriptor(DisabledExtensionsView),
+ when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions')),
+ hideByDefault: true,
+ weight: 10,
+ order: 5,
+ canToggleVisibility: true
+ });
+
+ }
+
+ return viewDescriptors;
+ }
+
+ private createSearchExtensionsViewDescriptors(): IViewDescriptor[] {
+ const viewDescriptors: IViewDescriptor[] = [];
+
+ /*
+ * View used for searching Marketplace
+ */
+ viewDescriptors.push({
+ id: 'workbench.views.extensions.marketplace',
+ name: localize('marketPlace', "Marketplace"),
ctorDescriptor: new SyncDescriptor(ExtensionsListView),
when: ContextKeyExpr.and(ContextKeyExpr.has('searchMarketplaceExtensions')),
- weight: 100
- };
- }
+ });
- // Separate view for enabled extensions required as we need to show enabled, disabled and recommended sections
- // in the default view when there is no search text, but user has installed extensions.
- private createDefaultEnabledExtensionsListViewDescriptor(): IViewDescriptor {
- const id = 'extensions.enabledExtensionList';
- return {
- id,
- name: viewIdNameMappings[id],
- ctorDescriptor: new SyncDescriptor(EnabledExtensionsView),
- when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), RemoteNameContext.isEqualTo('')),
- weight: 40,
- canToggleVisibility: true,
- order: 1
- };
- }
-
- // Separate view for disabled extensions required as we need to show enabled, disabled and recommended sections
- // in the default view when there is no search text, but user has installed extensions.
- private createDefaultDisabledExtensionsListViewDescriptor(): IViewDescriptor {
- const id = 'extensions.disabledExtensionList';
- return {
- id,
- name: viewIdNameMappings[id],
- ctorDescriptor: new SyncDescriptor(DisabledExtensionsView),
- when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), RemoteNameContext.isEqualTo('')),
- weight: 10,
- canToggleVisibility: true,
- order: 3,
- collapsed: true
- };
- }
-
- /* // {{SQL CARBON EDIT}}
- // Separate view for popular extensions required as we need to show popular and recommended sections
- // in the default view when there is no search text, and user has no installed extensions.
- private createDefaultPopularExtensionsListViewDescriptor(): IViewDescriptor {
- const id = 'extensions.popularExtensionsList';
- return {
- id,
- name: viewIdNameMappings[id],
- ctorDescriptor: new SyncDescriptor(ExtensionsListView),
- when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.not('hasInstalledExtensions')),
- weight: 60,
- order: 1
- };
- }
- */
-
- private createExtensionsViewDescriptorsForServer(server: IExtensionManagementServer): IViewDescriptor[] {
- const getViewName = (viewTitle: string, server: IExtensionManagementServer): string => {
- const serverLabel = server.label;
- if (viewTitle && this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer) {
- return `${serverLabel} - ${viewTitle}`;
- }
- return viewTitle ? viewTitle : serverLabel;
- };
- const getInstalledViewName = (): string => getViewName(localize('installed', "Installed"), server);
- const getOutdatedViewName = (): string => getViewName(localize('outdated', "Outdated"), server);
- const onDidChangeServerLabel: EventOf = EventOf.map(this.labelService.onDidChangeFormatters, () => undefined);
- return [{
- id: `extensions.${server.id}.installed`,
- get name() { return getInstalledViewName(); },
- ctorDescriptor: new SyncDescriptor(ServerExtensionsView, [server, EventOf.map(onDidChangeServerLabel, () => getInstalledViewName())]),
+ /*
+ * View used for searching all installed extensions
+ */
+ viewDescriptors.push({
+ id: 'workbench.views.extensions.searchInstalled',
+ name: localize('installed', "Installed"),
+ ctorDescriptor: new SyncDescriptor(InstalledExtensionsView),
when: ContextKeyExpr.and(ContextKeyExpr.has('searchInstalledExtensions')),
- weight: 100
- }, {
- id: `extensions.${server.id}.outdated`,
- get name() { return getOutdatedViewName(); },
- ctorDescriptor: new SyncDescriptor(ServerExtensionsView, [server, EventOf.map(onDidChangeServerLabel, () => getOutdatedViewName())]),
- when: ContextKeyExpr.and(ContextKeyExpr.has('searchOutdatedExtensions')),
- weight: 100
- }, {
- id: `extensions.${server.id}.default`,
- get name() { return getInstalledViewName(); },
- ctorDescriptor: new SyncDescriptor(ServerExtensionsView, [server, EventOf.map(onDidChangeServerLabel, () => getInstalledViewName())]),
- when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), RemoteNameContext.notEqualsTo('')),
- weight: 40,
- order: 1
- }];
- }
+ });
- // Separate view for recommended extensions required as we need to show it along with other views when there is no search text.
- // When user has installed extensions, this is shown along with the views for enabled & disabled extensions
- // When user has no installed extensions, this is shown along with the view for popular extensions
- private createDefaultRecommendedExtensionsListViewDescriptor(): IViewDescriptor {
- const id = 'extensions.recommendedList';
- return {
- id,
- name: viewIdNameMappings[id],
- ctorDescriptor: new SyncDescriptor(DefaultRecommendedExtensionsView),
- when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('defaultRecommendedExtensions')),
- weight: 40,
- order: 2,
- canToggleVisibility: true
- };
- }
-
- // Separate view for recommedations that are not workspace recommendations.
- // Shown along with view for workspace recommendations, when using the command that shows recommendations
- private createOtherRecommendedExtensionsListViewDescriptor(): IViewDescriptor {
- const id = 'extensions.otherrecommendedList';
- return {
- id,
- name: viewIdNameMappings[id],
- ctorDescriptor: new SyncDescriptor(RecommendedExtensionsView),
- when: ContextKeyExpr.has('recommendedExtensions'),
- weight: 50,
- order: 2
- };
- }
-
- // Separate view for workspace recommendations.
- // Shown along with view for other recommendations, when using the command that shows recommendations
- private createWorkspaceRecommendedExtensionsListViewDescriptor(): IViewDescriptor {
- const id = 'extensions.workspaceRecommendedList';
- return {
- id,
- name: viewIdNameMappings[id],
- ctorDescriptor: new SyncDescriptor(WorkspaceRecommendedExtensionsView),
- when: ContextKeyExpr.and(ContextKeyExpr.has('recommendedExtensions'), ContextKeyExpr.has('nonEmptyWorkspace')),
- weight: 50,
- order: 1
- };
- }
-
- private createEnabledExtensionsListViewDescriptor(): IViewDescriptor {
- const id = 'extensions.enabledExtensionList2';
- return {
- id,
- name: viewIdNameMappings[id],
+ /*
+ * View used for searching enabled extensions
+ */
+ viewDescriptors.push({
+ id: 'workbench.views.extensions.searchEnabled',
+ name: localize('enabled', "Enabled"),
ctorDescriptor: new SyncDescriptor(EnabledExtensionsView),
when: ContextKeyExpr.and(ContextKeyExpr.has('searchEnabledExtensions')),
- weight: 40,
- order: 1
- };
- }
+ });
- private createDisabledExtensionsListViewDescriptor(): IViewDescriptor {
- const id = 'extensions.disabledExtensionList2';
- return {
- id,
- name: viewIdNameMappings[id],
+ /*
+ * View used for searching disabled extensions
+ */
+ viewDescriptors.push({
+ id: 'workbench.views.extensions.searchDisabled',
+ name: localize('disabled', "Disabled"),
ctorDescriptor: new SyncDescriptor(DisabledExtensionsView),
when: ContextKeyExpr.and(ContextKeyExpr.has('searchDisabledExtensions')),
- weight: 10,
- order: 3,
- collapsed: true
- };
+ });
+
+ /*
+ * View used for searching outdated extensions
+ */
+ viewDescriptors.push({
+ id: 'workbench.views.extensions.searchOutdated',
+ name: localize('outdated', "Outdated"),
+ ctorDescriptor: new SyncDescriptor(OutdatedExtensionsView),
+ when: ContextKeyExpr.and(ContextKeyExpr.has('searchOutdatedExtensions')),
+ });
+
+ /*
+ * View used for searching builtin extensions
+ */
+ viewDescriptors.push({
+ id: 'workbench.views.extensions.searchBuiltin',
+ name: localize('builtin', "Builtin"),
+ ctorDescriptor: new SyncDescriptor(SearchBuiltInExtensionsView),
+ when: ContextKeyExpr.and(ContextKeyExpr.has('searchBuiltInExtensions')),
+ });
+
+ return viewDescriptors;
}
- private createBuiltInExtensionsListViewDescriptor(): IViewDescriptor {
- const id = 'extensions.builtInExtensionsList';
- return {
- id,
- name: viewIdNameMappings[id],
- ctorDescriptor: new SyncDescriptor(BuiltInExtensionsView),
- when: ContextKeyExpr.has('searchBuiltInExtensions'),
- weight: 100
- };
+ private createRecommendedExtensionsViewDescriptors(): IViewDescriptor[] {
+ const viewDescriptors: IViewDescriptor[] = [];
+
+ viewDescriptors.push({
+ id: 'workbench.views.extensions.workspaceRecommendations',
+ name: localize('workspaceRecommendedExtensions', "Workspace Recommendations"),
+ ctorDescriptor: new SyncDescriptor(WorkspaceRecommendedExtensionsView),
+ when: ContextKeyExpr.and(ContextKeyExpr.has('recommendedExtensions'), ContextKeyExpr.has('nonEmptyWorkspace')),
+ order: 1
+ });
+
+ viewDescriptors.push({
+ id: 'workbench.views.extensions.otherRecommendations',
+ name: localize('otherRecommendedExtensions', "Other Recommendations"),
+ ctorDescriptor: new SyncDescriptor(RecommendedExtensionsView),
+ when: ContextKeyExpr.has('recommendedExtensions'),
+ order: 2
+ });
+
+ return viewDescriptors;
}
- private createBuiltInThemesExtensionsListViewDescriptor(): IViewDescriptor {
- const id = 'extensions.builtInThemesExtensionsList';
- return {
- id,
- name: viewIdNameMappings[id],
+ private createBuiltinExtensionsViewDescriptors(): IViewDescriptor[] {
+ const viewDescriptors: IViewDescriptor[] = [];
+
+ viewDescriptors.push({
+ id: 'workbench.views.extensions.builtinFeatureExtensions',
+ name: localize('builtinFeatureExtensions', "Features"),
+ ctorDescriptor: new SyncDescriptor(BuiltInFeatureExtensionsView),
+ when: ContextKeyExpr.has('builtInExtensions'),
+ });
+
+ viewDescriptors.push({
+ id: 'workbench.views.extensions.builtinThemeExtensions',
+ name: localize('builtInThemesExtensions', "Themes"),
ctorDescriptor: new SyncDescriptor(BuiltInThemesExtensionsView),
- when: ContextKeyExpr.has('searchBuiltInExtensions'),
- weight: 100
- };
- }
+ when: ContextKeyExpr.has('builtInExtensions'),
+ });
- private createBuiltInBasicsExtensionsListViewDescriptor(): IViewDescriptor {
- const id = 'extensions.builtInBasicsExtensionsList';
- return {
- id,
- name: viewIdNameMappings[id],
- ctorDescriptor: new SyncDescriptor(BuiltInBasicsExtensionsView),
- when: ContextKeyExpr.has('searchBuiltInExtensions'),
- weight: 100
- };
+ viewDescriptors.push({
+ id: 'workbench.views.extensions.builtinProgrammingLanguageExtensions',
+ name: localize('builtinProgrammingLanguageExtensions', "Programming Languages"),
+ ctorDescriptor: new SyncDescriptor(BuiltInProgrammingLanguageExtensionsView),
+ when: ContextKeyExpr.has('builtInExtensions'),
+ });
+
+ return viewDescriptors;
}
}
@@ -336,15 +331,13 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE
private searchEnabledExtensionsContextKey: IContextKey;
private searchDisabledExtensionsContextKey: IContextKey;
private hasInstalledExtensionsContextKey: IContextKey;
+ private builtInExtensionsContextKey: IContextKey;
private searchBuiltInExtensionsContextKey: IContextKey;
private recommendedExtensionsContextKey: IContextKey;
- private defaultRecommendedExtensionsContextKey: IContextKey;
private searchDelayer: Delayer;
private root: HTMLElement | undefined;
private searchBox: SuggestEnabledInput | undefined;
- private primaryActions: IAction[] | undefined;
- private secondaryActions: IAction[] | null = null;
private readonly searchViewletState: MementoObject;
constructor(
@@ -364,7 +357,7 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE
@IContextMenuService contextMenuService: IContextMenuService,
@IExtensionService extensionService: IExtensionService,
@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
- @IPreferencesService private readonly preferencesService: IPreferencesService
+ @IPreferencesService private readonly preferencesService: IPreferencesService,
) {
super(VIEWLET_ID, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService, viewDescriptorService);
@@ -377,10 +370,9 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE
this.searchEnabledExtensionsContextKey = SearchEnabledExtensionsContext.bindTo(contextKeyService);
this.searchDisabledExtensionsContextKey = SearchDisabledExtensionsContext.bindTo(contextKeyService);
this.hasInstalledExtensionsContextKey = HasInstalledExtensionsContext.bindTo(contextKeyService);
+ this.builtInExtensionsContextKey = BuiltInExtensionsContext.bindTo(contextKeyService);
this.searchBuiltInExtensionsContextKey = SearchBuiltInExtensionsContext.bindTo(contextKeyService);
this.recommendedExtensionsContextKey = RecommendedExtensionsContext.bindTo(contextKeyService);
- this.defaultRecommendedExtensionsContextKey = DefaultRecommendedExtensionsContext.bindTo(contextKeyService);
- this.defaultRecommendedExtensionsContextKey.set(!this.configurationService.getValue(ShowRecommendationsOnlyOnDemandKey));
this._register(this.viewletService.onDidViewletOpen(this.onViewletOpen, this));
this.searchViewletState = this.getMemento(StorageScope.WORKSPACE);
@@ -390,12 +382,8 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE
this._register(this.configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration(AutoUpdateConfigurationKey)) {
- this.secondaryActions = null;
this.updateTitleArea();
}
- if (e.affectedKeys.indexOf(ShowRecommendationsOnlyOnDemandKey) > -1) {
- this.defaultRecommendedExtensionsContextKey.set(!this.configurationService.getValue(ShowRecommendationsOnlyOnDemandKey));
- }
}, this));
}
@@ -509,40 +497,57 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE
}
getActions(): IAction[] {
- if (!this.primaryActions) {
- this.primaryActions = [
- this.instantiationService.createInstance(ClearExtensionsInputAction, ClearExtensionsInputAction.ID, ClearExtensionsInputAction.LABEL, this.onSearchChange, this.searchBox ? this.searchBox.getValue() : '')
- ];
+ return [
+ new Action('workbench.extensions.action.filterExtensions', localize('filterExtensions', "Filter Extensions..."), 'codicon-filter', true),
+ this.instantiationService.createInstance(ClearExtensionsInputAction, ClearExtensionsInputAction.ID, ClearExtensionsInputAction.LABEL, this.onSearchChange, this.searchBox ? this.searchBox.getValue() : ''),
+ ];
+ }
+
+ getActionViewItem(action: IAction): IActionViewItem | undefined {
+ if (action.id === 'workbench.extensions.action.filterExtensions') {
+ return new DropdownMenuActionViewItem(action,
+ [
+ // this.instantiationService.createInstance(ShowPopularExtensionsAction, ShowPopularExtensionsAction.ID, localize('most popular filter', "Most Popular")), // {{SQL CARBON EDIT}}
+ // this.instantiationService.createInstance(RecentlyPublishedExtensionsAction, RecentlyPublishedExtensionsAction.ID, localize('recently published filter', "Recently Published")), // {{SQL CARBON EDIT}}
+ this.instantiationService.createInstance(ShowRecommendedExtensionsAction, ShowRecommendedExtensionsAction.ID, localize('recomended filter', "Recommended")),
+ new ContextSubMenu(localize('filter by category', "Category"), EXTENSION_CATEGORIES.map(category => this.instantiationService.createInstance(SearchCategoryAction, `extensions.actions.searchByCategory.${category}`, category, category))),
+
+ new Separator(),
+ this.instantiationService.createInstance(ShowBuiltInExtensionsAction, ShowBuiltInExtensionsAction.ID, localize('builtin filter', "Built-in")),
+ this.instantiationService.createInstance(ShowInstalledExtensionsAction, ShowInstalledExtensionsAction.ID, localize('installed filter', "Installed")),
+ this.instantiationService.createInstance(ShowEnabledExtensionsAction, ShowEnabledExtensionsAction.ID, localize('enabled filter', "Enabled")),
+ this.instantiationService.createInstance(ShowDisabledExtensionsAction, ShowDisabledExtensionsAction.ID, localize('disabled filter', "Disabled")),
+ this.instantiationService.createInstance(ShowOutdatedExtensionsAction, ShowOutdatedExtensionsAction.ID, localize('outdated filter', "Outdated")),
+
+ new Separator(),
+ new ContextSubMenu(localize('sorty by', "Sort By"), [
+ // this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.install', localize('sort by installs', "Install Count"), this.onSearchChange, 'installs'), // {{SQL CARBON EDIT}}
+ // this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.rating', localize('sort by rating', "Rating"), this.onSearchChange, 'rating'), // {{SQL CARBON EDIT}}
+ this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.name', localize('sort by name', "Name"), this.onSearchChange, 'name'),
+ this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.publishedDate', localize('sort by date', "Published Date"), this.onSearchChange, 'publishedDate'),
+ ]),
+ ],
+ this.contextMenuService, undefined, undefined, undefined, 'codicon-filter', undefined, true);
}
- return this.primaryActions;
+ return super.getActionViewItem(action);
}
getSecondaryActions(): IAction[] {
- if (!this.secondaryActions) {
- this.secondaryActions = [
- this.instantiationService.createInstance(ShowInstalledExtensionsAction, ShowInstalledExtensionsAction.ID, ShowInstalledExtensionsAction.LABEL),
- this.instantiationService.createInstance(ShowOutdatedExtensionsAction, ShowOutdatedExtensionsAction.ID, ShowOutdatedExtensionsAction.LABEL),
- this.instantiationService.createInstance(ShowEnabledExtensionsAction, ShowEnabledExtensionsAction.ID, ShowEnabledExtensionsAction.LABEL),
- this.instantiationService.createInstance(ShowDisabledExtensionsAction, ShowDisabledExtensionsAction.ID, ShowDisabledExtensionsAction.LABEL),
- this.instantiationService.createInstance(ShowBuiltInExtensionsAction, ShowBuiltInExtensionsAction.ID, ShowBuiltInExtensionsAction.LABEL),
- this.instantiationService.createInstance(ShowRecommendedExtensionsAction, ShowRecommendedExtensionsAction.ID, ShowRecommendedExtensionsAction.LABEL),
- // this.instantiationService.createInstance(ShowPopularExtensionsAction, ShowPopularExtensionsAction.ID, ShowPopularExtensionsAction.LABEL), // {{SQL CARBON EDIT}}
- new Separator(),
- // {{SQL CARBON EDIT}}
- //this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.install', localize('sort by installs', "Sort By: Install Count"), this.onSearchChange, 'installs'),
- //this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.rating', localize('sort by rating', "Sort By: Rating"), this.onSearchChange, 'rating'),
- this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.name', localize('sort by name', "Sort By: Name"), this.onSearchChange, 'name'),
- new Separator(),
- this.instantiationService.createInstance(CheckForUpdatesAction, CheckForUpdatesAction.ID, CheckForUpdatesAction.LABEL),
- ...(this.configurationService.getValue(AutoUpdateConfigurationKey) ? [this.instantiationService.createInstance(DisableAutoUpdateAction, DisableAutoUpdateAction.ID, DisableAutoUpdateAction.LABEL)] : [this.instantiationService.createInstance(UpdateAllAction, UpdateAllAction.ID, UpdateAllAction.LABEL), this.instantiationService.createInstance(EnableAutoUpdateAction, EnableAutoUpdateAction.ID, EnableAutoUpdateAction.LABEL)]),
- this.instantiationService.createInstance(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL),
- new Separator(),
- this.instantiationService.createInstance(DisableAllAction, DisableAllAction.ID, DisableAllAction.LABEL),
- this.instantiationService.createInstance(EnableAllAction, EnableAllAction.ID, EnableAllAction.LABEL)
- ];
- }
+ const actions: IAction[] = [];
- return this.secondaryActions;
+ actions.push(this.instantiationService.createInstance(CheckForUpdatesAction, CheckForUpdatesAction.ID, CheckForUpdatesAction.LABEL));
+ if (this.configurationService.getValue(AutoUpdateConfigurationKey)) {
+ actions.push(this.instantiationService.createInstance(DisableAutoUpdateAction, DisableAutoUpdateAction.ID, DisableAutoUpdateAction.LABEL));
+ } else {
+ actions.push(this.instantiationService.createInstance(UpdateAllAction, UpdateAllAction.ID, UpdateAllAction.LABEL), this.instantiationService.createInstance(EnableAutoUpdateAction, EnableAutoUpdateAction.ID, EnableAutoUpdateAction.LABEL));
+ }
+ actions.push(this.instantiationService.createInstance(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL));
+
+ actions.push(new Separator());
+ actions.push(this.instantiationService.createInstance(DisableAllAction, DisableAllAction.ID, DisableAllAction.LABEL));
+ actions.push(this.instantiationService.createInstance(EnableAllAction, EnableAllAction.ID, EnableAllAction.LABEL));
+
+ return actions;
}
search(value: string, refresh: boolean = false): void {
@@ -580,7 +585,8 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE
this.searchOutdatedExtensionsContextKey.set(ExtensionsListView.isOutdatedExtensionsQuery(value));
this.searchEnabledExtensionsContextKey.set(ExtensionsListView.isEnabledExtensionsQuery(value));
this.searchDisabledExtensionsContextKey.set(ExtensionsListView.isDisabledExtensionsQuery(value));
- this.searchBuiltInExtensionsContextKey.set(ExtensionsListView.isBuiltInExtensionsQuery(value));
+ this.searchBuiltInExtensionsContextKey.set(ExtensionsListView.isSearchBuiltInExtensionsQuery(value));
+ this.builtInExtensionsContextKey.set(ExtensionsListView.isBuiltInExtensionsQuery(value));
this.recommendedExtensionsContextKey.set(isRecommendedExtensionsQuery);
this.searchMarketplaceExtensionsContextKey.set(!!value && !ExtensionsListView.isLocalExtensionsQuery(value) && !isRecommendedExtensionsQuery);
this.nonEmptyWorkspaceContextKey.set(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY);
@@ -602,19 +608,20 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE
}
private alertSearchResult(count: number, viewId: string): void {
+ const view = this.viewContainerModel.visibleViewDescriptors.find(view => view.id === viewId);
switch (count) {
case 0:
break;
case 1:
- if (viewIdNameMappings[viewId]) {
- alert(localize('extensionFoundInSection', "1 extension found in the {0} section.", viewIdNameMappings[viewId]));
+ if (view) {
+ alert(localize('extensionFoundInSection', "1 extension found in the {0} section.", view.name));
} else {
alert(localize('extensionFound', "1 extension found."));
}
break;
default:
- if (viewIdNameMappings[viewId]) {
- alert(localize('extensionsFoundInSection', "{0} extensions found in the {1} section.", count, viewIdNameMappings[viewId]));
+ if (view) {
+ alert(localize('extensionsFoundInSection', "{0} extensions found in the {1} section.", count, view.name));
} else {
alert(localize('extensionsFound', "{0} extensions found.", count));
}
diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts
index d663d3289d..aa6d055a3f 100644
--- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts
+++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts
@@ -197,6 +197,7 @@ export class ExtensionsListView extends ViewPane {
case 'installs': options = assign(options, { sortBy: SortBy.InstallCount }); break;
case 'rating': options = assign(options, { sortBy: SortBy.WeightedRating }); break;
case 'name': options = assign(options, { sortBy: SortBy.Title }); break;
+ case 'publishedDate': options = assign(options, { sortBy: SortBy.PublishedDate }); break;
}
const successCallback = (model: IPagedModel) => {
@@ -882,16 +883,21 @@ export class ExtensionsListView extends ViewPane {
this.list = null;
}
- static isBuiltInExtensionsQuery(query: string): boolean {
- return /^\s*@builtin\s*$/i.test(query);
- }
-
static isLocalExtensionsQuery(query: string): boolean {
return this.isInstalledExtensionsQuery(query)
|| this.isOutdatedExtensionsQuery(query)
|| this.isEnabledExtensionsQuery(query)
|| this.isDisabledExtensionsQuery(query)
- || this.isBuiltInExtensionsQuery(query);
+ || this.isBuiltInExtensionsQuery(query)
+ || this.isSearchBuiltInExtensionsQuery(query);
+ }
+
+ static isSearchBuiltInExtensionsQuery(query: string): boolean {
+ return /@builtin\s.+/i.test(query);
+ }
+
+ static isBuiltInExtensionsQuery(query: string): boolean {
+ return /@builtin$/i.test(query.trim());
}
static isInstalledExtensionsQuery(query: string): boolean {
@@ -977,7 +983,7 @@ export class ServerExtensionsView extends ExtensionsListView {
async show(query: string): Promise> {
query = query ? query : '@installed';
- if (!ExtensionsListView.isLocalExtensionsQuery(query) && !ExtensionsListView.isBuiltInExtensionsQuery(query)) {
+ if (!ExtensionsListView.isLocalExtensionsQuery(query)) {
query = query += ' @installed';
}
return super.show(query.trim());
@@ -1009,7 +1015,29 @@ export class DisabledExtensionsView extends ExtensionsListView {
}
}
-export class BuiltInExtensionsView extends ExtensionsListView {
+export class OutdatedExtensionsView extends ExtensionsListView {
+
+ async show(query: string): Promise> {
+ query = query || '@outdated';
+ return ExtensionsListView.isOutdatedExtensionsQuery(query) ? super.show(query) : this.showEmptyModel();
+ }
+}
+
+export class InstalledExtensionsView extends ExtensionsListView {
+
+ async show(query: string): Promise> {
+ query = query || '@installed';
+ return ExtensionsListView.isInstalledExtensionsQuery(query) ? super.show(query) : this.showEmptyModel();
+ }
+}
+
+export class SearchBuiltInExtensionsView extends ExtensionsListView {
+ async show(query: string): Promise> {
+ return ExtensionsListView.isSearchBuiltInExtensionsQuery(query) ? super.show(query) : this.showEmptyModel();
+ }
+}
+
+export class BuiltInFeatureExtensionsView extends ExtensionsListView {
async show(query: string): Promise> {
return (query && query.trim() !== '@builtin') ? this.showEmptyModel() : super.show('@builtin:features');
}
@@ -1021,7 +1049,7 @@ export class BuiltInThemesExtensionsView extends ExtensionsListView {
}
}
-export class BuiltInBasicsExtensionsView extends ExtensionsListView {
+export class BuiltInProgrammingLanguageExtensionsView extends ExtensionsListView {
async show(query: string): Promise> {
return (query && query.trim() !== '@builtin') ? this.showEmptyModel() : super.show('@builtin:basics');
}
diff --git a/src/vs/workbench/contrib/notebook/browser/constants.ts b/src/vs/workbench/contrib/notebook/browser/constants.ts
index 088d2871ce..1be1d63b2a 100644
--- a/src/vs/workbench/contrib/notebook/browser/constants.ts
+++ b/src/vs/workbench/contrib/notebook/browser/constants.ts
@@ -13,7 +13,8 @@ export const CELL_RUN_GUTTER = 28;
export const CODE_CELL_LEFT_MARGIN = 32;
export const EDITOR_TOOLBAR_HEIGHT = 0;
-export const BOTTOM_CELL_TOOLBAR_HEIGHT = 28;
+export const BOTTOM_CELL_TOOLBAR_HEIGHT = 18;
+export const BOTTOM_CELL_TOOLBAR_OFFSET = 12;
export const CELL_STATUSBAR_HEIGHT = 22;
// Margin above editor
diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts
index 53033bd71c..5c85088648 100644
--- a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts
+++ b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts
@@ -74,7 +74,8 @@ const FOCUS_OUT_OUTPUT_COMMAND_ID = 'notebook.cell.focusOutOutput';
export const NOTEBOOK_ACTIONS_CATEGORY = { value: localize('notebookActions.category', "Notebook"), original: 'Notebook' };
-export const CELL_TITLE_GROUP_ID = 'inline';
+export const CELL_TITLE_CELL_GROUP_ID = 'inline/cell';
+export const CELL_TITLE_OUTPUT_GROUP_ID = 'inline/output';
const EDITOR_WIDGET_ACTION_WEIGHT = KeybindingWeight.EditorContrib; // smaller than Suggest Widget, etc
@@ -407,6 +408,11 @@ registerAction2(class extends NotebookCellAction {
weight: KeybindingWeight.WorkbenchContrib
},
precondition: ContextKeyExpr.and(NOTEBOOK_IS_ACTIVE_EDITOR),
+ menu: {
+ id: MenuId.NotebookCellTitle,
+ when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE),
+ group: '2_edit',
+ }
});
}
@@ -425,6 +431,11 @@ registerAction2(class extends NotebookCellAction {
primary: KeyCode.KEY_M,
weight: KeybindingWeight.WorkbenchContrib
},
+ menu: {
+ id: MenuId.NotebookCellTitle,
+ when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE),
+ group: '2_edit',
+ }
});
}
@@ -601,7 +612,7 @@ registerAction2(class extends NotebookCellAction {
NOTEBOOK_CELL_MARKDOWN_EDIT_MODE.toNegated(),
NOTEBOOK_CELL_EDITABLE),
order: CellToolbarOrder.EditCell,
- group: CELL_TITLE_GROUP_ID
+ group: CELL_TITLE_CELL_GROUP_ID
},
icon: { id: 'codicon/pencil' }
});
@@ -625,7 +636,7 @@ registerAction2(class extends NotebookCellAction {
NOTEBOOK_CELL_MARKDOWN_EDIT_MODE,
NOTEBOOK_CELL_EDITABLE),
order: CellToolbarOrder.SaveCell,
- group: CELL_TITLE_GROUP_ID
+ group: CELL_TITLE_CELL_GROUP_ID
},
icon: { id: 'codicon/check' },
keybinding: {
@@ -659,7 +670,7 @@ registerAction2(class extends NotebookCellAction {
id: MenuId.NotebookCellTitle,
order: CellToolbarOrder.DeleteCell,
when: NOTEBOOK_EDITOR_EDITABLE,
- group: CELL_TITLE_GROUP_ID
+ group: CELL_TITLE_CELL_GROUP_ID
},
keybinding: {
primary: KeyCode.Delete,
@@ -758,6 +769,11 @@ registerAction2(class extends NotebookCellAction {
primary: KeyMod.CtrlCmd | KeyCode.KEY_C,
weight: EDITOR_WIDGET_ACTION_WEIGHT
},
+ menu: {
+ id: MenuId.NotebookCellTitle,
+ when: NOTEBOOK_EDITOR_FOCUSED,
+ group: '1_copy',
+ }
});
}
@@ -780,6 +796,11 @@ registerAction2(class extends NotebookCellAction {
primary: KeyMod.CtrlCmd | KeyCode.KEY_X,
weight: EDITOR_WIDGET_ACTION_WEIGHT
},
+ menu: {
+ id: MenuId.NotebookCellTitle,
+ when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE),
+ group: '1_copy',
+ }
});
}
@@ -864,6 +885,11 @@ registerAction2(class extends NotebookAction {
primary: KeyMod.CtrlCmd | KeyCode.KEY_V,
weight: EDITOR_WIDGET_ACTION_WEIGHT
},
+ menu: {
+ id: MenuId.NotebookCellTitle,
+ when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE),
+ group: '1_copy',
+ }
});
}
@@ -1176,7 +1202,7 @@ registerAction2(class extends NotebookCellAction {
id: MenuId.NotebookCellTitle,
when: ContextKeyExpr.and(NOTEBOOK_CELL_TYPE.isEqualTo('code'), NOTEBOOK_EDITOR_RUNNABLE),
order: CellToolbarOrder.ClearCellOutput,
- group: CELL_TITLE_GROUP_ID
+ group: CELL_TITLE_OUTPUT_GROUP_ID
},
keybinding: {
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey), NOTEBOOK_CELL_HAS_OUTPUTS),
@@ -1339,7 +1365,11 @@ registerAction2(class extends NotebookCellAction {
id: MenuId.NotebookCellTitle,
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE, InputFocusedContext),
order: CellToolbarOrder.SplitCell,
- group: CELL_TITLE_GROUP_ID
+ group: CELL_TITLE_CELL_GROUP_ID,
+ // alt: {
+ // id: JOIN_CELL_BELOW_COMMAND_ID,
+ // title: localize('notebookActions.joinCellBelow', "Join with Next Cell")
+ // }
},
icon: { id: 'codicon/split-vertical' },
keybinding: {
@@ -1392,6 +1422,11 @@ registerAction2(class extends NotebookCellAction {
when: NOTEBOOK_EDITOR_FOCUSED,
primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.KEY_J,
weight: KeybindingWeight.WorkbenchContrib
+ },
+ menu: {
+ id: MenuId.NotebookCellTitle,
+ when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE),
+ group: '2_edit',
}
});
}
diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts b/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts
index 94ce77aee8..c85516dbc5 100644
--- a/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts
+++ b/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts
@@ -166,8 +166,7 @@ export class KernelStatus extends Disposable implements IWorkbenchContribution {
const activeEditor = getActiveNotebookEditor(this._editorService);
- if (activeEditor && activeEditor.multipleKernelsAvailable) {
- this.showKernelStatus(activeEditor.activeKernel);
+ if (activeEditor) {
this._editorDisposable.add(activeEditor.onDidChangeKernel(() => {
if (activeEditor.multipleKernelsAvailable) {
this.showKernelStatus(activeEditor.activeKernel);
@@ -175,6 +174,18 @@ export class KernelStatus extends Disposable implements IWorkbenchContribution {
this.kernelInfoElement.clear();
}
}));
+
+ this._editorDisposable.add(activeEditor.onDidChangeAvailableKernels(() => {
+ if (activeEditor.multipleKernelsAvailable) {
+ this.showKernelStatus(activeEditor.activeKernel);
+ } else {
+ this.kernelInfoElement.clear();
+ }
+ }));
+ }
+
+ if (activeEditor && activeEditor.multipleKernelsAvailable) {
+ this.showKernelStatus(activeEditor.activeKernel);
} else {
this.kernelInfoElement.clear();
}
diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebook.css b/src/vs/workbench/contrib/notebook/browser/media/notebook.css
index 38f355bb49..e226929a63 100644
--- a/src/vs/workbench/contrib/notebook/browser/media/notebook.css
+++ b/src/vs/workbench/contrib/notebook/browser/media/notebook.css
@@ -488,6 +488,9 @@
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container {
position: absolute;
display: flex;
+ justify-content: center;
+ z-index: 30; /* over the focus outline on the editor */
+ width: 100%;
opacity: 0;
transition: opacity 0.2s ease-in-out;
cursor: auto;
@@ -531,19 +534,8 @@
align-items: center;
}
-.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container .separator {
- height: 1px;
- flex-grow: 1;
- align-self: center;
-}
-
-.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container .action-item:first-child::after {
- content: ' ';
- display: block;
- height: 1px;
- width: 16px;
- align-self: center;
- margin: 0px 8px;
+.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container .action-item:first-child {
+ margin-right: 16px;
}
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container span.codicon {
@@ -699,7 +691,7 @@
.monaco-workbench .notebookOverlay > .cell-list-container .notebook-folding-indicator .codicon {
visibility: visible;
- padding: 8px 0 0 10px;
+ padding: 10px 0 0 10px;
}
/** Theming */
@@ -739,3 +731,11 @@
.monaco-workbench.vs-dark .monaco-workbench .notebookOverlay .cell.markdown table > tbody > tr > td {
border-color: rgba(255, 255, 255, 0.18);
} */
+
+.monaco-action-bar .action-item.verticalSeparator {
+ width: 1px !important;
+ background-color: #bbb;
+ height: 16px !important;
+ margin: 5px 4px !important;
+ cursor: none;
+}
diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts
index 2452ae4e2e..219873fea2 100644
--- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts
+++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts
@@ -180,6 +180,7 @@ export interface INotebookEditor extends IEditor {
isNotebookEditor: boolean;
activeKernel: INotebookKernelInfo | INotebookKernelInfo2 | undefined;
multipleKernelsAvailable: boolean;
+ readonly onDidChangeAvailableKernels: Event;
readonly onDidChangeKernel: Event;
isDisposed: boolean;
@@ -240,10 +241,16 @@ export interface INotebookEditor extends IEditor {
moveCellDown(cell: ICellViewModel): Promise;
/**
+ * @deprecated Note that this method doesn't support batch operations, use #moveCellToIdx instead.
* Move a cell above or below another cell
*/
moveCell(cell: ICellViewModel, relativeToCell: ICellViewModel, direction: 'above' | 'below'): Promise;
+ /**
+ * Move a cell to a specific position
+ */
+ moveCellToIdx(cell: ICellViewModel, index: number): Promise;
+
/**
* Focus the container of a cell (the monaco editor inside is not focused).
*/
diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts
index a328ad50e3..b21a6c97bb 100644
--- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts
+++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts
@@ -75,6 +75,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
private _overlayContainer!: HTMLElement;
private _body!: HTMLElement;
private _webview: BackLayerWebView | null = null;
+ private _webviewResolved: boolean = false;
+ private _webviewResolvePromise: Promise | null = null;
private _webviewTransparentCover: HTMLElement | null = null;
private _list: INotebookCellList | undefined;
private _dndController: CellDragAndDropController | null = null;
@@ -135,6 +137,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
private _activeKernel: INotebookKernelInfo | INotebookKernelInfo2 | undefined = undefined;
private readonly _onDidChangeKernel = this._register(new Emitter());
readonly onDidChangeKernel: Event = this._onDidChangeKernel.event;
+ private readonly _onDidChangeAvailableKernels = this._register(new Emitter());
+ readonly onDidChangeAvailableKernels: Event = this._onDidChangeAvailableKernels.event;
get activeKernel() {
return this._activeKernel;
@@ -150,7 +154,16 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
}
private _currentKernelTokenSource: CancellationTokenSource | undefined = undefined;
- multipleKernelsAvailable: boolean = false;
+ private _multipleKernelsAvailable: boolean = false;
+
+ get multipleKernelsAvailable() {
+ return this._multipleKernelsAvailable;
+ }
+
+ set multipleKernelsAvailable(state: boolean) {
+ this._multipleKernelsAvailable = state;
+ this._onDidChangeAvailableKernels.fire();
+ }
private readonly _onDidChangeActiveEditor = this._register(new Emitter());
readonly onDidChangeActiveEditor: Event = this._onDidChangeActiveEditor.event;
@@ -572,7 +585,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
// @deprecated
if (provider && provider.kernel) {
// it has a builtin kernel, don't automatically choose a kernel
- this._loadKernelPreloads(provider.providerExtensionLocation, provider.kernel);
+ await this._loadKernelPreloads(provider.providerExtensionLocation, provider.kernel);
tokenSource.dispose();
return;
}
@@ -591,7 +604,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
// the provider doesn't have a builtin kernel, choose a kernel
this.activeKernel = availableKernels[0];
if (this.activeKernel) {
- this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel);
+ await this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel);
}
tokenSource.dispose();
@@ -611,7 +624,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
}
if (this.activeKernel) {
- this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel);
+ await this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel);
await (this.activeKernel as INotebookKernelInfo2).resolve(this.viewModel!.uri, this.getId(), tokenSource.token); // {{SQL CARBON EDIT}} strict-null-checks
}
@@ -624,7 +637,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
if (kernelsFromSameExtension.length) {
const preferedKernel = kernelsFromSameExtension.find(kernel => kernel.isPreferred) || kernelsFromSameExtension[0];
this.activeKernel = preferedKernel;
- this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel);
+ await this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel);
await preferedKernel.resolve(this.viewModel!.uri, this.getId(), tokenSource.token);
tokenSource.dispose();
return;
@@ -633,15 +646,16 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
// the provider doesn't have a builtin kernel, choose a kernel
this.activeKernel = kernels[0];
if (this.activeKernel) {
- this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel);
+ await this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel);
await this.activeKernel.resolve(this.viewModel!.uri, this.getId(), tokenSource.token);
}
tokenSource.dispose();
}
- private _loadKernelPreloads(extensionLocation: URI, kernel: INotebookKernelInfoDto) {
- if (kernel.preloads) {
+ private async _loadKernelPreloads(extensionLocation: URI, kernel: INotebookKernelInfoDto) {
+ if (kernel.preloads && kernel.preloads.length) {
+ await this._resolveWebview();
this._webview?.updateKernelPreloads([extensionLocation], kernel.preloads.map(preload => URI.revive(preload)));
}
}
@@ -656,34 +670,63 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
this._notebookExecuting?.set(notebookMetadata.runState === NotebookRunState.Running);
}
+ private async _resolveWebview(): Promise {
+ if (!this.textModel) {
+ return null;
+ }
+
+ if (this._webviewResolvePromise) {
+ return this._webviewResolvePromise;
+ }
+
+ if (!this._webview) {
+ this._webview = this.instantiationService.createInstance(BackLayerWebView, this, this.getId(), this.textModel!.uri);
+ // attach the webview container to the DOM tree first
+ this._list?.rowsContainer.insertAdjacentElement('afterbegin', this._webview.element);
+ }
+
+ this._webviewResolvePromise = new Promise(async resolve => {
+ await this._webview!.createWebview();
+ this._webview!.webview!.onDidBlur(() => {
+ this._outputFocus?.set(false);
+ this.updateEditorFocus();
+
+ if (this._overlayContainer.contains(document.activeElement)) {
+ this._webiewFocused = false;
+ }
+ });
+ this._webview!.webview!.onDidFocus(() => {
+ this._outputFocus?.set(true);
+ this.updateEditorFocus();
+ this._onDidFocusEmitter.fire();
+
+ if (this._overlayContainer.contains(document.activeElement)) {
+ this._webiewFocused = true;
+ }
+ });
+
+ this._localStore.add(this._webview!.onMessage(({ message, forRenderer }) => {
+ if (this.viewModel) {
+ this.notebookService.onDidReceiveMessage(this.viewModel.viewType, this.getId(), forRenderer, message);
+ }
+ }));
+
+ if (this.viewModel && this.viewModel!.renderers.size) {
+ this._webview?.updateRendererPreloads(this.viewModel!.renderers);
+ }
+
+ this._webviewResolved = true;
+
+ resolve(this._webview!);
+ });
+
+ return this._webviewResolvePromise;
+ }
+
private async _createWebview(id: string, resource: URI): Promise {
this._webview = this.instantiationService.createInstance(BackLayerWebView, this, id, resource);
// attach the webview container to the DOM tree first
this._list?.rowsContainer.insertAdjacentElement('afterbegin', this._webview.element);
- await this._webview.createWebview();
- this._webview.webview.onDidBlur(() => {
- this._outputFocus?.set(false);
- this.updateEditorFocus();
-
- if (this._overlayContainer.contains(document.activeElement)) {
- this._webiewFocused = false;
- }
- });
- this._webview.webview.onDidFocus(() => {
- this._outputFocus?.set(true);
- this.updateEditorFocus();
- this._onDidFocusEmitter.fire();
-
- if (this._overlayContainer.contains(document.activeElement)) {
- this._webiewFocused = true;
- }
- });
-
- this._localStore.add(this._webview.onMessage(({ message, forRenderer }) => {
- if (this.viewModel) {
- this.notebookService.onDidReceiveMessage(this.viewModel.viewType, this.getId(), forRenderer, message);
- }
- }));
}
private async _attachModel(textModel: NotebookTextModel, viewState: INotebookEditorViewState | undefined) {
@@ -717,10 +760,17 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
}
}
- this._webview?.updateRendererPreloads(this.viewModel.renderers);
+ if (this.viewModel.renderers.size) {
+ await this._resolveWebview();
+ this._webview?.updateRendererPreloads(this.viewModel.renderers);
+ }
this._localStore.add(this._list!.onWillScroll(e => {
- this._webview!.updateViewScrollTop(-e.scrollTop, []);
+ if (!this._webviewResolved) {
+ return;
+ }
+
+ this._webview?.updateViewScrollTop(-e.scrollTop, []);
this._webviewTransparentCover!.style.top = `${e.scrollTop}px`;
}));
@@ -732,6 +782,11 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
const scrollTop = this._list?.scrollTop || 0;
const scrollHeight = this._list?.scrollHeight || 0;
+
+ if (!this._webviewResolved) {
+ return;
+ }
+
this._webview!.element.style.height = `${scrollHeight}px`;
if (this._webview?.insetMapping) {
@@ -1128,6 +1183,15 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
return this._moveCellToIndex(originalIdx, newIdx);
}
+ async moveCellToIdx(cell: ICellViewModel, index: number): Promise {
+ if (!this._notebookViewModel!.metadata.editable) {
+ return null;
+ }
+
+ const originalIdx = this._notebookViewModel!.getCellIndex(cell);
+ return this._moveCellToIndex(originalIdx, index);
+ }
+
private async _moveCellToIndex(index: number, newIdx: number): Promise {
if (index === newIdx) {
return null;
@@ -1356,6 +1420,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
return;
}
+ await this._resolveWebview();
+
let preloads = this._notebookViewModel!.renderers;
if (!this._webview!.insetMapping.has(output)) {
@@ -1370,7 +1436,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
}
removeInset(output: IProcessedOutput) {
- if (!this._webview) {
+ if (!this._webview || !this._webviewResolved) {
return;
}
@@ -1378,7 +1444,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
}
hideInset(output: IProcessedOutput) {
- if (!this._webview) {
+ if (!this._webview || !this._webviewResolved) {
return;
}
@@ -1390,10 +1456,14 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
}
postMessage(forRendererId: string | undefined, message: any) {
+ if (!this._webview || !this._webviewResolved) {
+ return;
+ }
+
if (forRendererId === undefined) {
- this._webview?.webview.postMessage(message);
+ this._webview.webview?.postMessage(message);
} else {
- this._webview?.postRendererMessage(forRendererId, message);
+ this._webview.postRendererMessage(forRendererId, message);
}
}
@@ -1600,13 +1670,13 @@ registerThemingParticipant((theme, collector) => {
collector.addRule(`.notebookOverlay .cell-statusbar-container { border-top: solid 1px ${editorBackgroundColor}; }`);
collector.addRule(`.notebookOverlay .monaco-list-row > .monaco-toolbar { background-color: ${editorBackgroundColor}; }`);
collector.addRule(`.notebookOverlay .monaco-list-row.cell-drag-image { background-color: ${editorBackgroundColor}; }`);
+ collector.addRule(`.notebookOverlay .cell-bottom-toolbar-container .action-item { background-color: ${editorBackgroundColor} }`);
}
const cellToolbarSeperator = theme.getColor(CELL_TOOLBAR_SEPERATOR);
if (cellToolbarSeperator) {
- collector.addRule(`.notebookOverlay .cell-bottom-toolbar-container .separator { background-color: ${cellToolbarSeperator} }`);
- collector.addRule(`.notebookOverlay .cell-bottom-toolbar-container .action-item:first-child::after { background-color: ${cellToolbarSeperator} }`);
collector.addRule(`.notebookOverlay .monaco-list-row > .monaco-toolbar { border: solid 1px ${cellToolbarSeperator}; }`);
+ collector.addRule(`.notebookOverlay .cell-bottom-toolbar-container .action-item { border: solid 1px ${cellToolbarSeperator} }`);
}
const focusedCellBackgroundColor = theme.getColor(focusedCellBackground);
@@ -1643,14 +1713,10 @@ registerThemingParticipant((theme, collector) => {
collector.addRule(`.notebookOverlay .monaco-list-row.cell-editor-focus .cell-editor-part:before { outline: solid 1px ${focusedEditorBorderColorColor}; }`);
}
- const editorBorderColor = theme.getColor(notebookCellBorder);
- if (editorBorderColor) {
- collector.addRule(`.notebookOverlay .monaco-list-row .cell-editor-part:before { outline: solid 1px ${editorBorderColor}; }`);
- }
-
- const headingBorderColor = theme.getColor(notebookCellBorder);
- if (headingBorderColor) {
- collector.addRule(`.notebookOverlay .cell.markdown h1 { border-color: ${headingBorderColor}; }`);
+ const cellBorderColor = theme.getColor(notebookCellBorder);
+ if (cellBorderColor) {
+ collector.addRule(`.notebookOverlay .cell.markdown h1 { border-color: ${cellBorderColor}; }`);
+ collector.addRule(`.notebookOverlay .monaco-list-row .cell-editor-part:before { outline: solid 1px ${cellBorderColor}; }`);
}
const cellStatusSuccessIcon = theme.getColor(cellStatusIconSuccess);
@@ -1701,10 +1767,8 @@ registerThemingParticipant((theme, collector) => {
collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row > div.cell.code { margin-left: ${CODE_CELL_LEFT_MARGIN}px; }`);
collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row { padding-top: ${EDITOR_TOP_MARGIN}px; }`);
collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .markdown-cell-row { padding-bottom: ${CELL_BOTTOM_MARGIN}px; }`);
- collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .markdown-cell-row .cell-bottom-toolbar-container { margin-top: ${CELL_BOTTOM_MARGIN}px; }`);
collector.addRule(`.notebookOverlay .output { margin: 0px ${CELL_MARGIN}px 0px ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER}px; }`);
collector.addRule(`.notebookOverlay .output { width: calc(100% - ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER + (CELL_MARGIN * 2)}px); }`);
- collector.addRule(`.notebookOverlay .cell-bottom-toolbar-container { width: calc(100% - ${CELL_MARGIN * 2 + CELL_RUN_GUTTER}px); margin: 0px ${CELL_MARGIN * 2}px 0px ${CELL_MARGIN + CELL_RUN_GUTTER}px; }`);
collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row > div.cell.markdown { padding-left: ${CELL_RUN_GUTTER}px; }`);
collector.addRule(`.notebookOverlay .cell .run-button-container { width: ${CELL_RUN_GUTTER}px; }`);
diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts
index 53319f6c66..f1aa6a888c 100644
--- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts
+++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts
@@ -219,7 +219,7 @@ export interface INotebookWebviewMessage {
let version = 0;
export class BackLayerWebView extends Disposable {
element: HTMLElement;
- webview!: WebviewElement;
+ webview: WebviewElement | undefined = undefined;
insetMapping: Map = new Map();
hiddenInsetMapping: Set = new Set();
reversedInsetMapping: Map = new Map();
@@ -714,7 +714,7 @@ ${loaderJs}
return;
}
- this.webview.focus();
+ this.webview?.focus();
}
focusOutput(cellId: string) {
@@ -722,7 +722,7 @@ ${loaderJs}
return;
}
- this.webview.focus();
+ this.webview?.focus();
setTimeout(() => { // Need this, or focus decoration is not shown. No clue.
this._sendMessageToWebview({
type: 'focus-output',
@@ -814,6 +814,10 @@ ${loaderJs}
}
private _updatePreloads(resources: IPreloadResource[], source: 'renderer' | 'kernel') {
+ if (!this.webview) {
+ return;
+ }
+
const mixedResourceRoots = [...(this.localResourceRootsCache || []), ...this.rendererRootsCache, ...this.kernelRootsCache];
this.webview.localResourcesRoot = mixedResourceRoots;
@@ -830,7 +834,7 @@ ${loaderJs}
return;
}
- this.webview.postMessage(message);
+ this.webview?.postMessage(message);
}
clearPreloadsCache() {
@@ -839,7 +843,7 @@ ${loaderJs}
dispose() {
this._disposed = true;
- this.webview.dispose();
+ this.webview?.dispose();
super.dispose();
}
}
diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellActionView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellActionView.ts
new file mode 100644
index 0000000000..f3cbb4e71b
--- /dev/null
+++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellActionView.ts
@@ -0,0 +1,74 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the Source EULA. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import * as DOM from 'vs/base/browser/dom';
+import { Action, IAction } from 'vs/base/common/actions';
+import { BaseActionViewItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
+import { IMenu, IMenuActionOptions, MenuItemAction, SubmenuItemAction } from 'vs/platform/actions/common/actions';
+import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
+
+export class VerticalSeparator extends Action {
+ static readonly ID = 'vs.actions.verticalSeparator';
+
+ constructor(
+ label?: string
+ ) {
+ super(VerticalSeparator.ID, label, label ? 'verticalSeparator text' : 'verticalSeparator');
+ this.checked = false;
+ this.enabled = false;
+ }
+}
+
+export class VerticalSeparatorViewItem extends BaseActionViewItem {
+ render(container: HTMLElement) {
+ DOM.addClass(container, 'verticalSeparator');
+ // const iconContainer = DOM.append(container, $('.verticalSeparator'));
+ // DOM.addClasses(iconContainer, 'codicon', 'codicon-chrome-minimize');
+ }
+}
+
+export function createAndFillInActionBarActionsWithVerticalSeparators(menu: IMenu, options: IMenuActionOptions | undefined, target: IAction[] | { primary: IAction[]; secondary: IAction[]; }, isPrimaryGroup?: (group: string) => boolean): IDisposable {
+ const groups = menu.getActions(options);
+ // Action bars handle alternative actions on their own so the alternative actions should be ignored
+ fillInActions(groups, target, false, isPrimaryGroup);
+ return asDisposable(groups);
+}
+
+function fillInActions(groups: ReadonlyArray<[string, ReadonlyArray]>, target: IAction[] | { primary: IAction[]; secondary: IAction[]; }, useAlternativeActions: boolean, isPrimaryGroup: (group: string) => boolean = group => group === 'navigation'): void {
+ for (let tuple of groups) {
+ let [group, actions] = tuple;
+ if (useAlternativeActions) {
+ actions = actions.map(a => (a instanceof MenuItemAction) && !!a.alt ? a.alt : a);
+ }
+
+ if (isPrimaryGroup(group)) {
+ const to = Array.isArray(target) ? target : target.primary;
+
+ if (to.length > 0) {
+ to.push(new VerticalSeparator());
+ }
+
+ to.push(...actions);
+ } else {
+ const to = Array.isArray(target) ? target : target.secondary;
+
+ if (to.length > 0) {
+ to.push(new Separator());
+ }
+
+ to.push(...actions);
+ }
+ }
+}
+
+function asDisposable(groups: ReadonlyArray<[string, ReadonlyArray]>): IDisposable {
+ const disposables = new DisposableStore();
+ for (const [, actions] of groups) {
+ for (const action of actions) {
+ disposables.add(action);
+ }
+ }
+ return disposables;
+}
diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellMenus.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellMenus.ts
index 9adad43c73..38e9cdc831 100644
--- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellMenus.ts
+++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellMenus.ts
@@ -3,16 +3,12 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { IAction } from 'vs/base/common/actions';
-import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
import { IMenu, IMenuService, MenuId } from 'vs/platform/actions/common/actions';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
-import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
export class CellMenus {
constructor(
@IMenuService private readonly menuService: IMenuService,
- @IContextMenuService private readonly contextMenuService: IContextMenuService
) { }
getCellTitleMenu(contextKeyService: IContextKeyService): IMenu {
@@ -26,11 +22,6 @@ export class CellMenus {
private getMenu(menuId: MenuId, contextKeyService: IContextKeyService): IMenu {
const menu = this.menuService.createMenu(menuId, contextKeyService);
- const primary: IAction[] = [];
- const secondary: IAction[] = [];
- const result = { primary, secondary };
-
- createAndFillInContextMenuActions(menu, { shouldForwardArgs: true }, result, this.contextMenuService, g => /^inline/.test(g));
return menu;
}
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 c6ba378683..10adb50a30 100644
--- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts
+++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts
@@ -36,17 +36,18 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { INotificationService } from 'vs/platform/notification/common/notification';
-import { BOTTOM_CELL_TOOLBAR_HEIGHT, EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT, EDITOR_TOP_MARGIN, EDITOR_TOP_PADDING, CELL_BOTTOM_MARGIN } from 'vs/workbench/contrib/notebook/browser/constants';
-import { CancelCellAction, ChangeCellLanguageAction, ExecuteCellAction, INotebookCellActionContext, CELL_TITLE_GROUP_ID } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions';
-import { BaseCellRenderTemplate, CellEditState, CodeCellRenderTemplate, ICellViewModel, INotebookCellList, INotebookEditor, MarkdownCellRenderTemplate, isCodeCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
+import { BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_BOTTOM_MARGIN, EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT, EDITOR_TOP_MARGIN, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants';
+import { CancelCellAction, ChangeCellLanguageAction, ExecuteCellAction, INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions';
+import { BaseCellRenderTemplate, CellEditState, CodeCellRenderTemplate, ICellViewModel, INotebookCellList, INotebookEditor, isCodeCellRenderTemplate, MarkdownCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
+import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys';
import { CellMenus } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellMenus';
import { CodeCell } from 'vs/workbench/contrib/notebook/browser/view/renderers/codeCell';
-import { StatefullMarkdownCell } from 'vs/workbench/contrib/notebook/browser/view/renderers/markdownCell';
+import { StatefulMarkdownCell } from 'vs/workbench/contrib/notebook/browser/view/renderers/markdownCell';
import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel';
import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel';
import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
-import { CellKind, NotebookCellRunState, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
-import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys';
+import { CellKind, NotebookCellMetadata, NotebookCellRunState } from 'vs/workbench/contrib/notebook/common/notebookCommon';
+import { VerticalSeparator, createAndFillInActionBarActionsWithVerticalSeparators, VerticalSeparatorViewItem } from './cellActionView';
const $ = DOM.$;
@@ -204,9 +205,6 @@ abstract class AbstractCellRenderer {
}
});
- toolbar.getContainer().style.height = `${BOTTOM_CELL_TOOLBAR_HEIGHT}px`;
- container.style.height = `${BOTTOM_CELL_TOOLBAR_HEIGHT}px`;
-
const cellMenu = this.instantiationService.createInstance(CellMenus);
const menu = disposables.add(cellMenu.getCellInsertionMenu(contextKeyService));
@@ -220,25 +218,28 @@ abstract class AbstractCellRenderer {
templateData.betweenCellToolbar.context = context;
const container = templateData.bottomCellContainer;
- if (element instanceof CodeCellViewModel) {
+ const bottomToolbarOffset = element.layoutInfo.bottomToolbarOffset;
+ container.style.top = `${bottomToolbarOffset}px`;
+
+ templateData.elementDisposables.add(element.onDidChangeLayout(() => {
const bottomToolbarOffset = element.layoutInfo.bottomToolbarOffset;
container.style.top = `${bottomToolbarOffset}px`;
-
- templateData.elementDisposables.add(element.onDidChangeLayout(() => {
- const bottomToolbarOffset = element.layoutInfo.bottomToolbarOffset;
- container.style.top = `${bottomToolbarOffset}px`;
- }));
- }
+ }));
}
protected createToolbar(container: HTMLElement): ToolBar {
const toolbar = new ToolBar(container, this.contextMenuService, {
+ getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id),
actionViewItemProvider: action => {
if (action instanceof MenuItemAction) {
const item = new ContextAwareMenuEntryActionViewItem(action, this.keybindingService, this.notificationService, this.contextMenuService);
return item;
}
+ if (action.id === VerticalSeparator.ID) {
+ return new VerticalSeparatorViewItem(undefined, action);
+ }
+
return undefined;
}
});
@@ -249,16 +250,11 @@ abstract class AbstractCellRenderer {
private getCellToolbarActions(menu: IMenu): { primary: IAction[], secondary: IAction[] } {
const primary: IAction[] = [];
const secondary: IAction[] = [];
- const actions = menu.getActions({ shouldForwardArgs: true });
- for (let [id, menuActions] of actions) {
- if (id === CELL_TITLE_GROUP_ID) {
- primary.push(...menuActions);
- } else {
- secondary.push(...menuActions);
- }
- }
+ const result = { primary, secondary };
- return { primary, secondary };
+ createAndFillInActionBarActionsWithVerticalSeparators(menu, { shouldForwardArgs: true }, result, g => /^inline/.test(g));
+
+ return result;
}
protected setupCellToolbarActions(templateData: BaseCellRenderTemplate, disposables: DisposableStore): void {
@@ -351,9 +347,7 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR
const foldingIndicator = DOM.append(focusIndicator, DOM.$('.notebook-folding-indicator'));
const bottomCellContainer = DOM.append(container, $('.cell-bottom-toolbar-container'));
- DOM.append(bottomCellContainer, $('.separator'));
const betweenCellToolbar = disposables.add(this.createBetweenCellToolbar(bottomCellContainer, disposables, contextKeyService));
- DOM.append(bottomCellContainer, $('.separator'));
const statusBar = this.instantiationService.createInstance(CellEditorStatusBar, editorPart);
const titleMenu = disposables.add(this.cellMenus.getCellTitleMenu(contextKeyService));
@@ -439,7 +433,8 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR
this.setBetweenCellToolbarContext(templateData, element, toolbarContext);
- const markdownCell = this.instantiationService.createInstance(StatefullMarkdownCell, this.notebookEditor, element, templateData, this.editorOptions.value, this.renderedEditors);
+ const scopedInstaService = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, templateData.contextKeyService]));
+ const markdownCell = scopedInstaService.createInstance(StatefulMarkdownCell, this.notebookEditor, element, templateData, this.editorOptions.value, this.renderedEditors);
elementDisposables.add(this.editorOptions.onDidChange(newValue => markdownCell.updateEditorOptions(newValue)));
elementDisposables.add(markdownCell);
@@ -611,6 +606,18 @@ export class CellDragAndDropController extends Disposable {
private onCellDrop(event: CellDragEvent): void {
const draggedCell = this.currentDraggedCell!;
+ let draggedCells: ICellViewModel[] = [draggedCell];
+
+ if (draggedCell.cellKind === CellKind.Markdown) {
+ const currCellIndex = this.notebookEditor.viewModel!.getCellIndex(draggedCell);
+ const nextVisibleCellIndex = this.notebookEditor.viewModel!.getNextVisibleCellIndex(currCellIndex);
+
+ if (nextVisibleCellIndex > currCellIndex + 1) {
+ // folding ;)
+ draggedCells = this.notebookEditor.viewModel!.viewCells.slice(currCellIndex, nextVisibleCellIndex);
+ }
+ }
+
this.dragCleanup();
const isCopy = (event.browserEvent.ctrlKey && !platform.isMacintosh) || (event.browserEvent.altKey && platform.isMacintosh);
@@ -625,9 +632,9 @@ export class CellDragAndDropController extends Disposable {
}
if (isCopy) {
- this.copyCell(draggedCell, event.draggedOverCell, dropDirection);
+ this.copyCells(draggedCells, event.draggedOverCell, dropDirection);
} else {
- this.moveCell(draggedCell, event.draggedOverCell, dropDirection);
+ this.moveCells(draggedCells, event.draggedOverCell, dropDirection);
}
}
@@ -673,16 +680,37 @@ export class CellDragAndDropController extends Disposable {
}));
}
- private async moveCell(draggedCell: ICellViewModel, ontoCell: ICellViewModel, direction: 'above' | 'below') {
- await this.notebookEditor.moveCell(draggedCell, ontoCell, direction);
+ private async moveCells(draggedCells: ICellViewModel[], ontoCell: ICellViewModel, direction: 'above' | 'below') {
+ const relativeToIndex = this.notebookEditor!.viewModel!.getCellIndex(ontoCell);
+ const newIdx = direction === 'above' ? relativeToIndex : relativeToIndex + 1;
+
+ this.notebookEditor.textModel!.pushStackElement('Move Cells');
+ for (let i = draggedCells.length - 1; i >= 0; i--) {
+ await this.notebookEditor.moveCellToIdx(draggedCells[i], newIdx);
+ }
+
+ this.notebookEditor.textModel!.pushStackElement('Move Cells');
}
- private copyCell(draggedCell: ICellViewModel, ontoCell: ICellViewModel, direction: 'above' | 'below') {
- const editState = draggedCell.editState;
- const newCell = this.notebookEditor.insertNotebookCell(ontoCell, draggedCell.cellKind, direction, draggedCell.getText());
- if (newCell) {
- this.notebookEditor.focusNotebookCell(newCell, editState === CellEditState.Editing ? 'editor' : 'container');
+ private copyCells(draggedCells: ICellViewModel[], ontoCell: ICellViewModel, direction: 'above' | 'below') {
+ this.notebookEditor.textModel!.pushStackElement('Copy Cells');
+ let firstNewCell: ICellViewModel | undefined = undefined;
+ let firstNewCellState: CellEditState = CellEditState.Preview;
+ for (let i = 0; i < draggedCells.length; i++) {
+ const draggedCell = draggedCells[i];
+ const newCell = this.notebookEditor.insertNotebookCell(ontoCell, draggedCell.cellKind, direction, draggedCell.getText());
+
+ if (newCell && !firstNewCell) {
+ firstNewCell = newCell;
+ firstNewCellState = draggedCell.editState;
+ }
}
+
+ if (firstNewCell) {
+ this.notebookEditor.focusNotebookCell(firstNewCell, firstNewCellState === CellEditState.Editing ? 'editor' : 'container');
+ }
+
+ this.notebookEditor.textModel!.pushStackElement('Copy Cells');
}
}
@@ -912,11 +940,8 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende
const focusSinkElement = DOM.append(container, $('.cell-editor-focus-sink'));
focusSinkElement.setAttribute('tabindex', '0');
const bottomCellContainer = DOM.append(container, $('.cell-bottom-toolbar-container'));
- DOM.append(bottomCellContainer, $('.separator'));
- const betweenCellToolbar = this.createBetweenCellToolbar(bottomCellContainer, disposables, contextKeyService);
- DOM.append(bottomCellContainer, $('.separator'));
-
const focusIndicatorBottom = DOM.append(container, $('.cell-focus-indicator.cell-focus-indicator-bottom'));
+ const betweenCellToolbar = this.createBetweenCellToolbar(bottomCellContainer, disposables, contextKeyService);
const titleMenu = disposables.add(this.cellMenus.getCellTitleMenu(contextKeyService));
diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts
index 485d4d2b82..750329a39d 100644
--- a/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts
+++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts
@@ -21,7 +21,7 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/renderers/sizeObserver';
-export class StatefullMarkdownCell extends Disposable {
+export class StatefulMarkdownCell extends Disposable {
private editor: CodeEditorWidget | null = null;
private markdownContainer: HTMLElement;
@@ -95,7 +95,7 @@ export class StatefullMarkdownCell extends Disposable {
this._register(viewCell.onDidChangeLayout((e) => {
const layoutInfo = this.editor?.getLayoutInfo();
- if (e.outerWidth && layoutInfo && layoutInfo.width !== viewCell.layoutInfo.editorWidth) {
+ if (e.outerWidth && this.viewCell.editState === CellEditState.Editing && layoutInfo && layoutInfo.width !== viewCell.layoutInfo.editorWidth) {
this.onCellEditorWidthChange();
} else if (e.totalHeight || e.outerWidth) {
this.relayoutCell();
diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts
index 1557de97cf..464d7bdac3 100644
--- a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts
+++ b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts
@@ -8,7 +8,7 @@ import * as UUID from 'vs/base/common/uuid';
import * as editorCommon from 'vs/editor/common/editorCommon';
import * as model from 'vs/editor/common/model';
import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer';
-import { BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_MARGIN, CELL_RUN_GUTTER, CELL_STATUSBAR_HEIGHT, EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT, EDITOR_TOP_MARGIN, EDITOR_TOP_PADDING, CELL_BOTTOM_MARGIN, CODE_CELL_LEFT_MARGIN } from 'vs/workbench/contrib/notebook/browser/constants';
+import { BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_MARGIN, CELL_RUN_GUTTER, CELL_STATUSBAR_HEIGHT, EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT, EDITOR_TOP_MARGIN, EDITOR_TOP_PADDING, CELL_BOTTOM_MARGIN, CODE_CELL_LEFT_MARGIN, BOTTOM_CELL_TOOLBAR_OFFSET } from 'vs/workbench/contrib/notebook/browser/constants';
import { CellEditState, CellFindMatch, CodeCellLayoutChangeEvent, CodeCellLayoutInfo, ICellViewModel, NotebookLayoutInfo, CodeCellLayoutState } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { CellKind, NotebookCellOutputsSplice, INotebookSearchOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon';
@@ -122,7 +122,7 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
const indicatorHeight = editorHeight + CELL_STATUSBAR_HEIGHT + outputTotalHeight;
const outputContainerOffset = EDITOR_TOOLBAR_HEIGHT + EDITOR_TOP_MARGIN + editorHeight + CELL_STATUSBAR_HEIGHT;
- const bottomToolbarOffset = totalHeight - BOTTOM_CELL_TOOLBAR_HEIGHT;
+ const bottomToolbarOffset = totalHeight - BOTTOM_CELL_TOOLBAR_HEIGHT - BOTTOM_CELL_TOOLBAR_OFFSET;
const editorWidth = state.outerWidth !== undefined ? this.computeEditorWidth(state.outerWidth) : this._layoutInfo?.editorWidth;
this._layoutInfo = {
diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts
index 5814924469..0e5e7700c0 100644
--- a/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts
+++ b/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts
@@ -8,7 +8,7 @@ import { Emitter, Event } from 'vs/base/common/event';
import * as UUID from 'vs/base/common/uuid';
import * as editorCommon from 'vs/editor/common/editorCommon';
import * as model from 'vs/editor/common/model';
-import { BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_MARGIN, CELL_STATUSBAR_HEIGHT, EDITOR_TOP_MARGIN, CELL_BOTTOM_MARGIN, CODE_CELL_LEFT_MARGIN } from 'vs/workbench/contrib/notebook/browser/constants';
+import { BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_MARGIN, CELL_STATUSBAR_HEIGHT, EDITOR_TOP_MARGIN, CELL_BOTTOM_MARGIN, CODE_CELL_LEFT_MARGIN, BOTTOM_CELL_TOOLBAR_OFFSET } from 'vs/workbench/contrib/notebook/browser/constants';
import { CellFindMatch, ICellViewModel, MarkdownCellLayoutChangeEvent, MarkdownCellLayoutInfo, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { MarkdownRenderer } from 'vs/workbench/contrib/notebook/browser/view/renderers/mdRenderer';
import { BaseCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel';
@@ -93,13 +93,14 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie
layoutChange(state: MarkdownCellLayoutChangeEvent) {
// recompute
const editorWidth = state.outerWidth !== undefined ? this.computeEditorWidth(state.outerWidth) : this._layoutInfo.editorWidth;
+ const totalHeight = state.totalHeight === undefined ? this._layoutInfo.totalHeight : state.totalHeight;
this._layoutInfo = {
fontInfo: state.font || this._layoutInfo.fontInfo,
editorWidth,
editorHeight: this._editorHeight,
- bottomToolbarOffset: BOTTOM_CELL_TOOLBAR_HEIGHT,
- totalHeight: state.totalHeight === undefined ? this._layoutInfo.totalHeight : state.totalHeight
+ bottomToolbarOffset: totalHeight - BOTTOM_CELL_TOOLBAR_HEIGHT - BOTTOM_CELL_TOOLBAR_OFFSET,
+ totalHeight
};
this._onDidChangeLayout.fire(state);
@@ -115,6 +116,7 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie
totalHeight: totalHeight,
editorHeight: this._editorHeight
};
+ this.layoutChange({});
}
}
diff --git a/src/vs/workbench/contrib/notebook/common/model/cellEdit.ts b/src/vs/workbench/contrib/notebook/common/model/cellEdit.ts
index 6adc6bc981..cbb9c2dfe3 100644
--- a/src/vs/workbench/contrib/notebook/common/model/cellEdit.ts
+++ b/src/vs/workbench/contrib/notebook/common/model/cellEdit.ts
@@ -31,7 +31,7 @@ export class InsertCellEdit implements IResourceUndoRedoElement {
) {
}
- undo(): void | Promise {
+ undo(): void {
if (!this.editingDelegate.deleteCell) {
throw new Error('Notebook Delete Cell not implemented for Undo/Redo');
}
@@ -41,7 +41,7 @@ export class InsertCellEdit implements IResourceUndoRedoElement {
this.editingDelegate.emitSelections(this.beforedSelections);
}
}
- redo(): void | Promise {
+ redo(): void {
if (!this.editingDelegate.insertCell) {
throw new Error('Notebook Insert Cell not implemented for Undo/Redo');
}
@@ -70,7 +70,7 @@ export class DeleteCellEdit implements IResourceUndoRedoElement {
// this._rawCell.source = [cell.getText()];
}
- undo(): void | Promise {
+ undo(): void {
if (!this.editingDelegate.insertCell) {
throw new Error('Notebook Insert Cell not implemented for Undo/Redo');
}
@@ -81,7 +81,7 @@ export class DeleteCellEdit implements IResourceUndoRedoElement {
}
}
- redo(): void | Promise {
+ redo(): void {
if (!this.editingDelegate.deleteCell) {
throw new Error('Notebook Delete Cell not implemented for Undo/Redo');
}
@@ -95,7 +95,7 @@ export class DeleteCellEdit implements IResourceUndoRedoElement {
export class MoveCellEdit implements IResourceUndoRedoElement {
type: UndoRedoElementType.Resource = UndoRedoElementType.Resource;
- label: string = 'Delete Cell';
+ label: string = 'Move Cell';
constructor(
public resource: URI,
@@ -107,7 +107,7 @@ export class MoveCellEdit implements IResourceUndoRedoElement {
) {
}
- undo(): void | Promise {
+ undo(): void {
if (!this.editingDelegate.moveCell) {
throw new Error('Notebook Move Cell not implemented for Undo/Redo');
}
@@ -118,7 +118,7 @@ export class MoveCellEdit implements IResourceUndoRedoElement {
}
}
- redo(): void | Promise {
+ redo(): void {
if (!this.editingDelegate.moveCell) {
throw new Error('Notebook Move Cell not implemented for Undo/Redo');
}
@@ -142,7 +142,7 @@ export class SpliceCellsEdit implements IResourceUndoRedoElement {
) {
}
- undo(): void | Promise {
+ undo(): void {
if (!this.editingDelegate.deleteCell || !this.editingDelegate.insertCell) {
throw new Error('Notebook Insert/Delete Cell not implemented for Undo/Redo');
}
@@ -162,7 +162,7 @@ export class SpliceCellsEdit implements IResourceUndoRedoElement {
}
}
- redo(): void | Promise {
+ redo(): void {
if (!this.editingDelegate.deleteCell || !this.editingDelegate.insertCell) {
throw new Error('Notebook Insert/Delete Cell not implemented for Undo/Redo');
}
diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts
index 93ae922efa..0f6a5d16e4 100644
--- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts
+++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts
@@ -10,7 +10,7 @@ import { URI } from 'vs/base/common/uri';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { INotebookTextModel, NotebookCellOutputsSplice, NotebookCellTextModelSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, CellEditType, CellUri, ICellInsertEdit, NotebookCellsChangedEvent, CellKind, IProcessedOutput, notebookDocumentMetadataDefaults, diff, ICellDeleteEdit, NotebookCellsChangeType, ICellDto2, IMainCellDto } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { ITextSnapshot } from 'vs/editor/common/model';
-import { IUndoRedoService, UndoRedoElementType } from 'vs/platform/undoRedo/common/undoRedo';
+import { IUndoRedoService, UndoRedoElementType, IUndoRedoElement, IResourceUndoRedoElement } from 'vs/platform/undoRedo/common/undoRedo';
import { InsertCellEdit, DeleteCellEdit, MoveCellEdit, SpliceCellsEdit } from 'vs/workbench/contrib/notebook/common/model/cellEdit';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
@@ -66,6 +66,53 @@ export class NotebookTextModelSnapshot implements ITextSnapshot {
}
+class StackOperation implements IResourceUndoRedoElement {
+ type: UndoRedoElementType.Resource;
+
+ private _operations: IUndoRedoElement[] = [];
+
+ constructor(readonly resource: URI, readonly label: string) {
+ this.type = UndoRedoElementType.Resource;
+ }
+
+ pushEditOperation(element: IUndoRedoElement) {
+ this._operations.push(element);
+ }
+
+ undo(): void {
+ this._operations.reverse().forEach(o => o.undo());
+ }
+ redo(): void | Promise {
+ this._operations.forEach(o => o.redo());
+ }
+}
+
+export class NotebookOperationManager {
+ private _pendingStackOperation: StackOperation | null = null;
+ constructor(private _undoService: IUndoRedoService, private _resource: URI) {
+
+ }
+
+ pushStackElement(label: string) {
+ if (this._pendingStackOperation) {
+ this._undoService.pushElement(this._pendingStackOperation);
+ this._pendingStackOperation = null;
+ return;
+ }
+
+ this._pendingStackOperation = new StackOperation(this._resource, label);
+ }
+
+ pushEditOperation(element: IUndoRedoElement) {
+ if (this._pendingStackOperation) {
+ this._pendingStackOperation.pushEditOperation(element);
+ return;
+ }
+
+ this._undoService.pushElement(element);
+ }
+}
+
export class NotebookTextModel extends Disposable implements INotebookTextModel {
private _cellhandlePool: number = 0;
@@ -112,6 +159,8 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
protected readonly _onDidChangeDirty = this._register(new Emitter());
readonly onDidChangeDirty = this._onDidChangeDirty.event;
+ private _operationManager: NotebookOperationManager;
+
constructor(
public handle: number,
public viewType: string,
@@ -122,6 +171,8 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
) {
super();
this.cells = [];
+
+ this._operationManager = new NotebookOperationManager(this._undoService, uri);
}
get isDirty() {
@@ -173,6 +224,10 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
this._increaseVersionId();
}
+ pushStackElement(label: string) {
+ this._operationManager.pushStackElement(label);
+ }
+
$applyEdit(modelVersionId: number, rawEdits: ICellEditOperation[], synchronous: boolean): boolean {
if (modelVersionId !== this._versionId) {
return false;
@@ -255,7 +310,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
return [diff[0], deletedCells, diff[2]] as [number, NotebookCellTextModel[], NotebookCellTextModel[]];
});
- this._undoService.pushElement(new SpliceCellsEdit(this.uri, undoDiff, {
+ this._operationManager.pushEditOperation(new SpliceCellsEdit(this.uri, undoDiff, {
insertCell: this._insertCellDelegate.bind(this),
deleteCell: this._deleteCellDelegate.bind(this),
emitSelections: this._emitSelectionsDelegate.bind(this)
@@ -266,7 +321,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
}
$handleEdit(label: string | undefined, undo: () => void, redo: () => void): void {
- this._undoService.pushElement({
+ this._operationManager.pushEditOperation({
type: UndoRedoElementType.Resource,
resource: this.uri,
label: label ?? nls.localize('defaultEditLabel', "Edit"),
@@ -502,7 +557,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
const cell = this.createCellTextModel(source, language, type, [], metadata);
if (pushUndoStop) {
- this._undoService.pushElement(new InsertCellEdit(this.uri, index, cell, {
+ this._operationManager.pushEditOperation(new InsertCellEdit(this.uri, index, cell, {
insertCell: this._insertCellDelegate.bind(this),
deleteCell: this._deleteCellDelegate.bind(this),
emitSelections: this._emitSelectionsDelegate.bind(this)
@@ -522,7 +577,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
insertCell2(index: number, cell: NotebookCellTextModel, synchronous: boolean, pushUndoStop: boolean): void {
if (pushUndoStop) {
- this._undoService.pushElement(new InsertCellEdit(this.uri, index, cell, {
+ this._operationManager.pushEditOperation(new InsertCellEdit(this.uri, index, cell, {
insertCell: this._insertCellDelegate.bind(this),
deleteCell: this._deleteCellDelegate.bind(this),
emitSelections: this._emitSelectionsDelegate.bind(this)
@@ -536,7 +591,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
deleteCell2(index: number, synchronous: boolean, pushUndoStop: boolean, beforeSelections: number[] | undefined, endSelections: number[] | undefined) {
const cell = this.cells[index];
if (pushUndoStop) {
- this._undoService.pushElement(new DeleteCellEdit(this.uri, index, cell, {
+ this._operationManager.pushEditOperation(new DeleteCellEdit(this.uri, index, cell, {
insertCell: this._insertCellDelegate.bind(this),
deleteCell: this._deleteCellDelegate.bind(this),
emitSelections: this._emitSelectionsDelegate.bind(this)
@@ -553,7 +608,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
moveCellToIdx2(index: number, newIdx: number, synchronous: boolean, pushedToUndoStack: boolean, beforeSelections: number[] | undefined, endSelections: number[] | undefined): boolean {
const cell = this.cells[index];
if (pushedToUndoStack) {
- this._undoService.pushElement(new MoveCellEdit(this.uri, index, newIdx, {
+ this._operationManager.pushEditOperation(new MoveCellEdit(this.uri, index, newIdx, {
moveCell: (fromIndex: number, toIndex: number, beforeSelections: number[] | undefined, endSelections: number[] | undefined) => {
this.moveCellToIdx2(fromIndex, toIndex, true, false, beforeSelections, endSelections);
},
diff --git a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts
index 06ecdd65f4..c8727e9ec6 100644
--- a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts
+++ b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts
@@ -63,6 +63,8 @@ export class TestNotebookEditor implements INotebookEditor {
) { }
multipleKernelsAvailable: boolean = false;
+ onDidChangeAvailableKernels: Event = new Emitter().event;
+
uri?: URI | undefined;
textModel?: NotebookTextModel | undefined;
@@ -158,6 +160,10 @@ export class TestNotebookEditor implements INotebookEditor {
throw new Error('Method not implemented.');
}
+ moveCellToIdx(cell: ICellViewModel, index: number): Promise {
+ throw new Error('Method not implemented.');
+ }
+
moveCell(cell: ICellViewModel, relativeToCell: ICellViewModel, direction: 'above' | 'below'): Promise {
throw new Error('Method not implemented.');
}
diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts
index b733a19a0e..c6ae6cdece 100644
--- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts
+++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts
@@ -883,7 +883,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
resolve(undefined);
}
} else {
- resolve(this.executeTask(task, resolver));
+ resolve(this.executeTask(task, resolver, runSource));
}
}).then((value) => {
if (runSource === TaskRunSource.User) {
@@ -1452,7 +1452,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
};
}
- private executeTask(task: Task, resolver: ITaskResolver): Promise {
+ private executeTask(task: Task, resolver: ITaskResolver, runSource?: TaskRunSource): Promise {
enum SaveBeforeRunConfigOptions {
Always = 'always',
Never = 'never',
@@ -1464,7 +1464,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
const execTask = async (task: Task, resolver: ITaskResolver): Promise => {
return ProblemMatcherRegistry.onReady().then(() => {
let executeResult = this.getTaskSystem().run(task, resolver);
- return this.handleExecuteResult(executeResult);
+ return this.handleExecuteResult(executeResult, runSource);
});
};
@@ -1501,7 +1501,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
}
}
- private async handleExecuteResult(executeResult: ITaskExecuteResult): Promise {
+ private async handleExecuteResult(executeResult: ITaskExecuteResult, runSource?: TaskRunSource): Promise {
if (executeResult.task.taskLoadMessages && executeResult.task.taskLoadMessages.length > 0) {
executeResult.task.taskLoadMessages.forEach(loadMessage => {
this._outputChannel.append(loadMessage + '\n');
@@ -1509,7 +1509,9 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
this.showOutput();
}
- await this.setRecentlyUsedTask(executeResult.task);
+ if (runSource === TaskRunSource.User) {
+ await this.setRecentlyUsedTask(executeResult.task);
+ }
if (executeResult.kind === TaskExecuteKind.Active) {
let active = executeResult.active;
if (active && active.same) {
diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts
index 906d744634..bbe073fee7 100644
--- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts
+++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts
@@ -840,6 +840,9 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
setTimeout(() => this.layout(this._timeoutDimension!), 0);
}
}
+ if (!visible) {
+ this._widgetManager.hideHovers();
+ }
}
public scrollDownLine(): void {
diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts
index 4e1322bd7d..bf910299ce 100644
--- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts
+++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts
@@ -110,6 +110,8 @@ export class TerminalViewPane extends ViewPane {
} else {
this.layoutBody(this._bodyDimensions.height, this._bodyDimensions.width);
}
+ } else {
+ this._terminalService.getActiveTab()?.setVisible(false);
}
}));
diff --git a/src/vs/workbench/contrib/terminal/browser/widgets/widgetManager.ts b/src/vs/workbench/contrib/terminal/browser/widgets/widgetManager.ts
index f8090a35cf..fd83be63aa 100644
--- a/src/vs/workbench/contrib/terminal/browser/widgets/widgetManager.ts
+++ b/src/vs/workbench/contrib/terminal/browser/widgets/widgetManager.ts
@@ -5,11 +5,15 @@
import { IDisposable } from 'vs/base/common/lifecycle';
import { ITerminalWidget } from 'vs/workbench/contrib/terminal/browser/widgets/widgets';
+import { IHoverService } from 'vs/workbench/services/hover/browser/hover';
export class TerminalWidgetManager implements IDisposable {
private _container: HTMLElement | undefined;
private _attached: Map = new Map();
+ constructor(@IHoverService private readonly _hoverService: IHoverService) {
+ }
+
attachToElement(terminalWrapper: HTMLElement) {
if (!this._container) {
this._container = document.createElement('div');
@@ -25,6 +29,10 @@ export class TerminalWidgetManager implements IDisposable {
}
}
+ hideHovers(): void {
+ this._hoverService.hideHover();
+ }
+
attachWidget(widget: ITerminalWidget): IDisposable | undefined {
if (!this._container) {
return undefined; // {{SQL CARBON EDIT}} strict-null-check
diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataManualSyncView.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncMergesView.ts
similarity index 97%
rename from src/vs/workbench/contrib/userDataSync/browser/userDataManualSyncView.ts
rename to src/vs/workbench/contrib/userDataSync/browser/userDataSyncMergesView.ts
index e6b505b73b..6d0798acbe 100644
--- a/src/vs/workbench/contrib/userDataSync/browser/userDataManualSyncView.ts
+++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncMergesView.ts
@@ -16,7 +16,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable, dispose } from 'vs/base/common/lifecycle';
import { Codicon } from 'vs/base/common/codicons';
-import { IUserDataSyncWorkbenchService, getSyncAreaLabel, IUserDataSyncPreview, IUserDataSyncResource, MANUAL_SYNC_VIEW_ID } from 'vs/workbench/services/userDataSync/common/userDataSync';
+import { IUserDataSyncWorkbenchService, getSyncAreaLabel, IUserDataSyncPreview, IUserDataSyncResource, SYNC_MERGES_VIEW_ID } from 'vs/workbench/services/userDataSync/common/userDataSync';
import { isEqual, basename } from 'vs/base/common/resources';
import { IDecorationsProvider, IDecorationData, IDecorationsService } from 'vs/workbench/services/decorations/browser/decorations';
import { IProgressService } from 'vs/platform/progress/common/progress';
@@ -39,7 +39,7 @@ import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { Severity } from 'vs/platform/notification/common/notification';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
-export class UserDataManualSyncViewPane extends TreeViewPane {
+export class UserDataSyncMergesViewPane extends TreeViewPane {
private userDataSyncPreview: IUserDataSyncPreview;
@@ -83,7 +83,7 @@ export class UserDataManualSyncViewPane extends TreeViewPane {
this.createButtons(container);
const that = this;
- this.treeView.message = localize('explanation', "Please go through each entry and accept the change to enable sync.");
+ this.treeView.message = localize('explanation', "Please go through each entry and merge to enable sync.");
this.treeView.dataProvider = { getChildren() { return that.getTreeItems(); } };
}
@@ -164,7 +164,7 @@ export class UserDataManualSyncViewPane extends TreeViewPane {
icon: Codicon.cloudDownload,
menu: {
id: MenuId.ViewItemContext,
- when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', MANUAL_SYNC_VIEW_ID), ContextKeyExpr.equals('viewItem', 'sync-resource-preview')),
+ when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', SYNC_MERGES_VIEW_ID), ContextKeyExpr.equals('viewItem', 'sync-resource-preview')),
group: 'inline',
order: 1,
},
@@ -184,7 +184,7 @@ export class UserDataManualSyncViewPane extends TreeViewPane {
icon: Codicon.cloudUpload,
menu: {
id: MenuId.ViewItemContext,
- when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', MANUAL_SYNC_VIEW_ID), ContextKeyExpr.equals('viewItem', 'sync-resource-preview')),
+ when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', SYNC_MERGES_VIEW_ID), ContextKeyExpr.equals('viewItem', 'sync-resource-preview')),
group: 'inline',
order: 2,
},
@@ -204,7 +204,7 @@ export class UserDataManualSyncViewPane extends TreeViewPane {
icon: Codicon.merge,
menu: {
id: MenuId.ViewItemContext,
- when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', MANUAL_SYNC_VIEW_ID), ContextKeyExpr.equals('viewItem', 'sync-resource-preview')),
+ when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', SYNC_MERGES_VIEW_ID), ContextKeyExpr.equals('viewItem', 'sync-resource-preview')),
group: 'inline',
order: 3,
},
@@ -224,7 +224,7 @@ export class UserDataManualSyncViewPane extends TreeViewPane {
icon: Codicon.discard,
menu: {
id: MenuId.ViewItemContext,
- when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', MANUAL_SYNC_VIEW_ID), ContextKeyExpr.or(ContextKeyExpr.equals('viewItem', 'sync-resource-accepted'), ContextKeyExpr.equals('viewItem', 'sync-resource-conflict'))),
+ when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', SYNC_MERGES_VIEW_ID), ContextKeyExpr.or(ContextKeyExpr.equals('viewItem', 'sync-resource-accepted'), ContextKeyExpr.equals('viewItem', 'sync-resource-conflict'))),
group: 'inline',
order: 3,
},
@@ -270,7 +270,7 @@ export class UserDataManualSyncViewPane extends TreeViewPane {
previewResource = this.userDataSyncPreview.resources.find(({ local }) => isEqual(local, previewResource.local))!;
await this.reopen(previewResource);
if (previewResource.mergeState === MergeState.Conflict) {
- await this.dialogService.show(Severity.Warning, localize('conflicts detected', "Conflicts Detected."), [], {
+ await this.dialogService.show(Severity.Warning, localize('conflicts detected', "Conflicts Detected"), [], {
detail: localize('resolve', "Unable to merge due to conflicts. Please resolve them to continue.")
});
}
@@ -373,7 +373,7 @@ export class UserDataManualSyncViewPane extends TreeViewPane {
}
private withProgress(task: () => Promise): Promise {
- return this.progressService.withProgress({ location: MANUAL_SYNC_VIEW_ID, delay: 500 }, task);
+ return this.progressService.withProgress({ location: SYNC_MERGES_VIEW_ID, delay: 500 }, task);
}
}
diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts
index 70679fe701..8e32e07a6f 100644
--- a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts
+++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts
@@ -30,13 +30,13 @@ import { IStorageService } from 'vs/platform/storage/common/storage';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IAction, Action } from 'vs/base/common/actions';
-import { IUserDataSyncWorkbenchService, CONTEXT_SYNC_STATE, getSyncAreaLabel, CONTEXT_ACCOUNT_STATE, AccountStatus, CONTEXT_ENABLE_ACTIVITY_VIEWS, SHOW_SYNC_LOG_COMMAND_ID, CONFIGURE_SYNC_COMMAND_ID, MANUAL_SYNC_VIEW_ID, CONTEXT_ENABLE_MANUAL_SYNC_VIEW } from 'vs/workbench/services/userDataSync/common/userDataSync';
+import { IUserDataSyncWorkbenchService, CONTEXT_SYNC_STATE, getSyncAreaLabel, CONTEXT_ACCOUNT_STATE, AccountStatus, CONTEXT_ENABLE_ACTIVITY_VIEWS, SHOW_SYNC_LOG_COMMAND_ID, CONFIGURE_SYNC_COMMAND_ID, SYNC_MERGES_VIEW_ID, CONTEXT_ENABLE_SYNC_MERGES_VIEW } from 'vs/workbench/services/userDataSync/common/userDataSync';
import { IUserDataSyncMachinesService, IUserDataSyncMachine } from 'vs/platform/userDataSync/common/userDataSyncMachines';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { TreeView } from 'vs/workbench/contrib/views/browser/treeView';
import { flatten } from 'vs/base/common/arrays';
-import { UserDataManualSyncViewPane } from 'vs/workbench/contrib/userDataSync/browser/userDataManualSyncView';
+import { UserDataSyncMergesViewPane } from 'vs/workbench/contrib/userDataSync/browser/userDataSyncMergesView';
export class UserDataSyncViewPaneContainer extends ViewPaneContainer {
@@ -86,7 +86,7 @@ export class UserDataSyncDataViews extends Disposable {
}
private registerViews(container: ViewContainer): void {
- this.registerManualSyncView(container);
+ this.registerMergesView(container);
this.registerActivityView(container, true);
this.registerMachinesView(container);
@@ -94,17 +94,17 @@ export class UserDataSyncDataViews extends Disposable {
this.registerActivityView(container, false);
}
- private registerManualSyncView(container: ViewContainer): void {
+ private registerMergesView(container: ViewContainer): void {
const viewsRegistry = Registry.as(Extensions.ViewsRegistry);
- const viewName = localize('manual sync', "Manual Sync");
+ const viewName = localize('merges', "Merges");
viewsRegistry.registerViews([{
- id: MANUAL_SYNC_VIEW_ID,
+ id: SYNC_MERGES_VIEW_ID,
name: viewName,
- ctorDescriptor: new SyncDescriptor(UserDataManualSyncViewPane),
- when: CONTEXT_ENABLE_MANUAL_SYNC_VIEW,
+ ctorDescriptor: new SyncDescriptor(UserDataSyncMergesViewPane),
+ when: CONTEXT_ENABLE_SYNC_MERGES_VIEW,
canToggleVisibility: false,
canMoveView: false,
- treeView: this.instantiationService.createInstance(TreeView, MANUAL_SYNC_VIEW_ID, viewName),
+ treeView: this.instantiationService.createInstance(TreeView, SYNC_MERGES_VIEW_ID, viewName),
collapsed: false,
order: 100,
}], container);
diff --git a/src/vs/workbench/services/editor/common/editorOpenWith.ts b/src/vs/workbench/services/editor/common/editorOpenWith.ts
index d19e16ed7a..7282621db2 100644
--- a/src/vs/workbench/services/editor/common/editorOpenWith.ts
+++ b/src/vs/workbench/services/editor/common/editorOpenWith.ts
@@ -49,7 +49,12 @@ export async function openEditorWith(
return undefined; // {{SQL CARBON EDIT}} strict-null-checks
}
- const overrideToUse = typeof id === 'string' && allEditorOverrides.find(([_, entry]) => entry.id === id);
+ let overrideToUse;
+ if (typeof id === 'string') {
+ overrideToUse = allEditorOverrides.find(([_, entry]) => entry.id === id);
+ } else if (allEditorOverrides.length === 1) {
+ overrideToUse = allEditorOverrides[0];
+ }
if (overrideToUse) {
return overrideToUse[0].open(input, overrideOptions, group, OpenEditorContext.NEW_EDITOR)?.override;
}
diff --git a/src/vs/workbench/services/extensions/common/extensionHostManager.ts b/src/vs/workbench/services/extensions/common/extensionHostManager.ts
index adbe01518e..2e75f1d28c 100644
--- a/src/vs/workbench/services/extensions/common/extensionHostManager.ts
+++ b/src/vs/workbench/services/extensions/common/extensionHostManager.ts
@@ -184,6 +184,7 @@ export class ExtensionHostManager extends Disposable {
getProxy: (identifier: ProxyIdentifier): T => this._rpcProtocol!.getProxy(identifier),
set: (identifier: ProxyIdentifier, instance: R): R => this._rpcProtocol!.set(identifier, instance),
assertRegistered: (identifiers: ProxyIdentifier[]): void => this._rpcProtocol!.assertRegistered(identifiers),
+ drain: (): Promise => this._rpcProtocol!.drain(),
};
// Named customers
diff --git a/src/vs/workbench/services/extensions/common/proxyIdentifier.ts b/src/vs/workbench/services/extensions/common/proxyIdentifier.ts
index b8de7fdfdc..04378c9a84 100644
--- a/src/vs/workbench/services/extensions/common/proxyIdentifier.ts
+++ b/src/vs/workbench/services/extensions/common/proxyIdentifier.ts
@@ -18,6 +18,11 @@ export interface IRPCProtocol {
* Assert these identifiers are already registered via `.set`.
*/
assertRegistered(identifiers: ProxyIdentifier[]): void;
+
+ /**
+ * Wait for the write buffer (if applicable) to become empty.
+ */
+ drain(): Promise;
}
export class ProxyIdentifier {
diff --git a/src/vs/workbench/services/extensions/common/rpcProtocol.ts b/src/vs/workbench/services/extensions/common/rpcProtocol.ts
index 5631263fed..be01321cdd 100644
--- a/src/vs/workbench/services/extensions/common/rpcProtocol.ts
+++ b/src/vs/workbench/services/extensions/common/rpcProtocol.ts
@@ -115,6 +115,13 @@ export class RPCProtocol extends Disposable implements IRPCProtocol {
});
}
+ public drain(): Promise {
+ if (typeof this._protocol.drain === 'function') {
+ return this._protocol.drain();
+ }
+ return Promise.resolve();
+ }
+
private _onWillSendRequest(req: number): void {
if (this._unacknowledgedCount === 0) {
// Since this is the first request we are sending in a while,
diff --git a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts
index 8bbc69a707..4aba981575 100644
--- a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts
+++ b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts
@@ -96,10 +96,10 @@ let onTerminate = function () {
nativeExit();
};
-function _createExtHostProtocol(): Promise {
+function _createExtHostProtocol(): Promise {
if (process.env.VSCODE_EXTHOST_WILL_SEND_SOCKET) {
- return new Promise((resolve, reject) => {
+ return new Promise((resolve, reject) => {
let protocol: PersistentProtocol | null = null;
@@ -163,7 +163,7 @@ function _createExtHostProtocol(): Promise {
const pipeName = process.env.VSCODE_IPC_HOOK_EXTHOST!;
- return new Promise((resolve, reject) => {
+ return new Promise((resolve, reject) => {
const socket = net.createConnection(pipeName, () => {
socket.removeListener('error', reject);
@@ -203,6 +203,10 @@ async function createExtHostProtocol(): Promise {
protocol.send(msg);
}
}
+
+ drain(): Promise {
+ return protocol.drain();
+ }
};
}
diff --git a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts
index ca8871e31d..e75b8365d7 100644
--- a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts
+++ b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts
@@ -6,7 +6,7 @@
import { IUserDataSyncService, IAuthenticationProvider, getUserDataSyncStore, isAuthenticationProvider, IUserDataAutoSyncService, SyncResource, IResourcePreview, ISyncResourcePreview, Change, IManualSyncTask } from 'vs/platform/userDataSync/common/userDataSync';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
-import { IUserDataSyncWorkbenchService, IUserDataSyncAccount, AccountStatus, CONTEXT_SYNC_ENABLEMENT, CONTEXT_SYNC_STATE, CONTEXT_ACCOUNT_STATE, SHOW_SYNC_LOG_COMMAND_ID, getSyncAreaLabel, IUserDataSyncPreview, IUserDataSyncResource, CONTEXT_ENABLE_MANUAL_SYNC_VIEW, MANUAL_SYNC_VIEW_ID, CONTEXT_ENABLE_ACTIVITY_VIEWS, SYNC_VIEW_CONTAINER_ID } from 'vs/workbench/services/userDataSync/common/userDataSync';
+import { IUserDataSyncWorkbenchService, IUserDataSyncAccount, AccountStatus, CONTEXT_SYNC_ENABLEMENT, CONTEXT_SYNC_STATE, CONTEXT_ACCOUNT_STATE, SHOW_SYNC_LOG_COMMAND_ID, getSyncAreaLabel, IUserDataSyncPreview, IUserDataSyncResource, CONTEXT_ENABLE_SYNC_MERGES_VIEW, SYNC_MERGES_VIEW_ID, CONTEXT_ENABLE_ACTIVITY_VIEWS, SYNC_VIEW_CONTAINER_ID } from 'vs/workbench/services/userDataSync/common/userDataSync';
import { AuthenticationSession, AuthenticationSessionsChangeEvent } from 'vs/editor/common/modes';
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
import { Emitter, Event } from 'vs/base/common/event';
@@ -79,7 +79,7 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat
private readonly syncEnablementContext: IContextKey;
private readonly syncStatusContext: IContextKey;
private readonly accountStatusContext: IContextKey;
- private readonly manualSyncViewEnablementContext: IContextKey;
+ private readonly mergesViewEnablementContext: IContextKey;
private readonly activityViewsEnablementContext: IContextKey;
readonly userDataSyncPreview: UserDataSyncPreview = this._register(new UserDataSyncPreview(this.userDataSyncService));
@@ -110,7 +110,7 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat
this.syncStatusContext = CONTEXT_SYNC_STATE.bindTo(contextKeyService);
this.accountStatusContext = CONTEXT_ACCOUNT_STATE.bindTo(contextKeyService);
this.activityViewsEnablementContext = CONTEXT_ENABLE_ACTIVITY_VIEWS.bindTo(contextKeyService);
- this.manualSyncViewEnablementContext = CONTEXT_ENABLE_MANUAL_SYNC_VIEW.bindTo(contextKeyService);
+ this.mergesViewEnablementContext = CONTEXT_ENABLE_SYNC_MERGES_VIEW.bindTo(contextKeyService);
if (this.authenticationProviders.length) {
@@ -302,16 +302,16 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat
const result = await this.dialogService.show(
Severity.Info,
- localize('preferences sync', "Preferences Sync"),
+ localize('merge or replace', "Merge or Replace"),
[
localize('merge', "Merge"),
localize('replace local', "Replace Local"),
- localize('sync manually', "Sync Manually..."),
+ localize('merge Manually', "Merge Manually..."),
localize('cancel', "Cancel"),
],
{
cancelId: 3,
- detail: localize('first time sync detail', "It looks like you last synced from another machine.\nWould you like to replace or merge with your data in the cloud or sync manually?"),
+ detail: localize('first time sync detail', "It looks like you last synced from another machine.\nWould you like to merge or replace with your data in the cloud?"),
}
);
switch (result.choice) {
@@ -333,18 +333,18 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat
const visibleViewContainer = this.viewsService.getVisibleViewContainer(ViewContainerLocation.Sidebar);
this.userDataSyncPreview.setManualSyncPreview(task, preview);
- this.manualSyncViewEnablementContext.set(true);
+ this.mergesViewEnablementContext.set(true);
await this.waitForActiveSyncViews();
- await this.viewsService.openView(MANUAL_SYNC_VIEW_ID);
+ await this.viewsService.openView(SYNC_MERGES_VIEW_ID);
const error = await Event.toPromise(this.userDataSyncPreview.onDidCompleteManualSync);
this.userDataSyncPreview.unsetManualSyncPreview();
- this.manualSyncViewEnablementContext.set(false);
+ this.mergesViewEnablementContext.set(false);
if (visibleViewContainer) {
this.viewsService.openViewContainer(visibleViewContainer.id);
} else {
- const viewContainer = this.viewDescriptorService.getViewContainerByViewId(MANUAL_SYNC_VIEW_ID);
+ const viewContainer = this.viewDescriptorService.getViewContainerByViewId(SYNC_MERGES_VIEW_ID);
this.viewsService.closeViewContainer(viewContainer!.id);
}
diff --git a/src/vs/workbench/services/userDataSync/common/userDataSync.ts b/src/vs/workbench/services/userDataSync/common/userDataSync.ts
index 811314b4c8..5e9cb42b73 100644
--- a/src/vs/workbench/services/userDataSync/common/userDataSync.ts
+++ b/src/vs/workbench/services/userDataSync/common/userDataSync.ts
@@ -82,7 +82,7 @@ export const CONTEXT_SYNC_STATE = new RawContextKey('syncStatus', SyncSt
export const CONTEXT_SYNC_ENABLEMENT = new RawContextKey('syncEnabled', false);
export const CONTEXT_ACCOUNT_STATE = new RawContextKey('userDataSyncAccountStatus', AccountStatus.Uninitialized);
export const CONTEXT_ENABLE_ACTIVITY_VIEWS = new RawContextKey(`enableSyncActivityViews`, false);
-export const CONTEXT_ENABLE_MANUAL_SYNC_VIEW = new RawContextKey(`enableManualSyncView`, false);
+export const CONTEXT_ENABLE_SYNC_MERGES_VIEW = new RawContextKey(`enableSyncMergesView`, false);
// Commands
export const CONFIGURE_SYNC_COMMAND_ID = 'workbench.userDataSync.actions.configure';
@@ -90,4 +90,4 @@ export const SHOW_SYNC_LOG_COMMAND_ID = 'workbench.userDataSync.actions.showLog'
// VIEWS
export const SYNC_VIEW_CONTAINER_ID = 'workbench.view.sync';
-export const MANUAL_SYNC_VIEW_ID = 'workbench.views.manualSyncView';
+export const SYNC_MERGES_VIEW_ID = 'workbench.views.sync.merges';
diff --git a/src/vs/workbench/test/browser/api/extHostDiagnostics.test.ts b/src/vs/workbench/test/browser/api/extHostDiagnostics.test.ts
index 5a0b4445ce..3c8d905d59 100644
--- a/src/vs/workbench/test/browser/api/extHostDiagnostics.test.ts
+++ b/src/vs/workbench/test/browser/api/extHostDiagnostics.test.ts
@@ -389,6 +389,9 @@ suite('ExtHostDiagnostics', () => {
assertRegistered(): void {
}
+ drain() {
+ return undefined!;
+ }
}, new NullLogService());
let collection1 = diags.createDiagnosticCollection(nullExtensionDescription.identifier, 'foo');
@@ -438,6 +441,9 @@ suite('ExtHostDiagnostics', () => {
assertRegistered(): void {
}
+ drain() {
+ return undefined!;
+ }
}, new NullLogService());
diff --git a/src/vs/workbench/test/browser/api/extHostFileSystemEventService.test.ts b/src/vs/workbench/test/browser/api/extHostFileSystemEventService.test.ts
index 094657fa5a..70adb5a277 100644
--- a/src/vs/workbench/test/browser/api/extHostFileSystemEventService.test.ts
+++ b/src/vs/workbench/test/browser/api/extHostFileSystemEventService.test.ts
@@ -15,7 +15,8 @@ suite('ExtHostFileSystemEventService', () => {
const protocol: IMainContext = {
getProxy: () => { return undefined!; },
set: undefined!,
- assertRegistered: undefined!
+ assertRegistered: undefined!,
+ drain: undefined!
};
const watcher1 = new ExtHostFileSystemEventService(protocol, new NullLogService(), undefined!).createFileSystemWatcher('**/somethingInteresting', false, false, false);
diff --git a/src/vs/workbench/test/browser/api/extHostWorkspace.test.ts b/src/vs/workbench/test/browser/api/extHostWorkspace.test.ts
index c6481233f8..ea4606745f 100644
--- a/src/vs/workbench/test/browser/api/extHostWorkspace.test.ts
+++ b/src/vs/workbench/test/browser/api/extHostWorkspace.test.ts
@@ -298,7 +298,8 @@ suite('ExtHostWorkspace', function () {
const protocol: IMainContext = {
getProxy: () => { return undefined!; },
set: () => { return undefined!; },
- assertRegistered: () => { }
+ assertRegistered: () => { },
+ drain: () => { return undefined!; },
};
const ws = createExtHostWorkspace(protocol, { id: 'foo', name: 'Test', folders: [] }, new NullLogService());
diff --git a/src/vs/workbench/test/browser/api/mainThreadDiagnostics.test.ts b/src/vs/workbench/test/browser/api/mainThreadDiagnostics.test.ts
index 9e934f033f..990c98bdc7 100644
--- a/src/vs/workbench/test/browser/api/mainThreadDiagnostics.test.ts
+++ b/src/vs/workbench/test/browser/api/mainThreadDiagnostics.test.ts
@@ -32,6 +32,7 @@ suite('MainThreadDiagnostics', function () {
$acceptMarkersChange() { }
};
}
+ drain(): any { return null; }
},
markerService,
new class extends mock() {
diff --git a/src/vs/workbench/test/browser/api/mainThreadTreeViews.test.ts b/src/vs/workbench/test/browser/api/mainThreadTreeViews.test.ts
index 2ac1c9bbe0..db70d8a2a0 100644
--- a/src/vs/workbench/test/browser/api/mainThreadTreeViews.test.ts
+++ b/src/vs/workbench/test/browser/api/mainThreadTreeViews.test.ts
@@ -66,6 +66,7 @@ suite('MainThreadHostTreeView', function () {
getProxy(): any {
return extHostTreeViewsShape;
}
+ drain(): any { return null; }
}, new TestViewsService(), new TestNotificationService(), testExtensionService, new NullLogService());
mainThreadTreeViews.$registerTreeViewDataProvider(testTreeViewId, { showCollapseAll: false, canSelectMany: false });
await testExtensionService.whenInstalledExtensionsRegistered();
diff --git a/src/vs/workbench/test/browser/api/testRPCProtocol.ts b/src/vs/workbench/test/browser/api/testRPCProtocol.ts
index 4a5829d377..b41002a4bb 100644
--- a/src/vs/workbench/test/browser/api/testRPCProtocol.ts
+++ b/src/vs/workbench/test/browser/api/testRPCProtocol.ts
@@ -19,7 +19,8 @@ export function SingleProxyRPCProtocol(thing: any): IExtHostContext & IExtHostRp
set(identifier: ProxyIdentifier, value: R): R {
return value;
},
- assertRegistered: undefined!
+ assertRegistered: undefined!,
+ drain: undefined!
};
}
@@ -40,6 +41,10 @@ export class TestRPCProtocol implements IExtHostContext, IExtHostRpcService {
this._proxies = Object.create(null);
}
+ drain(): Promise {
+ return Promise.resolve();
+ }
+
private get _callCount(): number {
return this._callCountValue;
}