Merge from vscode 8c426f9f3b6b18935cc6c2ec8aa6d45ccd88021e

This commit is contained in:
ADS Merger
2020-07-23 02:21:09 +00:00
parent efc8182954
commit ede827ee82
83 changed files with 1736 additions and 829 deletions
+1 -1
View File
@@ -49,7 +49,7 @@
"rollup-plugin-commonjs": "^10.1.0", "rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-node-resolve": "^5.2.0", "rollup-plugin-node-resolve": "^5.2.0",
"terser": "4.3.8", "terser": "4.3.8",
"typescript": "^4.0.0-dev.20200715", "typescript": "^4.0.0-dev.20200722",
"vsce": "1.48.0", "vsce": "1.48.0",
"vscode-telemetry-extractor": "^1.6.0", "vscode-telemetry-extractor": "^1.6.0",
"xml2js": "^0.4.17" "xml2js": "^0.4.17"
+4 -4
View File
@@ -3539,10 +3539,10 @@ typescript@^3.0.1:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977"
integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g== integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==
typescript@^4.0.0-dev.20200715: typescript@^4.0.0-dev.20200722:
version "4.0.0-dev.20200715" version "4.0.0-dev.20200722"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.0-dev.20200715.tgz#d65961a5a6f13fde95a6f4db5f5946f15e4c59bc" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.0-dev.20200722.tgz#b59dd5a3cd84a98d5aae0e4f3a3c58f0c81a3b9b"
integrity sha512-gmPXoWktfXeutmWTM6el9U4vIn5kqOHGI1OESSOhPtLWrxodKqLfFuMygQtOUTtGjKLFQRFAJhHEwUhHZNOURA== integrity sha512-MmJ1YyPNK3JYeKLiTg5sQXdeZaMgt99Fg4BMRZhJmhoq1/x2V1cpXMYvE1rtIYl9K7NvmTDdU3WDW7ZOD6ybaw==
typical@^4.0.0: typical@^4.0.0:
version "4.0.0" version "4.0.0"
+1 -1
View File
@@ -1801,7 +1801,7 @@
"Ignore", "Ignore",
"ignore" "ignore"
], ],
"filenames": [ "extensions": [
".gitignore_global", ".gitignore_global",
".gitignore" ".gitignore"
], ],
+1 -1
View File
@@ -74,7 +74,7 @@ async function createModel(context: ExtensionContext, outputChannel: OutputChann
new GitTimelineProvider(model) 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; return model;
} }
+1 -1
View File
@@ -3,7 +3,7 @@
"version": "0.0.1", "version": "0.0.1",
"description": "Dependencies shared by all extensions", "description": "Dependencies shared by all extensions",
"dependencies": { "dependencies": {
"typescript": "3.9.6" "typescript": "3.9.7"
}, },
"scripts": { "scripts": {
"postinstall": "node ./postinstall" "postinstall": "node ./postinstall"
+4 -4
View File
@@ -76,10 +76,10 @@ rimraf@^3.0.2:
dependencies: dependencies:
glob "^7.1.3" glob "^7.1.3"
typescript@3.9.6: typescript@3.9.7:
version "3.9.6" version "3.9.7"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.6.tgz#8f3e0198a34c3ae17091b35571d3afd31999365a" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa"
integrity sha512-Pspx3oKAPJtjNwE92YS05HQoY7z2SFyOpHo9MqJor3BXAGNaPUs83CuVp9VISFkSjyRfiTpmKuAYGJB7S7hOxw== integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==
wrappy@1: wrappy@1:
version "1.0.2" version "1.0.2"
+3 -3
View File
@@ -37,9 +37,6 @@ if "%INTEGRATION_TEST_ELECTRON_PATH%"=="" (
:: Tests in the extension host :: 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 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% 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% 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% 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 for /f "delims=" %%i in ('node -p "require('fs').realpathSync.native(require('os').tmpdir())"') do set TEMPDIR=%%i
set GITWORKSPACE=%TEMPDIR%\git-%RANDOM% set GITWORKSPACE=%TEMPDIR%\git-%RANDOM%
mkdir %GITWORKSPACE% mkdir %GITWORKSPACE%
+1 -1
View File
@@ -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/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 $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 $(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 "$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
+1 -1
View File
@@ -34,5 +34,5 @@ export interface IContextMenuDelegate {
actionRunner?: IActionRunner; actionRunner?: IActionRunner;
autoSelectFirstItem?: boolean; autoSelectFirstItem?: boolean;
anchorAlignment?: AnchorAlignment; anchorAlignment?: AnchorAlignment;
anchorAsContainer?: boolean; domForShadowRoot?: HTMLElement;
} }
@@ -5,7 +5,6 @@
.monaco-action-bar { .monaco-action-bar {
text-align: right; text-align: right;
overflow: hidden;
white-space: nowrap; white-space: nowrap;
} }
@@ -28,7 +28,7 @@ function initialize() {
delayer.schedule(); delayer.schedule();
} }
function formatRule(c: Codicon) { export function formatRule(c: Codicon) {
let def = c.definition; let def = c.definition;
while (def instanceof Codicon) { while (def instanceof Codicon) {
def = def.definition; def = def.definition;
@@ -10,6 +10,12 @@ import { IDisposable, toDisposable, Disposable, DisposableStore } from 'vs/base/
import { Range } from 'vs/base/common/range'; import { Range } from 'vs/base/common/range';
import { BrowserFeatures } from 'vs/base/browser/canIUse'; import { BrowserFeatures } from 'vs/base/browser/canIUse';
export const enum ContextViewDOMPosition {
ABSOLUTE = 1,
FIXED,
FIXED_SHADOW
}
export interface IAnchor { export interface IAnchor {
x: number; x: number;
y: number; y: number;
@@ -105,32 +111,62 @@ export class ContextView extends Disposable {
private container: HTMLElement | null = null; private container: HTMLElement | null = null;
private view: HTMLElement; private view: HTMLElement;
private useFixedPosition: boolean; private useFixedPosition: boolean;
private useShadowDOM: boolean;
private delegate: IDelegate | null = null; private delegate: IDelegate | null = null;
private toDisposeOnClean: IDisposable = Disposable.None; private toDisposeOnClean: IDisposable = Disposable.None;
private toDisposeOnSetContainer: 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(); super();
this.view = DOM.$('.context-view'); this.view = DOM.$('.context-view');
this.useFixedPosition = false; this.useFixedPosition = false;
this.useShadowDOM = false;
DOM.hide(this.view); 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) { if (this.container) {
this.toDisposeOnSetContainer.dispose(); this.toDisposeOnSetContainer.dispose();
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.removeChild(this.view);
}
this.container = null; this.container = null;
} }
if (container) { if (container) {
this.container = container; this.container = container;
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 = `
<style>
${SHADOW_ROOT_CSS}
</style>
`;
this.shadowRoot.appendChild(this.view);
this.shadowRoot.appendChild(DOM.$('slot'));
} else {
this.container.appendChild(this.view); this.container.appendChild(this.view);
}
const toDisposeOnSetContainer = new DisposableStore(); const toDisposeOnSetContainer = new DisposableStore();
@@ -148,8 +184,6 @@ export class ContextView extends Disposable {
this.toDisposeOnSetContainer = toDisposeOnSetContainer; this.toDisposeOnSetContainer = toDisposeOnSetContainer;
} }
this.useFixedPosition = useFixedPosition;
} }
show(delegate: IDelegate): void { show(delegate: IDelegate): void {
@@ -162,6 +196,7 @@ export class ContextView extends Disposable {
this.view.className = 'context-view'; this.view.className = 'context-view';
this.view.style.top = '0px'; this.view.style.top = '0px';
this.view.style.left = '0px'; this.view.style.left = '0px';
this.view.style.zIndex = '2500';
this.view.style.position = this.useFixedPosition ? 'fixed' : 'absolute'; this.view.style.position = this.useFixedPosition ? 'fixed' : 'absolute';
DOM.show(this.view); DOM.show(this.view);
@@ -301,3 +336,45 @@ export class ContextView extends Disposable {
super.dispose(); 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; }
`;
+6 -4
View File
@@ -270,7 +270,7 @@ export class DropdownMenu extends BaseDropdown {
onHide: () => this.onHide(), onHide: () => this.onHide(),
actionRunner: this.menuOptions ? this.menuOptions.actionRunner : undefined, actionRunner: this.menuOptions ? this.menuOptions.actionRunner : undefined,
anchorAlignment: this.menuOptions ? this.menuOptions.anchorAlignment : AnchorAlignment.LEFT, 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<boolean>()); private _onDidChangeVisibility = this._register(new Emitter<boolean>());
readonly onDidChangeVisibility = this._onDidChangeVisibility.event; readonly onDidChangeVisibility = this._onDidChangeVisibility.event;
constructor(action: IAction, menuActions: ReadonlyArray<IAction>, 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<IAction>, 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, keybindings: ((action: IAction) => ResolvedKeybinding) | 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<IAction> | IActionProvider, contextMenuProvider: IContextMenuProvider, actionViewItemProvider: IActionViewItemProvider | undefined, actionRunner: IActionRunner, keybindings: ((action: IAction) => ResolvedKeybinding | undefined) | undefined, clazz: string | undefined, anchorAlignmentProvider?: () => AnchorAlignment, menuAsChild?: boolean) { constructor(action: IAction, menuActionsOrProvider: ReadonlyArray<IAction> | 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); super(null, action);
this.menuActionsOrProvider = menuActionsOrProvider; this.menuActionsOrProvider = menuActionsOrProvider;
this.contextMenuProvider = contextMenuProvider; this.contextMenuProvider = contextMenuProvider;
this.actionViewItemProvider = actionViewItemProvider; this.actionViewItemProvider = actionViewItemProvider;
if (actionRunner) {
this.actionRunner = actionRunner; this.actionRunner = actionRunner;
}
this.keybindings = keybindings; this.keybindings = keybindings;
this.clazz = clazz; this.clazz = clazz;
this.anchorAlignmentProvider = anchorAlignmentProvider; this.anchorAlignmentProvider = anchorAlignmentProvider;
-225
View File
@@ -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;
}
+315 -2
View File
@@ -3,13 +3,12 @@
* Licensed under the Source EULA. See License.txt in the project root for license information. * 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 nls from 'vs/nls';
import * as strings from 'vs/base/common/strings'; import * as strings from 'vs/base/common/strings';
import { IActionRunner, IAction, Action } from 'vs/base/common/actions'; 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 { ActionBar, IActionViewItemProvider, ActionsOrientation, Separator, ActionViewItem, IActionViewItemOptions, BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { ResolvedKeybinding, KeyCode } from 'vs/base/common/keyCodes'; 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 { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { RunOnceScheduler } from 'vs/base/common/async'; import { RunOnceScheduler } from 'vs/base/common/async';
import { DisposableStore } from 'vs/base/common/lifecycle'; 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 { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview';
import { isLinux, isMacintosh } from 'vs/base/common/platform'; import { isLinux, isMacintosh } from 'vs/base/common/platform';
import { Codicon, registerIcon, stripCodicons } from 'vs/base/common/codicons'; 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_MNEMONIC_REGEX = /\(&([^\s&])\)|(^|[^&])&([^\s&])/;
export const MENU_ESCAPED_MNEMONIC_REGEX = /(&amp;)?(&amp;)([^\s&])/g; export const MENU_ESCAPED_MNEMONIC_REGEX = /(&amp;)?(&amp;)([^\s&])/g;
@@ -71,6 +71,8 @@ export class Menu extends ActionBar {
private readonly menuDisposables: DisposableStore; private readonly menuDisposables: DisposableStore;
private scrollableElement: DomScrollableElement; private scrollableElement: DomScrollableElement;
private menuElement: HTMLElement; private menuElement: HTMLElement;
static globalStyleSheet: HTMLStyleElement;
protected styleSheet: HTMLStyleElement | undefined;
constructor(container: HTMLElement, actions: ReadonlyArray<IAction>, options: IMenuOptions = {}) { constructor(container: HTMLElement, actions: ReadonlyArray<IAction>, options: IMenuOptions = {}) {
addClass(container, 'monaco-menu-container'); addClass(container, 'monaco-menu-container');
@@ -96,6 +98,8 @@ export class Menu extends ActionBar {
this.menuDisposables = this._register(new DisposableStore()); this.menuDisposables = this._register(new DisposableStore());
this.initializeStyleSheet(container);
addDisposableListener(menuElement, EventType.KEY_DOWN, (e) => { addDisposableListener(menuElement, EventType.KEY_DOWN, (e) => {
const event = new StandardKeyboardEvent(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 { style(style: IMenuStyles): void {
const container = this.getContainer(); const container = this.getContainer();
@@ -877,3 +895,298 @@ export function cleanMnemonic(label: string): string {
return label.replace(regex, mnemonicInText ? '$2$3' : '').trim(); 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;
}
`;
+83
View File
@@ -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;
}
+1
View File
@@ -3,6 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information. * 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 browser from 'vs/base/browser/browser';
import * as DOM from 'vs/base/browser/dom'; import * as DOM from 'vs/base/browser/dom';
import * as strings from 'vs/base/common/strings'; import * as strings from 'vs/base/common/strings';
+17 -3
View File
@@ -3,8 +3,9 @@
* Licensed under the Source EULA. See License.txt in the project root for license information. * 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 { regExpFlags } from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
export function stringify(obj: any): string { export function stringify(obj: any): string {
return JSON.stringify(obj, replacer); return JSON.stringify(obj, replacer);
@@ -44,13 +45,26 @@ export function revive(obj: any, depth = 0): any {
case 2: return new RegExp(obj.source, obj.flags); case 2: return new RegExp(obj.source, obj.flags);
} }
// walk object (or array) if (
for (let key in obj) { 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)) { if (Object.hasOwnProperty.call(obj, key)) {
obj[key] = revive(obj[key], depth + 1); obj[key] = revive(obj[key], depth + 1);
} }
} }
} }
}
return obj; return obj;
} }
+14
View File
@@ -16,6 +16,7 @@ export interface ISocket extends IDisposable {
onEnd(listener: () => void): IDisposable; onEnd(listener: () => void): IDisposable;
write(buffer: VSBuffer): void; write(buffer: VSBuffer): void;
end(): void; end(): void;
drain(): Promise<void>;
} }
let emptyBuffer: VSBuffer | null = null; let emptyBuffer: VSBuffer | null = null;
@@ -277,6 +278,11 @@ class ProtocolWriter {
this._isDisposed = true; this._isDisposed = true;
} }
public drain(): Promise<void> {
this.flush();
return this._socket.drain();
}
public flush(): void { public flush(): void {
// flush // flush
this._writeNow(); this._writeNow();
@@ -372,6 +378,10 @@ export class Protocol extends Disposable implements IMessagePassingProtocol {
this._register(this._socket.onClose(() => this._onClose.fire())); this._register(this._socket.onClose(() => this._onClose.fire()));
} }
drain(): Promise<void> {
return this._socketWriter.drain();
}
getSocket(): ISocket { getSocket(): ISocket {
return this._socket; return this._socket;
} }
@@ -619,6 +629,10 @@ export class PersistentProtocol implements IMessagePassingProtocol {
this._socketDisposables = dispose(this._socketDisposables); this._socketDisposables = dispose(this._socketDisposables);
} }
drain(): Promise<void> {
return this._socketWriter.drain();
}
sendDisconnect(): void { sendDisconnect(): void {
const msg = new ProtocolMessage(ProtocolMessageType.Disconnect, 0, 0, getEmptyBuffer()); const msg = new ProtocolMessage(ProtocolMessageType.Disconnect, 0, 0, getEmptyBuffer());
this._socketWriter.write(msg); this._socketWriter.write(msg);
+16 -4
View File
@@ -70,6 +70,10 @@ interface IHandler {
export interface IMessagePassingProtocol { export interface IMessagePassingProtocol {
send(buffer: VSBuffer): void; send(buffer: VSBuffer): void;
onMessage: Event<VSBuffer>; onMessage: Event<VSBuffer>;
/**
* Wait for the write buffer (if applicable) to become empty.
*/
drain?(): Promise<void>;
} }
enum State { enum State {
@@ -482,10 +486,7 @@ export class ChannelClient implements IChannelClient, IDisposable {
return e(errors.canceled()); return e(errors.canceled());
} }
let uninitializedPromise: CancelablePromise<void> | null = createCancelablePromise(_ => this.whenInitialized()); const doRequest = () => {
uninitializedPromise.then(() => {
uninitializedPromise = null;
const handler: IHandler = response => { const handler: IHandler = response => {
switch (response.type) { switch (response.type) {
case ResponseType.PromiseSuccess: case ResponseType.PromiseSuccess:
@@ -510,7 +511,18 @@ export class ChannelClient implements IChannelClient, IDisposable {
this.handlers.set(id, handler); this.handlers.set(id, handler);
this.sendRequest(request); this.sendRequest(request);
};
let uninitializedPromise: CancelablePromise<void> | null = null;
if (this.state === State.Idle) {
doRequest();
} else {
uninitializedPromise = createCancelablePromise(_ => this.whenInitialized());
uninitializedPromise.then(() => {
uninitializedPromise = null;
doRequest();
}); });
}
const cancel = () => { const cancel = () => {
if (uninitializedPromise) { if (uninitializedPromise) {
+40
View File
@@ -12,6 +12,7 @@ import { generateUuid } from 'vs/base/common/uuid';
import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { VSBuffer } from 'vs/base/common/buffer'; import { VSBuffer } from 'vs/base/common/buffer';
import { ISocket, Protocol, Client, ChunkStream } from 'vs/base/parts/ipc/common/ipc.net'; 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 { export class NodeSocket implements ISocket {
public readonly socket: Socket; 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 // > 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 // > 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. // > accept and buffer chunk even if it has not been allowed to drain.
try {
this.socket.write(<Buffer>buffer.buffer); this.socket.write(<Buffer>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 { public end(): void {
this.socket.end(); this.socket.end();
} }
public drain(): Promise<void> {
return new Promise<void>((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 { const enum Constants {
@@ -229,6 +265,10 @@ export class WebSocketNodeSocket extends Disposable implements ISocket {
} }
} }
} }
public drain(): Promise<void> {
return this.socket.drain();
}
} }
function unmask(buffer: VSBuffer, mask: number): void { function unmask(buffer: VSBuffer, mask: number): void {
@@ -90,6 +90,7 @@ export class CodeActionMenu extends Disposable {
const resolver = this._keybindingResolver.getResolver(); const resolver = this._keybindingResolver.getResolver();
this._contextMenuService.showContextMenu({ this._contextMenuService.showContextMenu({
domForShadowRoot: this._editor.getDomNode()!,
getAnchor: () => anchor, getAnchor: () => anchor,
getActions: () => menuActions, getActions: () => menuActions,
onHide: () => { onHide: () => {
@@ -205,6 +205,8 @@ export class ContextMenuController implements IEditorContribution {
// Show menu // Show menu
this._contextMenuIsBeingShownCount++; this._contextMenuIsBeingShownCount++;
this._contextMenuService.showContextMenu({ this._contextMenuService.showContextMenu({
domForShadowRoot: this._editor.getDomNode(),
getAnchor: () => anchor!, getAnchor: () => anchor!,
getActions: () => actions, getActions: () => actions,
+1
View File
@@ -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.onKeyDown((e: IKeyboardEvent) => this.onEditorKeyDown(e)));
this._register(this._editor.onKeyUp((e: IKeyboardEvent) => this.onEditorKeyUp(e))); this._register(this._editor.onKeyUp((e: IKeyboardEvent) => this.onEditorKeyUp(e)));
this._register(this._editor.onDidBlurEditorWidget(() => this.onEditorBlur())); this._register(this._editor.onDidBlurEditorWidget(() => this.onEditorBlur()));
this._register(this._editor.onDidBlurEditorText(() => this.onEditorBlur()));
this._dndDecorationIds = []; this._dndDecorationIds = [];
this._mouseDown = false; this._mouseDown = false;
this._modifierPressed = false; this._modifierPressed = false;
+1 -1
View File
@@ -6,7 +6,7 @@
/* Find widget */ /* Find widget */
.monaco-editor .find-widget { .monaco-editor .find-widget {
position: absolute; position: absolute;
z-index: 10; z-index: 20;
height: 33px; height: 33px;
overflow: hidden; overflow: hidden;
line-height: 19px; line-height: 19px;
@@ -50,7 +50,7 @@ export class ContextMenuHandler {
let menu: Menu | undefined; let menu: Menu | undefined;
const anchor = delegate.getAnchor(); let shadowRootElement = isHTMLElement(delegate.domForShadowRoot) ? delegate.domForShadowRoot : undefined;
this.contextViewService.showContextView({ this.contextViewService.showContextView({
getAnchor: () => delegate.getAnchor(), getAnchor: () => delegate.getAnchor(),
canRelayout: false, canRelayout: false,
@@ -133,7 +133,7 @@ export class ContextMenuHandler {
this.focusToReturn.focus(); this.focusToReturn.focus();
} }
} }
}, !!delegate.anchorAsContainer && isHTMLElement(anchor) ? anchor : undefined); }, shadowRootElement, !!shadowRootElement);
} }
private onActionRun(e: IRunEvent): void { private onActionRun(e: IRunEvent): void {
@@ -15,7 +15,7 @@ export interface IContextViewService extends IContextViewProvider {
readonly _serviceBrand: undefined; readonly _serviceBrand: undefined;
showContextView(delegate: IContextViewDelegate, container?: HTMLElement): IDisposable; showContextView(delegate: IContextViewDelegate, container?: HTMLElement, shadowRoot?: boolean): IDisposable;
hideContextView(data?: any): void; hideContextView(data?: any): void;
layout(): void; layout(): void;
anchorAlignment?: AnchorAlignment; anchorAlignment?: AnchorAlignment;
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { IContextViewService, IContextViewDelegate } from './contextView'; 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 { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
@@ -21,7 +21,7 @@ export class ContextViewService extends Disposable implements IContextViewServic
super(); super();
this.container = layoutService.container; 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.layout();
this._register(layoutService.onLayout(() => this.layout())); this._register(layoutService.onLayout(() => this.layout()));
@@ -29,20 +29,20 @@ export class ContextViewService extends Disposable implements IContextViewServic
// ContextView // ContextView
setContainer(container: HTMLElement, useFixedPosition?: boolean): void { setContainer(container: HTMLElement, domPosition?: ContextViewDOMPosition): void {
this.contextView.setContainer(container, !!useFixedPosition); 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) {
if (container !== this.container) { if (container !== this.container) {
this.container = container; this.container = container;
this.setContainer(container, true); this.setContainer(container, shadowRoot ? ContextViewDOMPosition.FIXED_SHADOW : ContextViewDOMPosition.FIXED);
} }
} else { } else {
if (this.container !== this.layoutService.container) { if (this.container !== this.layoutService.container) {
this.container = this.layoutService.container; this.container = this.layoutService.container;
this.setContainer(this.container, false); this.setContainer(this.container, ContextViewDOMPosition.ABSOLUTE);
} }
} }
@@ -149,8 +149,25 @@ export interface IExtensionIdentifier {
uuid?: string; uuid?: string;
} }
export const EXTENSION_CATEGORIES = ['Programming Languages', 'Snippets', 'Linters', 'Themes', 'Debuggers', 'Other', 'Keymaps', 'Formatters', 'Extension Packs', export const EXTENSION_CATEGORIES = [
'SCM Providers', 'Azure', 'Language Packs', 'Data Science', 'Machine Learning', 'Visualization', 'Testing', 'Notebooks']; '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 { export interface IExtensionManifest {
readonly name: string; readonly name: string;
@@ -194,6 +194,9 @@ class BrowserSocket implements ISocket {
this.socket.close(); this.socket.close();
} }
public drain(): Promise<void> {
return Promise.resolve();
}
} }
@@ -82,10 +82,19 @@ class RemovedResources {
let messages: string[] = []; let messages: string[] = [];
if (externalRemoval.length > 0) { 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) { 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'); 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 { private _checkWorkspaceUndo(strResource: string, element: WorkspaceStackElement, editStackSnapshot: EditStackSnapshot, checkInvalidatedResources: boolean): WorkspaceVerificationError | null {
if (element.removedResources) { 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) { 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! // 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) { 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[] = []; const cannotLockDueToResources: string[] = [];
@@ -795,12 +828,28 @@ export class UndoRedoService implements IUndoRedoService {
} }
} }
if (cannotLockDueToResources.length > 0) { 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... // check if new stack elements were added in the meantime...
if (!editStackSnapshot.isValid()) { 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; return null;
@@ -881,7 +930,10 @@ export class UndoRedoService implements IUndoRedoService {
return; return;
} }
if (editStack.locked) { 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); this._notificationService.info(message);
return; return;
} }
@@ -942,10 +994,26 @@ export class UndoRedoService implements IUndoRedoService {
private _checkWorkspaceRedo(strResource: string, element: WorkspaceStackElement, editStackSnapshot: EditStackSnapshot, checkInvalidatedResources: boolean): WorkspaceVerificationError | null { private _checkWorkspaceRedo(strResource: string, element: WorkspaceStackElement, editStackSnapshot: EditStackSnapshot, checkInvalidatedResources: boolean): WorkspaceVerificationError | null {
if (element.removedResources) { 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) { 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! // 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) { 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[] = []; const cannotLockDueToResources: string[] = [];
@@ -966,12 +1042,28 @@ export class UndoRedoService implements IUndoRedoService {
} }
} }
if (cannotLockDueToResources.length > 0) { 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... // check if new stack elements were added in the meantime...
if (!editStackSnapshot.isValid()) { 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; return null;
@@ -1015,7 +1107,10 @@ export class UndoRedoService implements IUndoRedoService {
return; return;
} }
if (editStack.locked) { 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); this._notificationService.info(message);
return; return;
} }
@@ -32,6 +32,7 @@ type AutoSyncErrorClassification = {
const enablementKey = 'sync.enable'; const enablementKey = 'sync.enable';
const disableMachineEventuallyKey = 'sync.disableMachineEventually'; const disableMachineEventuallyKey = 'sync.disableMachineEventually';
const sessionIdKey = 'sync.sessionId'; const sessionIdKey = 'sync.sessionId';
const storeUrlKey = 'sync.storeUrl';
export class UserDataAutoSyncEnablementService extends Disposable { export class UserDataAutoSyncEnablementService extends Disposable {
@@ -97,15 +98,20 @@ export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService i
this.syncTriggerDelayer = this._register(new Delayer<void>(0)); this.syncTriggerDelayer = this._register(new Delayer<void>(0));
if (userDataSyncStoreService.userDataSyncStore) { if (userDataSyncStoreService.userDataSyncStore) {
storageService.store(storeUrlKey, userDataSyncStoreService.userDataSyncStore.url.toString(), StorageScope.GLOBAL);
if (this.isEnabled()) { if (this.isEnabled()) {
this.logService.info('Auto Sync is enabled.'); this.logService.info('Auto Sync is enabled.');
} else { } else {
this.logService.info('Auto Sync is disabled.'); this.logService.info('Auto Sync is disabled.');
} }
this.updateAutoSync(); this.updateAutoSync();
if (this.hasToDisableMachineEventually()) { if (this.hasToDisableMachineEventually()) {
this.disableMachineEventually(); this.disableMachineEventually();
} }
this._register(userDataSyncAccountService.onDidChangeAccount(() => this.updateAutoSync())); this._register(userDataSyncAccountService.onDidChangeAccount(() => this.updateAutoSync()));
this._register(userDataSyncStoreService.onDidChangeDonotMakeRequestsUntil(() => this.updateAutoSync())); this._register(userDataSyncStoreService.onDidChangeDonotMakeRequestsUntil(() => this.updateAutoSync()));
this._register(Event.debounce<string, string[]>(userDataSyncService.onDidChangeLocal, (last, source) => last ? [...last, source] : [source], 1000)(sources => this.triggerSync(sources, false))); this._register(Event.debounce<string, string[]>(userDataSyncService.onDidChangeLocal, (last, source) => last ? [...last, source] : [source], 1000)(sources => this.triggerSync(sources, false)));
-16
View File
@@ -868,22 +868,6 @@ declare module 'vscode' {
debugAdapterExecutable?(folder: WorkspaceFolder | undefined, token?: CancellationToken): ProviderResult<DebugAdapterExecutable>; debugAdapterExecutable?(folder: WorkspaceFolder | undefined, token?: CancellationToken): ProviderResult<DebugAdapterExecutable>;
} }
export interface DebugSession {
/**
* Terminates the session.
*/
terminate(): Thenable<void>;
}
export interface DebugSession {
/**
* Terminates the session.
*/
terminate(): Thenable<void>;
}
export namespace debug { export namespace debug {
/** /**
@@ -262,14 +262,6 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
return Promise.reject(new Error('debug session not found')); return Promise.reject(new Error('debug session not found'));
} }
public $terminateDebugSession(sessionId: DebugSessionUUID): Promise<void> {
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<void> { public $stopDebugging(sessionId: DebugSessionUUID | undefined): Promise<void> {
if (sessionId) { if (sessionId) {
const session = this.debugService.getModel().getSession(sessionId, true); const session = this.debugService.getModel().getSession(sessionId, true);
@@ -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) => { const updateProperties = (selectionChangeSource: string | null) => {
// Some editor events get delivered faster than model content changes. This is // Some editor events get delivered faster than model content changes. This is
// problematic, as this leads to editor properties reaching the extension host // 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) => { this._codeEditorListeners.add(this._codeEditor.onDidChangeCursorSelection((e) => {
// selection // selection
if (!isValidCodeEditor()) {
return;
}
updateProperties(e.source); updateProperties(e.source);
})); }));
this._codeEditorListeners.add(this._codeEditor.onDidChangeConfiguration(() => { this._codeEditorListeners.add(this._codeEditor.onDidChangeConfiguration((e) => {
// options // options
if (!isValidCodeEditor()) {
return;
}
updateProperties(null); updateProperties(null);
})); }));
this._codeEditorListeners.add(this._codeEditor.onDidLayoutChange(() => { this._codeEditorListeners.add(this._codeEditor.onDidLayoutChange(() => {
// visibleRanges // visibleRanges
if (!isValidCodeEditor()) {
return;
}
updateProperties(null); updateProperties(null);
})); }));
this._codeEditorListeners.add(this._codeEditor.onDidScrollChange(() => { this._codeEditorListeners.add(this._codeEditor.onDidScrollChange(() => {
// visibleRanges // visibleRanges
if (!isValidCodeEditor()) {
return;
}
updateProperties(null); updateProperties(null);
})); }));
this._updatePropertiesNow(null); this._updatePropertiesNow(null);
@@ -128,7 +128,7 @@ export class MainThreadExtensionService implements MainThreadExtensionServiceSha
} }
} }
$onExtensionHostExit(code: number): void { async $onExtensionHostExit(code: number): Promise<void> {
this._extensionService._onExtensionHostExit(code); this._extensionService._onExtensionHostExit(code);
} }
} }
@@ -799,7 +799,7 @@ export interface MainThreadExtensionServiceShape extends IDisposable {
$onDidActivateExtension(extensionId: ExtensionIdentifier, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationReason: ExtensionActivationReason): void; $onDidActivateExtension(extensionId: ExtensionIdentifier, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationReason: ExtensionActivationReason): void;
$onExtensionActivationError(extensionId: ExtensionIdentifier, error: ExtensionActivationError): Promise<void>; $onExtensionActivationError(extensionId: ExtensionIdentifier, error: ExtensionActivationError): Promise<void>;
$onExtensionRuntimeError(extensionId: ExtensionIdentifier, error: SerializedError): void; $onExtensionRuntimeError(extensionId: ExtensionIdentifier, error: SerializedError): void;
$onExtensionHostExit(code: number): void; $onExtensionHostExit(code: number): Promise<void>;
} }
export interface SCMProviderFeatures { export interface SCMProviderFeatures {
@@ -882,7 +882,6 @@ export interface MainThreadDebugServiceShape extends IDisposable {
$stopDebugging(sessionId: DebugSessionUUID | undefined): Promise<void>; $stopDebugging(sessionId: DebugSessionUUID | undefined): Promise<void>;
$setDebugSessionName(id: DebugSessionUUID, name: string): void; $setDebugSessionName(id: DebugSessionUUID, name: string): void;
$customDebugAdapterRequest(id: DebugSessionUUID, command: string, args: any): Promise<any>; $customDebugAdapterRequest(id: DebugSessionUUID, command: string, args: any): Promise<any>;
$terminateDebugSession(id: DebugSessionUUID): Promise<void>;
$appendDebugConsole(value: string): void; $appendDebugConsole(value: string): void;
$startBreakpointEvents(): void; $startBreakpointEvents(): void;
$registerBreakpoints(breakpoints: Array<ISourceMultiBreakpointDto | IFunctionBreakpointDto | IDataBreakpointDto>): Promise<void>; $registerBreakpoints(breakpoints: Array<ISourceMultiBreakpointDto | IFunctionBreakpointDto | IDataBreakpointDto>): Promise<void>;
@@ -957,10 +957,6 @@ export class ExtHostDebugSession implements vscode.DebugSession {
public customRequest(command: string, args: any): Promise<any> { public customRequest(command: string, args: any): Promise<any> {
return this._debugServiceProxy.$customDebugAdapterRequest(this._id, command, args); return this._debugServiceProxy.$customDebugAdapterRequest(this._id, command, args);
} }
public terminate(): Promise<void> {
return this._debugServiceProxy.$terminateDebugSession(this._id);
}
} }
export class ExtHostDebugConsole implements vscode.DebugConsole { export class ExtHostDebugConsole implements vscode.DebugConsole {
@@ -557,7 +557,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
} }
// after tests have run, we shutdown the host // 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); const runResult = testRunner!.run(extensionTestsPath, oldTestRunnerCallback);
@@ -567,11 +567,11 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
runResult runResult
.then(() => { .then(() => {
c(); c();
this._gracefulExit(0); this._testRunnerExit(0);
}) })
.catch((err: Error) => { .catch((err: Error) => {
e(err.toString()); 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 // Otherwise make sure to shutdown anyway even in case of an error
else { 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))); 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 { private _testRunnerExit(code: number): void {
// to give the PH process a chance to flush any outstanding console // wait at most 5000ms for the renderer to confirm our exit request and for the renderer socket to drain
// messages to the main process, we delay the exit() by some time // (this is to ensure all outstanding messages reach the renderer)
setTimeout(() => { const exitPromise = this._mainThreadExtensionsProxy.$onExtensionHostExit(code);
// If extension tests are running, give the exit code to the renderer const drainPromise = this._extHostContext.drain();
if (this._initData.remote.isRemote && !!this._initData.environment.extensionTestsLocationURI) { Promise.race([Promise.all([exitPromise, drainPromise]), timeout(5000)]).then(() => {
this._mainThreadExtensionsProxy.$onExtensionHostExit(code);
return;
}
this._hostUtils.exit(code); this._hostUtils.exit(code);
}, 500); });
} }
private _startExtensionHost(): Promise<void> { private _startExtensionHost(): Promise<void> {
@@ -18,12 +18,12 @@ export class ExtHostRpcService implements IExtHostRpcService {
readonly getProxy: <T>(identifier: ProxyIdentifier<T>) => T; readonly getProxy: <T>(identifier: ProxyIdentifier<T>) => T;
readonly set: <T, R extends T> (identifier: ProxyIdentifier<T>, instance: R) => R; readonly set: <T, R extends T> (identifier: ProxyIdentifier<T>, instance: R) => R;
readonly assertRegistered: (identifiers: ProxyIdentifier<any>[]) => void; readonly assertRegistered: (identifiers: ProxyIdentifier<any>[]) => void;
readonly drain: () => Promise<void>;
constructor(rpcProtocol: IRPCProtocol) { constructor(rpcProtocol: IRPCProtocol) {
this.getProxy = rpcProtocol.getProxy.bind(rpcProtocol); this.getProxy = rpcProtocol.getProxy.bind(rpcProtocol);
this.set = rpcProtocol.set.bind(rpcProtocol); this.set = rpcProtocol.set.bind(rpcProtocol);
this.assertRegistered = rpcProtocol.assertRegistered.bind(rpcProtocol); this.assertRegistered = rpcProtocol.assertRegistered.bind(rpcProtocol);
this.drain = rpcProtocol.drain.bind(rpcProtocol);
} }
} }
@@ -51,7 +51,10 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService {
// fetch JS sources as text and create a new function around it // fetch JS sources as text and create a new function around it
const source = await response.text(); 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` // define commonjs globals: `module`, `exports`, and `require`
const _exports = {}; const _exports = {};
@@ -33,6 +33,7 @@ import { ContextSubMenu } from 'vs/base/browser/contextmenu';
import { IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService'; import { IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService';
import { AuthenticationSession } from 'vs/editor/common/modes'; import { AuthenticationSession } from 'vs/editor/common/modes';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
export class ViewContainerActivityAction extends ActivityAction { 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 { export class AccountsActionViewItem extends ActivityActionViewItem {
constructor( constructor(
action: ActivityAction, action: ActivityAction,
@@ -107,7 +110,8 @@ export class AccountsActionViewItem extends ActivityActionViewItem {
@IMenuService protected menuService: IMenuService, @IMenuService protected menuService: IMenuService,
@IContextKeyService private readonly contextKeyService: IContextKeyService, @IContextKeyService private readonly contextKeyService: IContextKeyService,
@IAuthenticationService private readonly authenticationService: IAuthenticationService, @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); 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; return menus;
} }
@@ -5,11 +5,10 @@
import 'vs/css!./media/activitybarpart'; import 'vs/css!./media/activitybarpart';
import * as nls from 'vs/nls'; 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 { GLOBAL_ACTIVITY_ID, IActivity, ACCOUNTS_ACTIIVTY_ID } from 'vs/workbench/common/activity';
import { Part } from 'vs/workbench/browser/part'; import { Part } from 'vs/workbench/browser/part';
import { GlobalActivityActionViewItem, ViewContainerActivityAction, PlaceHolderToggleCompositePinnedAction, PlaceHolderViewContainerActivityAction, HomeAction, HomeActionViewItem } from 'vs/workbench/browser/parts/activitybar/activitybarActions'; import { GlobalActivityActionViewItem, ViewContainerActivityAction, PlaceHolderToggleCompositePinnedAction, PlaceHolderViewContainerActivityAction, HomeAction, HomeActionViewItem, ACCOUNTS_VISIBILITY_PREFERENCE_KEY } 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 { IBadge, NumberBadge } from 'vs/workbench/services/activity/common/activity'; import { IBadge, NumberBadge } from 'vs/workbench/services/activity/common/activity';
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; 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 { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes'; 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 { interface IPlaceholderViewContainer {
id: string; id: string;
name?: string; name?: string;
@@ -76,7 +77,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
static readonly PINNED_VIEW_CONTAINERS = 'workbench.activity.pinnedViewlets2'; static readonly PINNED_VIEW_CONTAINERS = 'workbench.activity.pinnedViewlets2';
private static readonly PLACEHOLDER_VIEW_CONTAINERS = 'workbench.activity.placeholderViewlets'; private static readonly PLACEHOLDER_VIEW_CONTAINERS = 'workbench.activity.placeholderViewlets';
private static readonly HOME_BAR_VISIBILITY_PREFERENCE = 'workbench.activity.showHomeIndicator'; private static readonly HOME_BAR_VISIBILITY_PREFERENCE = 'workbench.activity.showHomeIndicator';
private static readonly ACCOUNTS_ACTION_INDEX = 0;
//#region IView //#region IView
readonly minimumWidth: number = 48; 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"))); 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( actions.push(new Action(
ToggleActivityBarVisibilityAction.ID, ToggleActivityBarVisibilityAction.ID,
nls.localize('hideActivitBar', "Hide Activity Bar"), nls.localize('hideActivitBar', "Hide Activity Bar"),
@@ -588,17 +601,35 @@ export class ActivitybarPart extends Part implements IActivityBarService {
cssClass: Codicon.settingsGear.classNames cssClass: Codicon.settingsGear.classNames
}); });
if (this.accountsVisibilityPreference) {
this.accountsActivityAction = new ActivityAction({ this.accountsActivityAction = new ActivityAction({
id: 'workbench.actions.accounts', id: 'workbench.actions.accounts',
name: nls.localize('accounts', "Accounts"), name: nls.localize('accounts', "Accounts"),
cssClass: Codicon.account.classNames cssClass: Codicon.account.classNames
}); });
this.globalActivityActionBar.push(this.accountsActivityAction); this.globalActivityActionBar.push(this.accountsActivityAction, { index: ActivitybarPart.ACCOUNTS_ACTION_INDEX });
}
this.globalActivityActionBar.push(this.globalActivityAction); 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 } { private getCompositeActions(compositeId: string): { activityAction: ViewContainerActivityAction, pinnedAction: ToggleCompositePinnedAction } {
let compositeActions = this.compositeActions.get(compositeId); let compositeActions = this.compositeActions.get(compositeId);
if (!compositeActions) { 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) { if (e.key === ActivitybarPart.HOME_BAR_VISIBILITY_PREFERENCE && e.scope === StorageScope.GLOBAL) {
this.onDidChangeHomeBarVisibility(); this.onDidChangeHomeBarVisibility();
} }
if (e.key === ACCOUNTS_VISIBILITY_PREFERENCE_KEY && e.scope === StorageScope.GLOBAL) {
this.toggleAccountsActivity();
}
} }
private saveCachedViewContainers(): void { 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); 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 { private migrateFromOldCachedViewContainersValue(): void {
const value = this.storageService.get('workbench.activity.pinnedViewlets', StorageScope.GLOBAL); const value = this.storageService.get('workbench.activity.pinnedViewlets', StorageScope.GLOBAL);
if (value !== undefined) { if (value !== undefined) {
+4 -4
View File
@@ -68,18 +68,18 @@ export abstract class Viewlet extends PaneComposite implements IViewlet {
} }
getSecondaryActions(): IAction[] { getSecondaryActions(): IAction[] {
const viewSecondaryActions = this.viewPaneContainer.getViewsVisibilityActions(); const viewVisibilityActions = this.viewPaneContainer.getViewsVisibilityActions();
const secondaryActions = this.viewPaneContainer.getSecondaryActions(); const secondaryActions = this.viewPaneContainer.getSecondaryActions();
if (viewSecondaryActions.length <= 1) { if (viewVisibilityActions.length <= 1 || viewVisibilityActions.every(({ enabled }) => !enabled)) {
return secondaryActions; return secondaryActions;
} }
if (secondaryActions.length === 0) { if (secondaryActions.length === 0) {
return viewSecondaryActions; return viewVisibilityActions;
} }
return [ return [
new ContextSubMenu(nls.localize('views', "Views"), viewSecondaryActions), new ContextSubMenu(nls.localize('views', "Views"), viewVisibilityActions),
new Separator(), new Separator(),
...secondaryActions ...secondaryActions
]; ];
@@ -689,7 +689,7 @@ export class Breakpoint extends BaseBreakpoint implements IBreakpoint {
toJSON(): any { toJSON(): any {
const result = super.toJSON(); const result = super.toJSON();
result.uri = this.uri; result.uri = this._uri;
result.lineNumber = this._lineNumber; result.lineNumber = this._lineNumber;
result.column = this._column; result.column = this._column;
result.adapterData = this.adapterData; result.adapterData = this.adapterData;
@@ -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<void> {
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 { export class ShowRecommendedExtensionsAction extends Action {
static readonly ID = 'workbench.extensions.action.showRecommendedExtensions'; 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<void> {
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 { export class ChangeSortAction extends Action {
private query: Query; private query: Query;
@@ -11,23 +11,23 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle';
import { Event as EventOf, Emitter } from 'vs/base/common/event'; import { Event as EventOf, Emitter } from 'vs/base/common/event';
import { IAction, Action } from 'vs/base/common/actions'; 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 { IViewlet } from 'vs/workbench/common/viewlet';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { append, $, addClass, toggleClass, Dimension, hide, show } from 'vs/base/browser/dom'; import { append, $, addClass, toggleClass, Dimension, hide, show } from 'vs/base/browser/dom';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; 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 { import {
ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowRecommendedExtensionsAction, /*ShowPopularExtensionsAction,*/ ShowDisabledExtensionsAction, ShowRecommendedExtensionsAction, /*ShowPopularExtensionsAction,*/
ShowOutdatedExtensionsAction, ClearExtensionsInputAction, ChangeSortAction, UpdateAllAction, CheckForUpdatesAction, DisableAllAction, EnableAllAction, ClearExtensionsInputAction, ChangeSortAction, UpdateAllAction, CheckForUpdatesAction, DisableAllAction, EnableAllAction,
EnableAutoUpdateAction, DisableAutoUpdateAction, ShowBuiltInExtensionsAction, InstallVSIXAction EnableAutoUpdateAction, DisableAutoUpdateAction, ShowBuiltInExtensionsAction, InstallVSIXAction, SearchCategoryAction, /*RecentlyPublishedExtensionsAction, */ShowInstalledExtensionsAction, ShowOutdatedExtensionsAction, ShowDisabledExtensionsAction, ShowEnabledExtensionsAction
} from 'vs/workbench/contrib/extensions/browser/extensionsActions'; } from 'vs/workbench/contrib/extensions/browser/extensionsActions';
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IWorkbenchExtensionEnablementService, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IWorkbenchExtensionEnablementService, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput'; 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 { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import Severity from 'vs/base/common/severity'; 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 { alert } from 'vs/base/browser/ui/aria/aria';
import { createErrorWithActions } from 'vs/base/common/errorsWithActions'; import { createErrorWithActions } from 'vs/base/common/errorsWithActions';
import { IEnvironmentService } from 'vs/platform/environment/common/environment'; 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 { Registry } from 'vs/platform/registry/common/platform';
import { RemoteNameContext } from 'vs/workbench/browser/contextkeys';
import { ILabelService } from 'vs/platform/label/common/label'; import { ILabelService } from 'vs/platform/label/common/label';
import { MementoObject } from 'vs/workbench/common/memento'; import { MementoObject } from 'vs/workbench/common/memento';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; 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 { DragAndDropObserver } from 'vs/workbench/browser/dnd';
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import { SIDE_BAR_DRAG_AND_DROP_BACKGROUND } from 'vs/workbench/common/theme'; 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<boolean>('nonEmptyWorkspace', false); const NonEmptyWorkspaceContext = new RawContextKey<boolean>('nonEmptyWorkspace', false);
const DefaultViewsContext = new RawContextKey<boolean>('defaultExtensionViews', true); const DefaultViewsContext = new RawContextKey<boolean>('defaultExtensionViews', true);
@@ -69,25 +70,9 @@ const SearchOutdatedExtensionsContext = new RawContextKey<boolean>('searchOutdat
const SearchEnabledExtensionsContext = new RawContextKey<boolean>('searchEnabledExtensions', false); const SearchEnabledExtensionsContext = new RawContextKey<boolean>('searchEnabledExtensions', false);
const SearchDisabledExtensionsContext = new RawContextKey<boolean>('searchDisabledExtensions', false); const SearchDisabledExtensionsContext = new RawContextKey<boolean>('searchDisabledExtensions', false);
const HasInstalledExtensionsContext = new RawContextKey<boolean>('hasInstalledExtensions', true); const HasInstalledExtensionsContext = new RawContextKey<boolean>('hasInstalledExtensions', true);
const BuiltInExtensionsContext = new RawContextKey<boolean>('builtInExtensions', false);
const SearchBuiltInExtensionsContext = new RawContextKey<boolean>('searchBuiltInExtensions', false); const SearchBuiltInExtensionsContext = new RawContextKey<boolean>('searchBuiltInExtensions', false);
const RecommendedExtensionsContext = new RawContextKey<boolean>('recommendedExtensions', false); const RecommendedExtensionsContext = new RawContextKey<boolean>('recommendedExtensions', false);
const DefaultRecommendedExtensionsContext = new RawContextKey<boolean>('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 { export class ExtensionsViewletViewsContribution implements IWorkbenchContribution {
@@ -103,223 +88,233 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
} }
private registerViews(): void { private registerViews(): void {
let viewDescriptors: IViewDescriptor[] = []; const 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());
if (this.extensionManagementServerService.localExtensionManagementServer) { /* Default views */
viewDescriptors.push(...this.createExtensionsViewDescriptorsForServer(this.extensionManagementServerService.localExtensionManagementServer)); viewDescriptors.push(...this.createDefaultExtensionsViewDescriptors());
}
if (this.extensionManagementServerService.remoteExtensionManagementServer) { /* Search views */
viewDescriptors.push(...this.createExtensionsViewDescriptorsForServer(this.extensionManagementServerService.remoteExtensionManagementServer)); viewDescriptors.push(...this.createSearchExtensionsViewDescriptors());
}
/* Recommendations views */
viewDescriptors.push(...this.createRecommendedExtensionsViewDescriptors());
/* Built-in extensions views */
viewDescriptors.push(...this.createBuiltinExtensionsViewDescriptors());
Registry.as<IViewsRegistry>(Extensions.ViewsRegistry).registerViews(viewDescriptors, this.container); Registry.as<IViewsRegistry>(Extensions.ViewsRegistry).registerViews(viewDescriptors, this.container);
} }
// View used for any kind of searching private createDefaultExtensionsViewDescriptors(): IViewDescriptor[] {
private createMarketPlaceExtensionsListViewDescriptor(): IViewDescriptor { const viewDescriptors: IViewDescriptor[] = [];
const id = 'extensions.listView';
return { /*
id, * Default popular extensions view
name: viewIdNameMappings[id], * 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<void> = 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<void, string>(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), ctorDescriptor: new SyncDescriptor(ExtensionsListView),
when: ContextKeyExpr.and(ContextKeyExpr.has('searchMarketplaceExtensions')), 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. * View used for searching all 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
};
}
*/ */
viewDescriptors.push({
private createExtensionsViewDescriptorsForServer(server: IExtensionManagementServer): IViewDescriptor[] { id: 'workbench.views.extensions.searchInstalled',
const getViewName = (viewTitle: string, server: IExtensionManagementServer): string => { name: localize('installed', "Installed"),
const serverLabel = server.label; ctorDescriptor: new SyncDescriptor(InstalledExtensionsView),
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<void> = EventOf.map(this.labelService.onDidChangeFormatters, () => undefined);
return [{
id: `extensions.${server.id}.installed`,
get name() { return getInstalledViewName(); },
ctorDescriptor: new SyncDescriptor(ServerExtensionsView, [server, EventOf.map<void, string>(onDidChangeServerLabel, () => getInstalledViewName())]),
when: ContextKeyExpr.and(ContextKeyExpr.has('searchInstalledExtensions')), when: ContextKeyExpr.and(ContextKeyExpr.has('searchInstalledExtensions')),
weight: 100 });
}, {
id: `extensions.${server.id}.outdated`,
get name() { return getOutdatedViewName(); },
ctorDescriptor: new SyncDescriptor(ServerExtensionsView, [server, EventOf.map<void, string>(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<void, string>(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 * View used for searching enabled extensions
// When user has no installed extensions, this is shown along with the view for popular extensions */
private createDefaultRecommendedExtensionsListViewDescriptor(): IViewDescriptor { viewDescriptors.push({
const id = 'extensions.recommendedList'; id: 'workbench.views.extensions.searchEnabled',
return { name: localize('enabled', "Enabled"),
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],
ctorDescriptor: new SyncDescriptor(EnabledExtensionsView), ctorDescriptor: new SyncDescriptor(EnabledExtensionsView),
when: ContextKeyExpr.and(ContextKeyExpr.has('searchEnabledExtensions')), when: ContextKeyExpr.and(ContextKeyExpr.has('searchEnabledExtensions')),
weight: 40, });
order: 1
};
}
private createDisabledExtensionsListViewDescriptor(): IViewDescriptor { /*
const id = 'extensions.disabledExtensionList2'; * View used for searching disabled extensions
return { */
id, viewDescriptors.push({
name: viewIdNameMappings[id], id: 'workbench.views.extensions.searchDisabled',
name: localize('disabled', "Disabled"),
ctorDescriptor: new SyncDescriptor(DisabledExtensionsView), ctorDescriptor: new SyncDescriptor(DisabledExtensionsView),
when: ContextKeyExpr.and(ContextKeyExpr.has('searchDisabledExtensions')), 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 { private createRecommendedExtensionsViewDescriptors(): IViewDescriptor[] {
const id = 'extensions.builtInExtensionsList'; const viewDescriptors: IViewDescriptor[] = [];
return {
id, viewDescriptors.push({
name: viewIdNameMappings[id], id: 'workbench.views.extensions.workspaceRecommendations',
ctorDescriptor: new SyncDescriptor(BuiltInExtensionsView), name: localize('workspaceRecommendedExtensions', "Workspace Recommendations"),
when: ContextKeyExpr.has('searchBuiltInExtensions'), ctorDescriptor: new SyncDescriptor(WorkspaceRecommendedExtensionsView),
weight: 100 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 { private createBuiltinExtensionsViewDescriptors(): IViewDescriptor[] {
const id = 'extensions.builtInThemesExtensionsList'; const viewDescriptors: IViewDescriptor[] = [];
return {
id, viewDescriptors.push({
name: viewIdNameMappings[id], 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), ctorDescriptor: new SyncDescriptor(BuiltInThemesExtensionsView),
when: ContextKeyExpr.has('searchBuiltInExtensions'), when: ContextKeyExpr.has('builtInExtensions'),
weight: 100 });
};
}
private createBuiltInBasicsExtensionsListViewDescriptor(): IViewDescriptor { viewDescriptors.push({
const id = 'extensions.builtInBasicsExtensionsList'; id: 'workbench.views.extensions.builtinProgrammingLanguageExtensions',
return { name: localize('builtinProgrammingLanguageExtensions', "Programming Languages"),
id, ctorDescriptor: new SyncDescriptor(BuiltInProgrammingLanguageExtensionsView),
name: viewIdNameMappings[id], when: ContextKeyExpr.has('builtInExtensions'),
ctorDescriptor: new SyncDescriptor(BuiltInBasicsExtensionsView), });
when: ContextKeyExpr.has('searchBuiltInExtensions'),
weight: 100 return viewDescriptors;
};
} }
} }
@@ -336,15 +331,13 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE
private searchEnabledExtensionsContextKey: IContextKey<boolean>; private searchEnabledExtensionsContextKey: IContextKey<boolean>;
private searchDisabledExtensionsContextKey: IContextKey<boolean>; private searchDisabledExtensionsContextKey: IContextKey<boolean>;
private hasInstalledExtensionsContextKey: IContextKey<boolean>; private hasInstalledExtensionsContextKey: IContextKey<boolean>;
private builtInExtensionsContextKey: IContextKey<boolean>;
private searchBuiltInExtensionsContextKey: IContextKey<boolean>; private searchBuiltInExtensionsContextKey: IContextKey<boolean>;
private recommendedExtensionsContextKey: IContextKey<boolean>; private recommendedExtensionsContextKey: IContextKey<boolean>;
private defaultRecommendedExtensionsContextKey: IContextKey<boolean>;
private searchDelayer: Delayer<void>; private searchDelayer: Delayer<void>;
private root: HTMLElement | undefined; private root: HTMLElement | undefined;
private searchBox: SuggestEnabledInput | undefined; private searchBox: SuggestEnabledInput | undefined;
private primaryActions: IAction[] | undefined;
private secondaryActions: IAction[] | null = null;
private readonly searchViewletState: MementoObject; private readonly searchViewletState: MementoObject;
constructor( constructor(
@@ -364,7 +357,7 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE
@IContextMenuService contextMenuService: IContextMenuService, @IContextMenuService contextMenuService: IContextMenuService,
@IExtensionService extensionService: IExtensionService, @IExtensionService extensionService: IExtensionService,
@IViewDescriptorService viewDescriptorService: IViewDescriptorService, @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); 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.searchEnabledExtensionsContextKey = SearchEnabledExtensionsContext.bindTo(contextKeyService);
this.searchDisabledExtensionsContextKey = SearchDisabledExtensionsContext.bindTo(contextKeyService); this.searchDisabledExtensionsContextKey = SearchDisabledExtensionsContext.bindTo(contextKeyService);
this.hasInstalledExtensionsContextKey = HasInstalledExtensionsContext.bindTo(contextKeyService); this.hasInstalledExtensionsContextKey = HasInstalledExtensionsContext.bindTo(contextKeyService);
this.builtInExtensionsContextKey = BuiltInExtensionsContext.bindTo(contextKeyService);
this.searchBuiltInExtensionsContextKey = SearchBuiltInExtensionsContext.bindTo(contextKeyService); this.searchBuiltInExtensionsContextKey = SearchBuiltInExtensionsContext.bindTo(contextKeyService);
this.recommendedExtensionsContextKey = RecommendedExtensionsContext.bindTo(contextKeyService); this.recommendedExtensionsContextKey = RecommendedExtensionsContext.bindTo(contextKeyService);
this.defaultRecommendedExtensionsContextKey = DefaultRecommendedExtensionsContext.bindTo(contextKeyService);
this.defaultRecommendedExtensionsContextKey.set(!this.configurationService.getValue<boolean>(ShowRecommendationsOnlyOnDemandKey));
this._register(this.viewletService.onDidViewletOpen(this.onViewletOpen, this)); this._register(this.viewletService.onDidViewletOpen(this.onViewletOpen, this));
this.searchViewletState = this.getMemento(StorageScope.WORKSPACE); this.searchViewletState = this.getMemento(StorageScope.WORKSPACE);
@@ -390,12 +382,8 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE
this._register(this.configurationService.onDidChangeConfiguration(e => { this._register(this.configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration(AutoUpdateConfigurationKey)) { if (e.affectsConfiguration(AutoUpdateConfigurationKey)) {
this.secondaryActions = null;
this.updateTitleArea(); this.updateTitleArea();
} }
if (e.affectedKeys.indexOf(ShowRecommendationsOnlyOnDemandKey) > -1) {
this.defaultRecommendedExtensionsContextKey.set(!this.configurationService.getValue<boolean>(ShowRecommendationsOnlyOnDemandKey));
}
}, this)); }, this));
} }
@@ -509,40 +497,57 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE
} }
getActions(): IAction[] { getActions(): IAction[] {
if (!this.primaryActions) { return [
this.primaryActions = [ 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() : '') this.instantiationService.createInstance(ClearExtensionsInputAction, ClearExtensionsInputAction.ID, ClearExtensionsInputAction.LABEL, this.onSearchChange, this.searchBox ? this.searchBox.getValue() : ''),
]; ];
} }
return this.primaryActions;
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 super.getActionViewItem(action);
} }
getSecondaryActions(): IAction[] { getSecondaryActions(): IAction[] {
if (!this.secondaryActions) { const actions: IAction[] = [];
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)
];
}
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 { 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.searchOutdatedExtensionsContextKey.set(ExtensionsListView.isOutdatedExtensionsQuery(value));
this.searchEnabledExtensionsContextKey.set(ExtensionsListView.isEnabledExtensionsQuery(value)); this.searchEnabledExtensionsContextKey.set(ExtensionsListView.isEnabledExtensionsQuery(value));
this.searchDisabledExtensionsContextKey.set(ExtensionsListView.isDisabledExtensionsQuery(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.recommendedExtensionsContextKey.set(isRecommendedExtensionsQuery);
this.searchMarketplaceExtensionsContextKey.set(!!value && !ExtensionsListView.isLocalExtensionsQuery(value) && !isRecommendedExtensionsQuery); this.searchMarketplaceExtensionsContextKey.set(!!value && !ExtensionsListView.isLocalExtensionsQuery(value) && !isRecommendedExtensionsQuery);
this.nonEmptyWorkspaceContextKey.set(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY); 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 { private alertSearchResult(count: number, viewId: string): void {
const view = this.viewContainerModel.visibleViewDescriptors.find(view => view.id === viewId);
switch (count) { switch (count) {
case 0: case 0:
break; break;
case 1: case 1:
if (viewIdNameMappings[viewId]) { if (view) {
alert(localize('extensionFoundInSection', "1 extension found in the {0} section.", viewIdNameMappings[viewId])); alert(localize('extensionFoundInSection', "1 extension found in the {0} section.", view.name));
} else { } else {
alert(localize('extensionFound', "1 extension found.")); alert(localize('extensionFound', "1 extension found."));
} }
break; break;
default: default:
if (viewIdNameMappings[viewId]) { if (view) {
alert(localize('extensionsFoundInSection', "{0} extensions found in the {1} section.", count, viewIdNameMappings[viewId])); alert(localize('extensionsFoundInSection', "{0} extensions found in the {1} section.", count, view.name));
} else { } else {
alert(localize('extensionsFound', "{0} extensions found.", count)); alert(localize('extensionsFound', "{0} extensions found.", count));
} }
@@ -197,6 +197,7 @@ export class ExtensionsListView extends ViewPane {
case 'installs': options = assign(options, { sortBy: SortBy.InstallCount }); break; case 'installs': options = assign(options, { sortBy: SortBy.InstallCount }); break;
case 'rating': options = assign(options, { sortBy: SortBy.WeightedRating }); break; case 'rating': options = assign(options, { sortBy: SortBy.WeightedRating }); break;
case 'name': options = assign(options, { sortBy: SortBy.Title }); break; case 'name': options = assign(options, { sortBy: SortBy.Title }); break;
case 'publishedDate': options = assign(options, { sortBy: SortBy.PublishedDate }); break;
} }
const successCallback = (model: IPagedModel<IExtension>) => { const successCallback = (model: IPagedModel<IExtension>) => {
@@ -882,16 +883,21 @@ export class ExtensionsListView extends ViewPane {
this.list = null; this.list = null;
} }
static isBuiltInExtensionsQuery(query: string): boolean {
return /^\s*@builtin\s*$/i.test(query);
}
static isLocalExtensionsQuery(query: string): boolean { static isLocalExtensionsQuery(query: string): boolean {
return this.isInstalledExtensionsQuery(query) return this.isInstalledExtensionsQuery(query)
|| this.isOutdatedExtensionsQuery(query) || this.isOutdatedExtensionsQuery(query)
|| this.isEnabledExtensionsQuery(query) || this.isEnabledExtensionsQuery(query)
|| this.isDisabledExtensionsQuery(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 { static isInstalledExtensionsQuery(query: string): boolean {
@@ -977,7 +983,7 @@ export class ServerExtensionsView extends ExtensionsListView {
async show(query: string): Promise<IPagedModel<IExtension>> { async show(query: string): Promise<IPagedModel<IExtension>> {
query = query ? query : '@installed'; query = query ? query : '@installed';
if (!ExtensionsListView.isLocalExtensionsQuery(query) && !ExtensionsListView.isBuiltInExtensionsQuery(query)) { if (!ExtensionsListView.isLocalExtensionsQuery(query)) {
query = query += ' @installed'; query = query += ' @installed';
} }
return super.show(query.trim()); 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<IPagedModel<IExtension>> {
query = query || '@outdated';
return ExtensionsListView.isOutdatedExtensionsQuery(query) ? super.show(query) : this.showEmptyModel();
}
}
export class InstalledExtensionsView extends ExtensionsListView {
async show(query: string): Promise<IPagedModel<IExtension>> {
query = query || '@installed';
return ExtensionsListView.isInstalledExtensionsQuery(query) ? super.show(query) : this.showEmptyModel();
}
}
export class SearchBuiltInExtensionsView extends ExtensionsListView {
async show(query: string): Promise<IPagedModel<IExtension>> {
return ExtensionsListView.isSearchBuiltInExtensionsQuery(query) ? super.show(query) : this.showEmptyModel();
}
}
export class BuiltInFeatureExtensionsView extends ExtensionsListView {
async show(query: string): Promise<IPagedModel<IExtension>> { async show(query: string): Promise<IPagedModel<IExtension>> {
return (query && query.trim() !== '@builtin') ? this.showEmptyModel() : super.show('@builtin:features'); 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<IPagedModel<IExtension>> { async show(query: string): Promise<IPagedModel<IExtension>> {
return (query && query.trim() !== '@builtin') ? this.showEmptyModel() : super.show('@builtin:basics'); return (query && query.trim() !== '@builtin') ? this.showEmptyModel() : super.show('@builtin:basics');
} }
@@ -13,7 +13,8 @@ export const CELL_RUN_GUTTER = 28;
export const CODE_CELL_LEFT_MARGIN = 32; export const CODE_CELL_LEFT_MARGIN = 32;
export const EDITOR_TOOLBAR_HEIGHT = 0; 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; export const CELL_STATUSBAR_HEIGHT = 22;
// Margin above editor // Margin above editor
@@ -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 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 const EDITOR_WIDGET_ACTION_WEIGHT = KeybindingWeight.EditorContrib; // smaller than Suggest Widget, etc
@@ -407,6 +408,11 @@ registerAction2(class extends NotebookCellAction {
weight: KeybindingWeight.WorkbenchContrib weight: KeybindingWeight.WorkbenchContrib
}, },
precondition: ContextKeyExpr.and(NOTEBOOK_IS_ACTIVE_EDITOR), 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, primary: KeyCode.KEY_M,
weight: KeybindingWeight.WorkbenchContrib 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_MARKDOWN_EDIT_MODE.toNegated(),
NOTEBOOK_CELL_EDITABLE), NOTEBOOK_CELL_EDITABLE),
order: CellToolbarOrder.EditCell, order: CellToolbarOrder.EditCell,
group: CELL_TITLE_GROUP_ID group: CELL_TITLE_CELL_GROUP_ID
}, },
icon: { id: 'codicon/pencil' } icon: { id: 'codicon/pencil' }
}); });
@@ -625,7 +636,7 @@ registerAction2(class extends NotebookCellAction {
NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE,
NOTEBOOK_CELL_EDITABLE), NOTEBOOK_CELL_EDITABLE),
order: CellToolbarOrder.SaveCell, order: CellToolbarOrder.SaveCell,
group: CELL_TITLE_GROUP_ID group: CELL_TITLE_CELL_GROUP_ID
}, },
icon: { id: 'codicon/check' }, icon: { id: 'codicon/check' },
keybinding: { keybinding: {
@@ -659,7 +670,7 @@ registerAction2(class extends NotebookCellAction {
id: MenuId.NotebookCellTitle, id: MenuId.NotebookCellTitle,
order: CellToolbarOrder.DeleteCell, order: CellToolbarOrder.DeleteCell,
when: NOTEBOOK_EDITOR_EDITABLE, when: NOTEBOOK_EDITOR_EDITABLE,
group: CELL_TITLE_GROUP_ID group: CELL_TITLE_CELL_GROUP_ID
}, },
keybinding: { keybinding: {
primary: KeyCode.Delete, primary: KeyCode.Delete,
@@ -758,6 +769,11 @@ registerAction2(class extends NotebookCellAction {
primary: KeyMod.CtrlCmd | KeyCode.KEY_C, primary: KeyMod.CtrlCmd | KeyCode.KEY_C,
weight: EDITOR_WIDGET_ACTION_WEIGHT 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, primary: KeyMod.CtrlCmd | KeyCode.KEY_X,
weight: EDITOR_WIDGET_ACTION_WEIGHT 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, primary: KeyMod.CtrlCmd | KeyCode.KEY_V,
weight: EDITOR_WIDGET_ACTION_WEIGHT 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, id: MenuId.NotebookCellTitle,
when: ContextKeyExpr.and(NOTEBOOK_CELL_TYPE.isEqualTo('code'), NOTEBOOK_EDITOR_RUNNABLE), when: ContextKeyExpr.and(NOTEBOOK_CELL_TYPE.isEqualTo('code'), NOTEBOOK_EDITOR_RUNNABLE),
order: CellToolbarOrder.ClearCellOutput, order: CellToolbarOrder.ClearCellOutput,
group: CELL_TITLE_GROUP_ID group: CELL_TITLE_OUTPUT_GROUP_ID
}, },
keybinding: { keybinding: {
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey), NOTEBOOK_CELL_HAS_OUTPUTS), when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey), NOTEBOOK_CELL_HAS_OUTPUTS),
@@ -1339,7 +1365,11 @@ registerAction2(class extends NotebookCellAction {
id: MenuId.NotebookCellTitle, id: MenuId.NotebookCellTitle,
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE, InputFocusedContext), when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE, InputFocusedContext),
order: CellToolbarOrder.SplitCell, 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' }, icon: { id: 'codicon/split-vertical' },
keybinding: { keybinding: {
@@ -1392,6 +1422,11 @@ registerAction2(class extends NotebookCellAction {
when: NOTEBOOK_EDITOR_FOCUSED, when: NOTEBOOK_EDITOR_FOCUSED,
primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.KEY_J, primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.KEY_J,
weight: KeybindingWeight.WorkbenchContrib weight: KeybindingWeight.WorkbenchContrib
},
menu: {
id: MenuId.NotebookCellTitle,
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE),
group: '2_edit',
} }
}); });
} }
@@ -166,8 +166,7 @@ export class KernelStatus extends Disposable implements IWorkbenchContribution {
const activeEditor = getActiveNotebookEditor(this._editorService); const activeEditor = getActiveNotebookEditor(this._editorService);
if (activeEditor && activeEditor.multipleKernelsAvailable) { if (activeEditor) {
this.showKernelStatus(activeEditor.activeKernel);
this._editorDisposable.add(activeEditor.onDidChangeKernel(() => { this._editorDisposable.add(activeEditor.onDidChangeKernel(() => {
if (activeEditor.multipleKernelsAvailable) { if (activeEditor.multipleKernelsAvailable) {
this.showKernelStatus(activeEditor.activeKernel); this.showKernelStatus(activeEditor.activeKernel);
@@ -175,6 +174,18 @@ export class KernelStatus extends Disposable implements IWorkbenchContribution {
this.kernelInfoElement.clear(); 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 { } else {
this.kernelInfoElement.clear(); this.kernelInfoElement.clear();
} }
@@ -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 { .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container {
position: absolute; position: absolute;
display: flex; display: flex;
justify-content: center;
z-index: 30; /* over the focus outline on the editor */
width: 100%;
opacity: 0; opacity: 0;
transition: opacity 0.2s ease-in-out; transition: opacity 0.2s ease-in-out;
cursor: auto; cursor: auto;
@@ -531,19 +534,8 @@
align-items: center; 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 { .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 {
height: 1px; margin-right: 16px;
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 span.codicon { .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 { .monaco-workbench .notebookOverlay > .cell-list-container .notebook-folding-indicator .codicon {
visibility: visible; visibility: visible;
padding: 8px 0 0 10px; padding: 10px 0 0 10px;
} }
/** Theming */ /** Theming */
@@ -739,3 +731,11 @@
.monaco-workbench.vs-dark .monaco-workbench .notebookOverlay .cell.markdown table > tbody > tr > td { .monaco-workbench.vs-dark .monaco-workbench .notebookOverlay .cell.markdown table > tbody > tr > td {
border-color: rgba(255, 255, 255, 0.18); 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;
}
@@ -180,6 +180,7 @@ export interface INotebookEditor extends IEditor {
isNotebookEditor: boolean; isNotebookEditor: boolean;
activeKernel: INotebookKernelInfo | INotebookKernelInfo2 | undefined; activeKernel: INotebookKernelInfo | INotebookKernelInfo2 | undefined;
multipleKernelsAvailable: boolean; multipleKernelsAvailable: boolean;
readonly onDidChangeAvailableKernels: Event<void>;
readonly onDidChangeKernel: Event<void>; readonly onDidChangeKernel: Event<void>;
isDisposed: boolean; isDisposed: boolean;
@@ -240,10 +241,16 @@ export interface INotebookEditor extends IEditor {
moveCellDown(cell: ICellViewModel): Promise<ICellViewModel | null>; moveCellDown(cell: ICellViewModel): Promise<ICellViewModel | null>;
/** /**
* @deprecated Note that this method doesn't support batch operations, use #moveCellToIdx instead.
* Move a cell above or below another cell * Move a cell above or below another cell
*/ */
moveCell(cell: ICellViewModel, relativeToCell: ICellViewModel, direction: 'above' | 'below'): Promise<ICellViewModel | null>; moveCell(cell: ICellViewModel, relativeToCell: ICellViewModel, direction: 'above' | 'below'): Promise<ICellViewModel | null>;
/**
* Move a cell to a specific position
*/
moveCellToIdx(cell: ICellViewModel, index: number): Promise<ICellViewModel | null>;
/** /**
* Focus the container of a cell (the monaco editor inside is not focused). * Focus the container of a cell (the monaco editor inside is not focused).
*/ */
@@ -75,6 +75,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
private _overlayContainer!: HTMLElement; private _overlayContainer!: HTMLElement;
private _body!: HTMLElement; private _body!: HTMLElement;
private _webview: BackLayerWebView | null = null; private _webview: BackLayerWebView | null = null;
private _webviewResolved: boolean = false;
private _webviewResolvePromise: Promise<BackLayerWebView | null> | null = null;
private _webviewTransparentCover: HTMLElement | null = null; private _webviewTransparentCover: HTMLElement | null = null;
private _list: INotebookCellList | undefined; private _list: INotebookCellList | undefined;
private _dndController: CellDragAndDropController | null = null; private _dndController: CellDragAndDropController | null = null;
@@ -135,6 +137,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
private _activeKernel: INotebookKernelInfo | INotebookKernelInfo2 | undefined = undefined; private _activeKernel: INotebookKernelInfo | INotebookKernelInfo2 | undefined = undefined;
private readonly _onDidChangeKernel = this._register(new Emitter<void>()); private readonly _onDidChangeKernel = this._register(new Emitter<void>());
readonly onDidChangeKernel: Event<void> = this._onDidChangeKernel.event; readonly onDidChangeKernel: Event<void> = this._onDidChangeKernel.event;
private readonly _onDidChangeAvailableKernels = this._register(new Emitter<void>());
readonly onDidChangeAvailableKernels: Event<void> = this._onDidChangeAvailableKernels.event;
get activeKernel() { get activeKernel() {
return this._activeKernel; return this._activeKernel;
@@ -150,7 +154,16 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
} }
private _currentKernelTokenSource: CancellationTokenSource | undefined = undefined; 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<this>()); private readonly _onDidChangeActiveEditor = this._register(new Emitter<this>());
readonly onDidChangeActiveEditor: Event<this> = this._onDidChangeActiveEditor.event; readonly onDidChangeActiveEditor: Event<this> = this._onDidChangeActiveEditor.event;
@@ -572,7 +585,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
// @deprecated // @deprecated
if (provider && provider.kernel) { if (provider && provider.kernel) {
// it has a builtin kernel, don't automatically choose a 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(); tokenSource.dispose();
return; return;
} }
@@ -591,7 +604,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
// the provider doesn't have a builtin kernel, choose a kernel // the provider doesn't have a builtin kernel, choose a kernel
this.activeKernel = availableKernels[0]; this.activeKernel = availableKernels[0];
if (this.activeKernel) { if (this.activeKernel) {
this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel); await this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel);
} }
tokenSource.dispose(); tokenSource.dispose();
@@ -611,7 +624,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
} }
if (this.activeKernel) { 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 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) { if (kernelsFromSameExtension.length) {
const preferedKernel = kernelsFromSameExtension.find(kernel => kernel.isPreferred) || kernelsFromSameExtension[0]; const preferedKernel = kernelsFromSameExtension.find(kernel => kernel.isPreferred) || kernelsFromSameExtension[0];
this.activeKernel = preferedKernel; 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); await preferedKernel.resolve(this.viewModel!.uri, this.getId(), tokenSource.token);
tokenSource.dispose(); tokenSource.dispose();
return; return;
@@ -633,15 +646,16 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
// the provider doesn't have a builtin kernel, choose a kernel // the provider doesn't have a builtin kernel, choose a kernel
this.activeKernel = kernels[0]; this.activeKernel = kernels[0];
if (this.activeKernel) { 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); await this.activeKernel.resolve(this.viewModel!.uri, this.getId(), tokenSource.token);
} }
tokenSource.dispose(); tokenSource.dispose();
} }
private _loadKernelPreloads(extensionLocation: URI, kernel: INotebookKernelInfoDto) { private async _loadKernelPreloads(extensionLocation: URI, kernel: INotebookKernelInfoDto) {
if (kernel.preloads) { if (kernel.preloads && kernel.preloads.length) {
await this._resolveWebview();
this._webview?.updateKernelPreloads([extensionLocation], kernel.preloads.map(preload => URI.revive(preload))); this._webview?.updateKernelPreloads([extensionLocation], kernel.preloads.map(preload => URI.revive(preload)));
} }
} }
@@ -656,12 +670,24 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
this._notebookExecuting?.set(notebookMetadata.runState === NotebookRunState.Running); this._notebookExecuting?.set(notebookMetadata.runState === NotebookRunState.Running);
} }
private async _createWebview(id: string, resource: URI): Promise<void> { private async _resolveWebview(): Promise<BackLayerWebView | null> {
this._webview = this.instantiationService.createInstance(BackLayerWebView, this, id, resource); 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 // attach the webview container to the DOM tree first
this._list?.rowsContainer.insertAdjacentElement('afterbegin', this._webview.element); this._list?.rowsContainer.insertAdjacentElement('afterbegin', this._webview.element);
await this._webview.createWebview(); }
this._webview.webview.onDidBlur(() => {
this._webviewResolvePromise = new Promise(async resolve => {
await this._webview!.createWebview();
this._webview!.webview!.onDidBlur(() => {
this._outputFocus?.set(false); this._outputFocus?.set(false);
this.updateEditorFocus(); this.updateEditorFocus();
@@ -669,7 +695,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
this._webiewFocused = false; this._webiewFocused = false;
} }
}); });
this._webview.webview.onDidFocus(() => { this._webview!.webview!.onDidFocus(() => {
this._outputFocus?.set(true); this._outputFocus?.set(true);
this.updateEditorFocus(); this.updateEditorFocus();
this._onDidFocusEmitter.fire(); this._onDidFocusEmitter.fire();
@@ -679,11 +705,28 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
} }
}); });
this._localStore.add(this._webview.onMessage(({ message, forRenderer }) => { this._localStore.add(this._webview!.onMessage(({ message, forRenderer }) => {
if (this.viewModel) { if (this.viewModel) {
this.notebookService.onDidReceiveMessage(this.viewModel.viewType, this.getId(), forRenderer, message); 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<void> {
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);
} }
private async _attachModel(textModel: NotebookTextModel, viewState: INotebookEditorViewState | undefined) { private async _attachModel(textModel: NotebookTextModel, viewState: INotebookEditorViewState | undefined) {
@@ -717,10 +760,17 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
} }
} }
if (this.viewModel.renderers.size) {
await this._resolveWebview();
this._webview?.updateRendererPreloads(this.viewModel.renderers); this._webview?.updateRendererPreloads(this.viewModel.renderers);
}
this._localStore.add(this._list!.onWillScroll(e => { 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`; 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 scrollTop = this._list?.scrollTop || 0;
const scrollHeight = this._list?.scrollHeight || 0; const scrollHeight = this._list?.scrollHeight || 0;
if (!this._webviewResolved) {
return;
}
this._webview!.element.style.height = `${scrollHeight}px`; this._webview!.element.style.height = `${scrollHeight}px`;
if (this._webview?.insetMapping) { if (this._webview?.insetMapping) {
@@ -1128,6 +1183,15 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
return this._moveCellToIndex(originalIdx, newIdx); return this._moveCellToIndex(originalIdx, newIdx);
} }
async moveCellToIdx(cell: ICellViewModel, index: number): Promise<ICellViewModel | null> {
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<ICellViewModel | null> { private async _moveCellToIndex(index: number, newIdx: number): Promise<ICellViewModel | null> {
if (index === newIdx) { if (index === newIdx) {
return null; return null;
@@ -1356,6 +1420,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
return; return;
} }
await this._resolveWebview();
let preloads = this._notebookViewModel!.renderers; let preloads = this._notebookViewModel!.renderers;
if (!this._webview!.insetMapping.has(output)) { if (!this._webview!.insetMapping.has(output)) {
@@ -1370,7 +1436,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
} }
removeInset(output: IProcessedOutput) { removeInset(output: IProcessedOutput) {
if (!this._webview) { if (!this._webview || !this._webviewResolved) {
return; return;
} }
@@ -1378,7 +1444,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
} }
hideInset(output: IProcessedOutput) { hideInset(output: IProcessedOutput) {
if (!this._webview) { if (!this._webview || !this._webviewResolved) {
return; return;
} }
@@ -1390,10 +1456,14 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
} }
postMessage(forRendererId: string | undefined, message: any) { postMessage(forRendererId: string | undefined, message: any) {
if (!this._webview || !this._webviewResolved) {
return;
}
if (forRendererId === undefined) { if (forRendererId === undefined) {
this._webview?.webview.postMessage(message); this._webview.webview?.postMessage(message);
} else { } 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 .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 > .monaco-toolbar { background-color: ${editorBackgroundColor}; }`);
collector.addRule(`.notebookOverlay .monaco-list-row.cell-drag-image { 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); const cellToolbarSeperator = theme.getColor(CELL_TOOLBAR_SEPERATOR);
if (cellToolbarSeperator) { 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 .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); 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}; }`); collector.addRule(`.notebookOverlay .monaco-list-row.cell-editor-focus .cell-editor-part:before { outline: solid 1px ${focusedEditorBorderColorColor}; }`);
} }
const editorBorderColor = theme.getColor(notebookCellBorder); const cellBorderColor = theme.getColor(notebookCellBorder);
if (editorBorderColor) { if (cellBorderColor) {
collector.addRule(`.notebookOverlay .monaco-list-row .cell-editor-part:before { outline: solid 1px ${editorBorderColor}; }`); collector.addRule(`.notebookOverlay .cell.markdown h1 { border-color: ${cellBorderColor}; }`);
} collector.addRule(`.notebookOverlay .monaco-list-row .cell-editor-part:before { outline: solid 1px ${cellBorderColor}; }`);
const headingBorderColor = theme.getColor(notebookCellBorder);
if (headingBorderColor) {
collector.addRule(`.notebookOverlay .cell.markdown h1 { border-color: ${headingBorderColor}; }`);
} }
const cellStatusSuccessIcon = theme.getColor(cellStatusIconSuccess); 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 > 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 > .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 { 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 { 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 .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-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; }`); collector.addRule(`.notebookOverlay .cell .run-button-container { width: ${CELL_RUN_GUTTER}px; }`);
@@ -219,7 +219,7 @@ export interface INotebookWebviewMessage {
let version = 0; let version = 0;
export class BackLayerWebView extends Disposable { export class BackLayerWebView extends Disposable {
element: HTMLElement; element: HTMLElement;
webview!: WebviewElement; webview: WebviewElement | undefined = undefined;
insetMapping: Map<IProcessedOutput, ICachedInset> = new Map(); insetMapping: Map<IProcessedOutput, ICachedInset> = new Map();
hiddenInsetMapping: Set<IProcessedOutput> = new Set(); hiddenInsetMapping: Set<IProcessedOutput> = new Set();
reversedInsetMapping: Map<string, IProcessedOutput> = new Map(); reversedInsetMapping: Map<string, IProcessedOutput> = new Map();
@@ -714,7 +714,7 @@ ${loaderJs}
return; return;
} }
this.webview.focus(); this.webview?.focus();
} }
focusOutput(cellId: string) { focusOutput(cellId: string) {
@@ -722,7 +722,7 @@ ${loaderJs}
return; return;
} }
this.webview.focus(); this.webview?.focus();
setTimeout(() => { // Need this, or focus decoration is not shown. No clue. setTimeout(() => { // Need this, or focus decoration is not shown. No clue.
this._sendMessageToWebview({ this._sendMessageToWebview({
type: 'focus-output', type: 'focus-output',
@@ -814,6 +814,10 @@ ${loaderJs}
} }
private _updatePreloads(resources: IPreloadResource[], source: 'renderer' | 'kernel') { private _updatePreloads(resources: IPreloadResource[], source: 'renderer' | 'kernel') {
if (!this.webview) {
return;
}
const mixedResourceRoots = [...(this.localResourceRootsCache || []), ...this.rendererRootsCache, ...this.kernelRootsCache]; const mixedResourceRoots = [...(this.localResourceRootsCache || []), ...this.rendererRootsCache, ...this.kernelRootsCache];
this.webview.localResourcesRoot = mixedResourceRoots; this.webview.localResourcesRoot = mixedResourceRoots;
@@ -830,7 +834,7 @@ ${loaderJs}
return; return;
} }
this.webview.postMessage(message); this.webview?.postMessage(message);
} }
clearPreloadsCache() { clearPreloadsCache() {
@@ -839,7 +843,7 @@ ${loaderJs}
dispose() { dispose() {
this._disposed = true; this._disposed = true;
this.webview.dispose(); this.webview?.dispose();
super.dispose(); super.dispose();
} }
} }
@@ -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<MenuItemAction | SubmenuItemAction>]>, 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<IAction>(target) ? target : target.primary;
if (to.length > 0) {
to.push(new VerticalSeparator());
}
to.push(...actions);
} else {
const to = Array.isArray<IAction>(target) ? target : target.secondary;
if (to.length > 0) {
to.push(new Separator());
}
to.push(...actions);
}
}
}
function asDisposable(groups: ReadonlyArray<[string, ReadonlyArray<MenuItemAction | SubmenuItemAction>]>): IDisposable {
const disposables = new DisposableStore();
for (const [, actions] of groups) {
for (const action of actions) {
disposables.add(action);
}
}
return disposables;
}
@@ -3,16 +3,12 @@
* Licensed under the Source EULA. See License.txt in the project root for license information. * 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 { IMenu, IMenuService, MenuId } from 'vs/platform/actions/common/actions';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
export class CellMenus { export class CellMenus {
constructor( constructor(
@IMenuService private readonly menuService: IMenuService, @IMenuService private readonly menuService: IMenuService,
@IContextMenuService private readonly contextMenuService: IContextMenuService
) { } ) { }
getCellTitleMenu(contextKeyService: IContextKeyService): IMenu { getCellTitleMenu(contextKeyService: IContextKeyService): IMenu {
@@ -26,11 +22,6 @@ export class CellMenus {
private getMenu(menuId: MenuId, contextKeyService: IContextKeyService): IMenu { private getMenu(menuId: MenuId, contextKeyService: IContextKeyService): IMenu {
const menu = this.menuService.createMenu(menuId, contextKeyService); 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; return menu;
} }
@@ -36,17 +36,18 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { INotificationService } from 'vs/platform/notification/common/notification'; 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 { 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, CELL_TITLE_GROUP_ID } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions'; import { CancelCellAction, ChangeCellLanguageAction, ExecuteCellAction, INotebookCellActionContext } 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 { 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 { CellMenus } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellMenus';
import { CodeCell } from 'vs/workbench/contrib/notebook/browser/view/renderers/codeCell'; 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 { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel';
import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel'; import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel';
import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
import { CellKind, NotebookCellRunState, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { CellKind, NotebookCellMetadata, NotebookCellRunState } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys'; import { VerticalSeparator, createAndFillInActionBarActionsWithVerticalSeparators, VerticalSeparatorViewItem } from './cellActionView';
const $ = DOM.$; 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 cellMenu = this.instantiationService.createInstance(CellMenus);
const menu = disposables.add(cellMenu.getCellInsertionMenu(contextKeyService)); const menu = disposables.add(cellMenu.getCellInsertionMenu(contextKeyService));
@@ -220,7 +218,6 @@ abstract class AbstractCellRenderer {
templateData.betweenCellToolbar.context = context; templateData.betweenCellToolbar.context = context;
const container = templateData.bottomCellContainer; const container = templateData.bottomCellContainer;
if (element instanceof CodeCellViewModel) {
const bottomToolbarOffset = element.layoutInfo.bottomToolbarOffset; const bottomToolbarOffset = element.layoutInfo.bottomToolbarOffset;
container.style.top = `${bottomToolbarOffset}px`; container.style.top = `${bottomToolbarOffset}px`;
@@ -229,16 +226,20 @@ abstract class AbstractCellRenderer {
container.style.top = `${bottomToolbarOffset}px`; container.style.top = `${bottomToolbarOffset}px`;
})); }));
} }
}
protected createToolbar(container: HTMLElement): ToolBar { protected createToolbar(container: HTMLElement): ToolBar {
const toolbar = new ToolBar(container, this.contextMenuService, { const toolbar = new ToolBar(container, this.contextMenuService, {
getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id),
actionViewItemProvider: action => { actionViewItemProvider: action => {
if (action instanceof MenuItemAction) { if (action instanceof MenuItemAction) {
const item = new ContextAwareMenuEntryActionViewItem(action, this.keybindingService, this.notificationService, this.contextMenuService); const item = new ContextAwareMenuEntryActionViewItem(action, this.keybindingService, this.notificationService, this.contextMenuService);
return item; return item;
} }
if (action.id === VerticalSeparator.ID) {
return new VerticalSeparatorViewItem(undefined, action);
}
return undefined; return undefined;
} }
}); });
@@ -249,16 +250,11 @@ abstract class AbstractCellRenderer {
private getCellToolbarActions(menu: IMenu): { primary: IAction[], secondary: IAction[] } { private getCellToolbarActions(menu: IMenu): { primary: IAction[], secondary: IAction[] } {
const primary: IAction[] = []; const primary: IAction[] = [];
const secondary: IAction[] = []; const secondary: IAction[] = [];
const actions = menu.getActions({ shouldForwardArgs: true }); const result = { primary, secondary };
for (let [id, menuActions] of actions) {
if (id === CELL_TITLE_GROUP_ID) {
primary.push(...menuActions);
} else {
secondary.push(...menuActions);
}
}
return { primary, secondary }; createAndFillInActionBarActionsWithVerticalSeparators(menu, { shouldForwardArgs: true }, result, g => /^inline/.test(g));
return result;
} }
protected setupCellToolbarActions(templateData: BaseCellRenderTemplate, disposables: DisposableStore): void { 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 foldingIndicator = DOM.append(focusIndicator, DOM.$('.notebook-folding-indicator'));
const bottomCellContainer = DOM.append(container, $('.cell-bottom-toolbar-container')); const bottomCellContainer = DOM.append(container, $('.cell-bottom-toolbar-container'));
DOM.append(bottomCellContainer, $('.separator'));
const betweenCellToolbar = disposables.add(this.createBetweenCellToolbar(bottomCellContainer, disposables, contextKeyService)); const betweenCellToolbar = disposables.add(this.createBetweenCellToolbar(bottomCellContainer, disposables, contextKeyService));
DOM.append(bottomCellContainer, $('.separator'));
const statusBar = this.instantiationService.createInstance(CellEditorStatusBar, editorPart); const statusBar = this.instantiationService.createInstance(CellEditorStatusBar, editorPart);
const titleMenu = disposables.add(this.cellMenus.getCellTitleMenu(contextKeyService)); const titleMenu = disposables.add(this.cellMenus.getCellTitleMenu(contextKeyService));
@@ -439,7 +433,8 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR
this.setBetweenCellToolbarContext(templateData, element, toolbarContext); 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(this.editorOptions.onDidChange(newValue => markdownCell.updateEditorOptions(newValue)));
elementDisposables.add(markdownCell); elementDisposables.add(markdownCell);
@@ -611,6 +606,18 @@ export class CellDragAndDropController extends Disposable {
private onCellDrop(event: CellDragEvent): void { private onCellDrop(event: CellDragEvent): void {
const draggedCell = this.currentDraggedCell!; 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(); this.dragCleanup();
const isCopy = (event.browserEvent.ctrlKey && !platform.isMacintosh) || (event.browserEvent.altKey && platform.isMacintosh); const isCopy = (event.browserEvent.ctrlKey && !platform.isMacintosh) || (event.browserEvent.altKey && platform.isMacintosh);
@@ -625,9 +632,9 @@ export class CellDragAndDropController extends Disposable {
} }
if (isCopy) { if (isCopy) {
this.copyCell(draggedCell, event.draggedOverCell, dropDirection); this.copyCells(draggedCells, event.draggedOverCell, dropDirection);
} else { } 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') { private async moveCells(draggedCells: ICellViewModel[], ontoCell: ICellViewModel, direction: 'above' | 'below') {
await this.notebookEditor.moveCell(draggedCell, ontoCell, direction); 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);
} }
private copyCell(draggedCell: ICellViewModel, ontoCell: ICellViewModel, direction: 'above' | 'below') { this.notebookEditor.textModel!.pushStackElement('Move Cells');
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')); const focusSinkElement = DOM.append(container, $('.cell-editor-focus-sink'));
focusSinkElement.setAttribute('tabindex', '0'); focusSinkElement.setAttribute('tabindex', '0');
const bottomCellContainer = DOM.append(container, $('.cell-bottom-toolbar-container')); 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 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)); const titleMenu = disposables.add(this.cellMenus.getCellTitleMenu(contextKeyService));
@@ -21,7 +21,7 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/renderers/sizeObserver'; 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 editor: CodeEditorWidget | null = null;
private markdownContainer: HTMLElement; private markdownContainer: HTMLElement;
@@ -95,7 +95,7 @@ export class StatefullMarkdownCell extends Disposable {
this._register(viewCell.onDidChangeLayout((e) => { this._register(viewCell.onDidChangeLayout((e) => {
const layoutInfo = this.editor?.getLayoutInfo(); 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(); this.onCellEditorWidthChange();
} else if (e.totalHeight || e.outerWidth) { } else if (e.totalHeight || e.outerWidth) {
this.relayoutCell(); this.relayoutCell();
@@ -8,7 +8,7 @@ import * as UUID from 'vs/base/common/uuid';
import * as editorCommon from 'vs/editor/common/editorCommon'; import * as editorCommon from 'vs/editor/common/editorCommon';
import * as model from 'vs/editor/common/model'; import * as model from 'vs/editor/common/model';
import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer'; 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 { 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 { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { CellKind, NotebookCellOutputsSplice, INotebookSearchOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; 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 indicatorHeight = editorHeight + CELL_STATUSBAR_HEIGHT + outputTotalHeight;
const outputContainerOffset = EDITOR_TOOLBAR_HEIGHT + EDITOR_TOP_MARGIN + editorHeight + CELL_STATUSBAR_HEIGHT; 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; const editorWidth = state.outerWidth !== undefined ? this.computeEditorWidth(state.outerWidth) : this._layoutInfo?.editorWidth;
this._layoutInfo = { this._layoutInfo = {
@@ -8,7 +8,7 @@ import { Emitter, Event } from 'vs/base/common/event';
import * as UUID from 'vs/base/common/uuid'; import * as UUID from 'vs/base/common/uuid';
import * as editorCommon from 'vs/editor/common/editorCommon'; import * as editorCommon from 'vs/editor/common/editorCommon';
import * as model from 'vs/editor/common/model'; 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 { CellFindMatch, ICellViewModel, MarkdownCellLayoutChangeEvent, MarkdownCellLayoutInfo, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { MarkdownRenderer } from 'vs/workbench/contrib/notebook/browser/view/renderers/mdRenderer'; import { MarkdownRenderer } from 'vs/workbench/contrib/notebook/browser/view/renderers/mdRenderer';
import { BaseCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel'; import { BaseCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel';
@@ -93,13 +93,14 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie
layoutChange(state: MarkdownCellLayoutChangeEvent) { layoutChange(state: MarkdownCellLayoutChangeEvent) {
// recompute // recompute
const editorWidth = state.outerWidth !== undefined ? this.computeEditorWidth(state.outerWidth) : this._layoutInfo.editorWidth; const editorWidth = state.outerWidth !== undefined ? this.computeEditorWidth(state.outerWidth) : this._layoutInfo.editorWidth;
const totalHeight = state.totalHeight === undefined ? this._layoutInfo.totalHeight : state.totalHeight;
this._layoutInfo = { this._layoutInfo = {
fontInfo: state.font || this._layoutInfo.fontInfo, fontInfo: state.font || this._layoutInfo.fontInfo,
editorWidth, editorWidth,
editorHeight: this._editorHeight, editorHeight: this._editorHeight,
bottomToolbarOffset: BOTTOM_CELL_TOOLBAR_HEIGHT, bottomToolbarOffset: totalHeight - BOTTOM_CELL_TOOLBAR_HEIGHT - BOTTOM_CELL_TOOLBAR_OFFSET,
totalHeight: state.totalHeight === undefined ? this._layoutInfo.totalHeight : state.totalHeight totalHeight
}; };
this._onDidChangeLayout.fire(state); this._onDidChangeLayout.fire(state);
@@ -115,6 +116,7 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie
totalHeight: totalHeight, totalHeight: totalHeight,
editorHeight: this._editorHeight editorHeight: this._editorHeight
}; };
this.layoutChange({});
} }
} }
@@ -31,7 +31,7 @@ export class InsertCellEdit implements IResourceUndoRedoElement {
) { ) {
} }
undo(): void | Promise<void> { undo(): void {
if (!this.editingDelegate.deleteCell) { if (!this.editingDelegate.deleteCell) {
throw new Error('Notebook Delete Cell not implemented for Undo/Redo'); 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); this.editingDelegate.emitSelections(this.beforedSelections);
} }
} }
redo(): void | Promise<void> { redo(): void {
if (!this.editingDelegate.insertCell) { if (!this.editingDelegate.insertCell) {
throw new Error('Notebook Insert Cell not implemented for Undo/Redo'); 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()]; // this._rawCell.source = [cell.getText()];
} }
undo(): void | Promise<void> { undo(): void {
if (!this.editingDelegate.insertCell) { if (!this.editingDelegate.insertCell) {
throw new Error('Notebook Insert Cell not implemented for Undo/Redo'); throw new Error('Notebook Insert Cell not implemented for Undo/Redo');
} }
@@ -81,7 +81,7 @@ export class DeleteCellEdit implements IResourceUndoRedoElement {
} }
} }
redo(): void | Promise<void> { redo(): void {
if (!this.editingDelegate.deleteCell) { if (!this.editingDelegate.deleteCell) {
throw new Error('Notebook Delete Cell not implemented for Undo/Redo'); 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 { export class MoveCellEdit implements IResourceUndoRedoElement {
type: UndoRedoElementType.Resource = UndoRedoElementType.Resource; type: UndoRedoElementType.Resource = UndoRedoElementType.Resource;
label: string = 'Delete Cell'; label: string = 'Move Cell';
constructor( constructor(
public resource: URI, public resource: URI,
@@ -107,7 +107,7 @@ export class MoveCellEdit implements IResourceUndoRedoElement {
) { ) {
} }
undo(): void | Promise<void> { undo(): void {
if (!this.editingDelegate.moveCell) { if (!this.editingDelegate.moveCell) {
throw new Error('Notebook Move Cell not implemented for Undo/Redo'); throw new Error('Notebook Move Cell not implemented for Undo/Redo');
} }
@@ -118,7 +118,7 @@ export class MoveCellEdit implements IResourceUndoRedoElement {
} }
} }
redo(): void | Promise<void> { redo(): void {
if (!this.editingDelegate.moveCell) { if (!this.editingDelegate.moveCell) {
throw new Error('Notebook Move Cell not implemented for Undo/Redo'); throw new Error('Notebook Move Cell not implemented for Undo/Redo');
} }
@@ -142,7 +142,7 @@ export class SpliceCellsEdit implements IResourceUndoRedoElement {
) { ) {
} }
undo(): void | Promise<void> { undo(): void {
if (!this.editingDelegate.deleteCell || !this.editingDelegate.insertCell) { if (!this.editingDelegate.deleteCell || !this.editingDelegate.insertCell) {
throw new Error('Notebook Insert/Delete Cell not implemented for Undo/Redo'); throw new Error('Notebook Insert/Delete Cell not implemented for Undo/Redo');
} }
@@ -162,7 +162,7 @@ export class SpliceCellsEdit implements IResourceUndoRedoElement {
} }
} }
redo(): void | Promise<void> { redo(): void {
if (!this.editingDelegate.deleteCell || !this.editingDelegate.insertCell) { if (!this.editingDelegate.deleteCell || !this.editingDelegate.insertCell) {
throw new Error('Notebook Insert/Delete Cell not implemented for Undo/Redo'); throw new Error('Notebook Insert/Delete Cell not implemented for Undo/Redo');
} }
@@ -10,7 +10,7 @@ import { URI } from 'vs/base/common/uri';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; 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 { 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 { 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 { InsertCellEdit, DeleteCellEdit, MoveCellEdit, SpliceCellsEdit } from 'vs/workbench/contrib/notebook/common/model/cellEdit';
import { ITextModelService } from 'vs/editor/common/services/resolverService'; 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<void> {
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 { export class NotebookTextModel extends Disposable implements INotebookTextModel {
private _cellhandlePool: number = 0; private _cellhandlePool: number = 0;
@@ -112,6 +159,8 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
protected readonly _onDidChangeDirty = this._register(new Emitter<void>()); protected readonly _onDidChangeDirty = this._register(new Emitter<void>());
readonly onDidChangeDirty = this._onDidChangeDirty.event; readonly onDidChangeDirty = this._onDidChangeDirty.event;
private _operationManager: NotebookOperationManager;
constructor( constructor(
public handle: number, public handle: number,
public viewType: string, public viewType: string,
@@ -122,6 +171,8 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
) { ) {
super(); super();
this.cells = []; this.cells = [];
this._operationManager = new NotebookOperationManager(this._undoService, uri);
} }
get isDirty() { get isDirty() {
@@ -173,6 +224,10 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
this._increaseVersionId(); this._increaseVersionId();
} }
pushStackElement(label: string) {
this._operationManager.pushStackElement(label);
}
$applyEdit(modelVersionId: number, rawEdits: ICellEditOperation[], synchronous: boolean): boolean { $applyEdit(modelVersionId: number, rawEdits: ICellEditOperation[], synchronous: boolean): boolean {
if (modelVersionId !== this._versionId) { if (modelVersionId !== this._versionId) {
return false; return false;
@@ -255,7 +310,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
return [diff[0], deletedCells, diff[2]] as [number, NotebookCellTextModel[], NotebookCellTextModel[]]; 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), insertCell: this._insertCellDelegate.bind(this),
deleteCell: this._deleteCellDelegate.bind(this), deleteCell: this._deleteCellDelegate.bind(this),
emitSelections: this._emitSelectionsDelegate.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 { $handleEdit(label: string | undefined, undo: () => void, redo: () => void): void {
this._undoService.pushElement({ this._operationManager.pushEditOperation({
type: UndoRedoElementType.Resource, type: UndoRedoElementType.Resource,
resource: this.uri, resource: this.uri,
label: label ?? nls.localize('defaultEditLabel', "Edit"), 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); const cell = this.createCellTextModel(source, language, type, [], metadata);
if (pushUndoStop) { 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), insertCell: this._insertCellDelegate.bind(this),
deleteCell: this._deleteCellDelegate.bind(this), deleteCell: this._deleteCellDelegate.bind(this),
emitSelections: this._emitSelectionsDelegate.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 { insertCell2(index: number, cell: NotebookCellTextModel, synchronous: boolean, pushUndoStop: boolean): void {
if (pushUndoStop) { 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), insertCell: this._insertCellDelegate.bind(this),
deleteCell: this._deleteCellDelegate.bind(this), deleteCell: this._deleteCellDelegate.bind(this),
emitSelections: this._emitSelectionsDelegate.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) { deleteCell2(index: number, synchronous: boolean, pushUndoStop: boolean, beforeSelections: number[] | undefined, endSelections: number[] | undefined) {
const cell = this.cells[index]; const cell = this.cells[index];
if (pushUndoStop) { 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), insertCell: this._insertCellDelegate.bind(this),
deleteCell: this._deleteCellDelegate.bind(this), deleteCell: this._deleteCellDelegate.bind(this),
emitSelections: this._emitSelectionsDelegate.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 { moveCellToIdx2(index: number, newIdx: number, synchronous: boolean, pushedToUndoStack: boolean, beforeSelections: number[] | undefined, endSelections: number[] | undefined): boolean {
const cell = this.cells[index]; const cell = this.cells[index];
if (pushedToUndoStack) { 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) => { moveCell: (fromIndex: number, toIndex: number, beforeSelections: number[] | undefined, endSelections: number[] | undefined) => {
this.moveCellToIdx2(fromIndex, toIndex, true, false, beforeSelections, endSelections); this.moveCellToIdx2(fromIndex, toIndex, true, false, beforeSelections, endSelections);
}, },
@@ -63,6 +63,8 @@ export class TestNotebookEditor implements INotebookEditor {
) { } ) { }
multipleKernelsAvailable: boolean = false; multipleKernelsAvailable: boolean = false;
onDidChangeAvailableKernels: Event<void> = new Emitter<void>().event;
uri?: URI | undefined; uri?: URI | undefined;
textModel?: NotebookTextModel | undefined; textModel?: NotebookTextModel | undefined;
@@ -158,6 +160,10 @@ export class TestNotebookEditor implements INotebookEditor {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
moveCellToIdx(cell: ICellViewModel, index: number): Promise<ICellViewModel | null> {
throw new Error('Method not implemented.');
}
moveCell(cell: ICellViewModel, relativeToCell: ICellViewModel, direction: 'above' | 'below'): Promise<ICellViewModel | null> { moveCell(cell: ICellViewModel, relativeToCell: ICellViewModel, direction: 'above' | 'below'): Promise<ICellViewModel | null> {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
@@ -883,7 +883,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
resolve(undefined); resolve(undefined);
} }
} else { } else {
resolve(this.executeTask(task, resolver)); resolve(this.executeTask(task, resolver, runSource));
} }
}).then((value) => { }).then((value) => {
if (runSource === TaskRunSource.User) { if (runSource === TaskRunSource.User) {
@@ -1452,7 +1452,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
}; };
} }
private executeTask(task: Task, resolver: ITaskResolver): Promise<ITaskSummary> { private executeTask(task: Task, resolver: ITaskResolver, runSource?: TaskRunSource): Promise<ITaskSummary> {
enum SaveBeforeRunConfigOptions { enum SaveBeforeRunConfigOptions {
Always = 'always', Always = 'always',
Never = 'never', Never = 'never',
@@ -1464,7 +1464,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
const execTask = async (task: Task, resolver: ITaskResolver): Promise<ITaskSummary> => { const execTask = async (task: Task, resolver: ITaskResolver): Promise<ITaskSummary> => {
return ProblemMatcherRegistry.onReady().then(() => { return ProblemMatcherRegistry.onReady().then(() => {
let executeResult = this.getTaskSystem().run(task, resolver); 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<ITaskSummary> { private async handleExecuteResult(executeResult: ITaskExecuteResult, runSource?: TaskRunSource): Promise<ITaskSummary> {
if (executeResult.task.taskLoadMessages && executeResult.task.taskLoadMessages.length > 0) { if (executeResult.task.taskLoadMessages && executeResult.task.taskLoadMessages.length > 0) {
executeResult.task.taskLoadMessages.forEach(loadMessage => { executeResult.task.taskLoadMessages.forEach(loadMessage => {
this._outputChannel.append(loadMessage + '\n'); this._outputChannel.append(loadMessage + '\n');
@@ -1509,7 +1509,9 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
this.showOutput(); this.showOutput();
} }
if (runSource === TaskRunSource.User) {
await this.setRecentlyUsedTask(executeResult.task); await this.setRecentlyUsedTask(executeResult.task);
}
if (executeResult.kind === TaskExecuteKind.Active) { if (executeResult.kind === TaskExecuteKind.Active) {
let active = executeResult.active; let active = executeResult.active;
if (active && active.same) { if (active && active.same) {
@@ -840,6 +840,9 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
setTimeout(() => this.layout(this._timeoutDimension!), 0); setTimeout(() => this.layout(this._timeoutDimension!), 0);
} }
} }
if (!visible) {
this._widgetManager.hideHovers();
}
} }
public scrollDownLine(): void { public scrollDownLine(): void {
@@ -110,6 +110,8 @@ export class TerminalViewPane extends ViewPane {
} else { } else {
this.layoutBody(this._bodyDimensions.height, this._bodyDimensions.width); this.layoutBody(this._bodyDimensions.height, this._bodyDimensions.width);
} }
} else {
this._terminalService.getActiveTab()?.setVisible(false);
} }
})); }));
@@ -5,11 +5,15 @@
import { IDisposable } from 'vs/base/common/lifecycle'; import { IDisposable } from 'vs/base/common/lifecycle';
import { ITerminalWidget } from 'vs/workbench/contrib/terminal/browser/widgets/widgets'; import { ITerminalWidget } from 'vs/workbench/contrib/terminal/browser/widgets/widgets';
import { IHoverService } from 'vs/workbench/services/hover/browser/hover';
export class TerminalWidgetManager implements IDisposable { export class TerminalWidgetManager implements IDisposable {
private _container: HTMLElement | undefined; private _container: HTMLElement | undefined;
private _attached: Map<string, ITerminalWidget> = new Map(); private _attached: Map<string, ITerminalWidget> = new Map();
constructor(@IHoverService private readonly _hoverService: IHoverService) {
}
attachToElement(terminalWrapper: HTMLElement) { attachToElement(terminalWrapper: HTMLElement) {
if (!this._container) { if (!this._container) {
this._container = document.createElement('div'); this._container = document.createElement('div');
@@ -25,6 +29,10 @@ export class TerminalWidgetManager implements IDisposable {
} }
} }
hideHovers(): void {
this._hoverService.hideHover();
}
attachWidget(widget: ITerminalWidget): IDisposable | undefined { attachWidget(widget: ITerminalWidget): IDisposable | undefined {
if (!this._container) { if (!this._container) {
return undefined; // {{SQL CARBON EDIT}} strict-null-check return undefined; // {{SQL CARBON EDIT}} strict-null-check
@@ -16,7 +16,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
import { Emitter, Event } from 'vs/base/common/event'; import { Emitter, Event } from 'vs/base/common/event';
import { Disposable, dispose } from 'vs/base/common/lifecycle'; import { Disposable, dispose } from 'vs/base/common/lifecycle';
import { Codicon } from 'vs/base/common/codicons'; 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 { isEqual, basename } from 'vs/base/common/resources';
import { IDecorationsProvider, IDecorationData, IDecorationsService } from 'vs/workbench/services/decorations/browser/decorations'; import { IDecorationsProvider, IDecorationData, IDecorationsService } from 'vs/workbench/services/decorations/browser/decorations';
import { IProgressService } from 'vs/platform/progress/common/progress'; 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 { Severity } from 'vs/platform/notification/common/notification';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
export class UserDataManualSyncViewPane extends TreeViewPane { export class UserDataSyncMergesViewPane extends TreeViewPane {
private userDataSyncPreview: IUserDataSyncPreview; private userDataSyncPreview: IUserDataSyncPreview;
@@ -83,7 +83,7 @@ export class UserDataManualSyncViewPane extends TreeViewPane {
this.createButtons(container); this.createButtons(container);
const that = this; 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(); } }; this.treeView.dataProvider = { getChildren() { return that.getTreeItems(); } };
} }
@@ -164,7 +164,7 @@ export class UserDataManualSyncViewPane extends TreeViewPane {
icon: Codicon.cloudDownload, icon: Codicon.cloudDownload,
menu: { menu: {
id: MenuId.ViewItemContext, 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', group: 'inline',
order: 1, order: 1,
}, },
@@ -184,7 +184,7 @@ export class UserDataManualSyncViewPane extends TreeViewPane {
icon: Codicon.cloudUpload, icon: Codicon.cloudUpload,
menu: { menu: {
id: MenuId.ViewItemContext, 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', group: 'inline',
order: 2, order: 2,
}, },
@@ -204,7 +204,7 @@ export class UserDataManualSyncViewPane extends TreeViewPane {
icon: Codicon.merge, icon: Codicon.merge,
menu: { menu: {
id: MenuId.ViewItemContext, 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', group: 'inline',
order: 3, order: 3,
}, },
@@ -224,7 +224,7 @@ export class UserDataManualSyncViewPane extends TreeViewPane {
icon: Codicon.discard, icon: Codicon.discard,
menu: { menu: {
id: MenuId.ViewItemContext, 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', group: 'inline',
order: 3, order: 3,
}, },
@@ -270,7 +270,7 @@ export class UserDataManualSyncViewPane extends TreeViewPane {
previewResource = this.userDataSyncPreview.resources.find(({ local }) => isEqual(local, previewResource.local))!; previewResource = this.userDataSyncPreview.resources.find(({ local }) => isEqual(local, previewResource.local))!;
await this.reopen(previewResource); await this.reopen(previewResource);
if (previewResource.mergeState === MergeState.Conflict) { 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.") 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<void>): Promise<void> { private withProgress(task: () => Promise<void>): Promise<void> {
return this.progressService.withProgress({ location: MANUAL_SYNC_VIEW_ID, delay: 500 }, task); return this.progressService.withProgress({ location: SYNC_MERGES_VIEW_ID, delay: 500 }, task);
} }
} }
@@ -30,13 +30,13 @@ import { IStorageService } from 'vs/platform/storage/common/storage';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IAction, Action } from 'vs/base/common/actions'; 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 { IUserDataSyncMachinesService, IUserDataSyncMachine } from 'vs/platform/userDataSync/common/userDataSyncMachines';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { TreeView } from 'vs/workbench/contrib/views/browser/treeView'; import { TreeView } from 'vs/workbench/contrib/views/browser/treeView';
import { flatten } from 'vs/base/common/arrays'; 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 { export class UserDataSyncViewPaneContainer extends ViewPaneContainer {
@@ -86,7 +86,7 @@ export class UserDataSyncDataViews extends Disposable {
} }
private registerViews(container: ViewContainer): void { private registerViews(container: ViewContainer): void {
this.registerManualSyncView(container); this.registerMergesView(container);
this.registerActivityView(container, true); this.registerActivityView(container, true);
this.registerMachinesView(container); this.registerMachinesView(container);
@@ -94,17 +94,17 @@ export class UserDataSyncDataViews extends Disposable {
this.registerActivityView(container, false); this.registerActivityView(container, false);
} }
private registerManualSyncView(container: ViewContainer): void { private registerMergesView(container: ViewContainer): void {
const viewsRegistry = Registry.as<IViewsRegistry>(Extensions.ViewsRegistry); const viewsRegistry = Registry.as<IViewsRegistry>(Extensions.ViewsRegistry);
const viewName = localize('manual sync', "Manual Sync"); const viewName = localize('merges', "Merges");
viewsRegistry.registerViews([<ITreeViewDescriptor>{ viewsRegistry.registerViews([<ITreeViewDescriptor>{
id: MANUAL_SYNC_VIEW_ID, id: SYNC_MERGES_VIEW_ID,
name: viewName, name: viewName,
ctorDescriptor: new SyncDescriptor(UserDataManualSyncViewPane), ctorDescriptor: new SyncDescriptor(UserDataSyncMergesViewPane),
when: CONTEXT_ENABLE_MANUAL_SYNC_VIEW, when: CONTEXT_ENABLE_SYNC_MERGES_VIEW,
canToggleVisibility: false, canToggleVisibility: false,
canMoveView: 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, collapsed: false,
order: 100, order: 100,
}], container); }], container);
@@ -49,7 +49,12 @@ export async function openEditorWith(
return undefined; // {{SQL CARBON EDIT}} strict-null-checks 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) { if (overrideToUse) {
return overrideToUse[0].open(input, overrideOptions, group, OpenEditorContext.NEW_EDITOR)?.override; return overrideToUse[0].open(input, overrideOptions, group, OpenEditorContext.NEW_EDITOR)?.override;
} }
@@ -184,6 +184,7 @@ export class ExtensionHostManager extends Disposable {
getProxy: <T>(identifier: ProxyIdentifier<T>): T => this._rpcProtocol!.getProxy(identifier), getProxy: <T>(identifier: ProxyIdentifier<T>): T => this._rpcProtocol!.getProxy(identifier),
set: <T, R extends T>(identifier: ProxyIdentifier<T>, instance: R): R => this._rpcProtocol!.set(identifier, instance), set: <T, R extends T>(identifier: ProxyIdentifier<T>, instance: R): R => this._rpcProtocol!.set(identifier, instance),
assertRegistered: (identifiers: ProxyIdentifier<any>[]): void => this._rpcProtocol!.assertRegistered(identifiers), assertRegistered: (identifiers: ProxyIdentifier<any>[]): void => this._rpcProtocol!.assertRegistered(identifiers),
drain: (): Promise<void> => this._rpcProtocol!.drain(),
}; };
// Named customers // Named customers
@@ -18,6 +18,11 @@ export interface IRPCProtocol {
* Assert these identifiers are already registered via `.set`. * Assert these identifiers are already registered via `.set`.
*/ */
assertRegistered(identifiers: ProxyIdentifier<any>[]): void; assertRegistered(identifiers: ProxyIdentifier<any>[]): void;
/**
* Wait for the write buffer (if applicable) to become empty.
*/
drain(): Promise<void>;
} }
export class ProxyIdentifier<T> { export class ProxyIdentifier<T> {
@@ -115,6 +115,13 @@ export class RPCProtocol extends Disposable implements IRPCProtocol {
}); });
} }
public drain(): Promise<void> {
if (typeof this._protocol.drain === 'function') {
return this._protocol.drain();
}
return Promise.resolve();
}
private _onWillSendRequest(req: number): void { private _onWillSendRequest(req: number): void {
if (this._unacknowledgedCount === 0) { if (this._unacknowledgedCount === 0) {
// Since this is the first request we are sending in a while, // Since this is the first request we are sending in a while,
@@ -96,10 +96,10 @@ let onTerminate = function () {
nativeExit(); nativeExit();
}; };
function _createExtHostProtocol(): Promise<IMessagePassingProtocol> { function _createExtHostProtocol(): Promise<PersistentProtocol> {
if (process.env.VSCODE_EXTHOST_WILL_SEND_SOCKET) { if (process.env.VSCODE_EXTHOST_WILL_SEND_SOCKET) {
return new Promise<IMessagePassingProtocol>((resolve, reject) => { return new Promise<PersistentProtocol>((resolve, reject) => {
let protocol: PersistentProtocol | null = null; let protocol: PersistentProtocol | null = null;
@@ -163,7 +163,7 @@ function _createExtHostProtocol(): Promise<IMessagePassingProtocol> {
const pipeName = process.env.VSCODE_IPC_HOOK_EXTHOST!; const pipeName = process.env.VSCODE_IPC_HOOK_EXTHOST!;
return new Promise<IMessagePassingProtocol>((resolve, reject) => { return new Promise<PersistentProtocol>((resolve, reject) => {
const socket = net.createConnection(pipeName, () => { const socket = net.createConnection(pipeName, () => {
socket.removeListener('error', reject); socket.removeListener('error', reject);
@@ -203,6 +203,10 @@ async function createExtHostProtocol(): Promise<IMessagePassingProtocol> {
protocol.send(msg); protocol.send(msg);
} }
} }
drain(): Promise<void> {
return protocol.drain();
}
}; };
} }
@@ -6,7 +6,7 @@
import { IUserDataSyncService, IAuthenticationProvider, getUserDataSyncStore, isAuthenticationProvider, IUserDataAutoSyncService, SyncResource, IResourcePreview, ISyncResourcePreview, Change, IManualSyncTask } from 'vs/platform/userDataSync/common/userDataSync'; 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 { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; 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 { AuthenticationSession, AuthenticationSessionsChangeEvent } from 'vs/editor/common/modes';
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
import { Emitter, Event } from 'vs/base/common/event'; import { Emitter, Event } from 'vs/base/common/event';
@@ -79,7 +79,7 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat
private readonly syncEnablementContext: IContextKey<boolean>; private readonly syncEnablementContext: IContextKey<boolean>;
private readonly syncStatusContext: IContextKey<string>; private readonly syncStatusContext: IContextKey<string>;
private readonly accountStatusContext: IContextKey<string>; private readonly accountStatusContext: IContextKey<string>;
private readonly manualSyncViewEnablementContext: IContextKey<boolean>; private readonly mergesViewEnablementContext: IContextKey<boolean>;
private readonly activityViewsEnablementContext: IContextKey<boolean>; private readonly activityViewsEnablementContext: IContextKey<boolean>;
readonly userDataSyncPreview: UserDataSyncPreview = this._register(new UserDataSyncPreview(this.userDataSyncService)); 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.syncStatusContext = CONTEXT_SYNC_STATE.bindTo(contextKeyService);
this.accountStatusContext = CONTEXT_ACCOUNT_STATE.bindTo(contextKeyService); this.accountStatusContext = CONTEXT_ACCOUNT_STATE.bindTo(contextKeyService);
this.activityViewsEnablementContext = CONTEXT_ENABLE_ACTIVITY_VIEWS.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) { if (this.authenticationProviders.length) {
@@ -302,16 +302,16 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat
const result = await this.dialogService.show( const result = await this.dialogService.show(
Severity.Info, Severity.Info,
localize('preferences sync', "Preferences Sync"), localize('merge or replace', "Merge or Replace"),
[ [
localize('merge', "Merge"), localize('merge', "Merge"),
localize('replace local', "Replace Local"), localize('replace local', "Replace Local"),
localize('sync manually', "Sync Manually..."), localize('merge Manually', "Merge Manually..."),
localize('cancel', "Cancel"), localize('cancel', "Cancel"),
], ],
{ {
cancelId: 3, 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) { switch (result.choice) {
@@ -333,18 +333,18 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat
const visibleViewContainer = this.viewsService.getVisibleViewContainer(ViewContainerLocation.Sidebar); const visibleViewContainer = this.viewsService.getVisibleViewContainer(ViewContainerLocation.Sidebar);
this.userDataSyncPreview.setManualSyncPreview(task, preview); this.userDataSyncPreview.setManualSyncPreview(task, preview);
this.manualSyncViewEnablementContext.set(true); this.mergesViewEnablementContext.set(true);
await this.waitForActiveSyncViews(); 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); const error = await Event.toPromise(this.userDataSyncPreview.onDidCompleteManualSync);
this.userDataSyncPreview.unsetManualSyncPreview(); this.userDataSyncPreview.unsetManualSyncPreview();
this.manualSyncViewEnablementContext.set(false); this.mergesViewEnablementContext.set(false);
if (visibleViewContainer) { if (visibleViewContainer) {
this.viewsService.openViewContainer(visibleViewContainer.id); this.viewsService.openViewContainer(visibleViewContainer.id);
} else { } else {
const viewContainer = this.viewDescriptorService.getViewContainerByViewId(MANUAL_SYNC_VIEW_ID); const viewContainer = this.viewDescriptorService.getViewContainerByViewId(SYNC_MERGES_VIEW_ID);
this.viewsService.closeViewContainer(viewContainer!.id); this.viewsService.closeViewContainer(viewContainer!.id);
} }
@@ -82,7 +82,7 @@ export const CONTEXT_SYNC_STATE = new RawContextKey<string>('syncStatus', SyncSt
export const CONTEXT_SYNC_ENABLEMENT = new RawContextKey<boolean>('syncEnabled', false); export const CONTEXT_SYNC_ENABLEMENT = new RawContextKey<boolean>('syncEnabled', false);
export const CONTEXT_ACCOUNT_STATE = new RawContextKey<string>('userDataSyncAccountStatus', AccountStatus.Uninitialized); export const CONTEXT_ACCOUNT_STATE = new RawContextKey<string>('userDataSyncAccountStatus', AccountStatus.Uninitialized);
export const CONTEXT_ENABLE_ACTIVITY_VIEWS = new RawContextKey<boolean>(`enableSyncActivityViews`, false); export const CONTEXT_ENABLE_ACTIVITY_VIEWS = new RawContextKey<boolean>(`enableSyncActivityViews`, false);
export const CONTEXT_ENABLE_MANUAL_SYNC_VIEW = new RawContextKey<boolean>(`enableManualSyncView`, false); export const CONTEXT_ENABLE_SYNC_MERGES_VIEW = new RawContextKey<boolean>(`enableSyncMergesView`, false);
// Commands // Commands
export const CONFIGURE_SYNC_COMMAND_ID = 'workbench.userDataSync.actions.configure'; 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 // VIEWS
export const SYNC_VIEW_CONTAINER_ID = 'workbench.view.sync'; 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';
@@ -389,6 +389,9 @@ suite('ExtHostDiagnostics', () => {
assertRegistered(): void { assertRegistered(): void {
} }
drain() {
return undefined!;
}
}, new NullLogService()); }, new NullLogService());
let collection1 = diags.createDiagnosticCollection(nullExtensionDescription.identifier, 'foo'); let collection1 = diags.createDiagnosticCollection(nullExtensionDescription.identifier, 'foo');
@@ -438,6 +441,9 @@ suite('ExtHostDiagnostics', () => {
assertRegistered(): void { assertRegistered(): void {
} }
drain() {
return undefined!;
}
}, new NullLogService()); }, new NullLogService());
@@ -15,7 +15,8 @@ suite('ExtHostFileSystemEventService', () => {
const protocol: IMainContext = { const protocol: IMainContext = {
getProxy: () => { return undefined!; }, getProxy: () => { return undefined!; },
set: undefined!, set: undefined!,
assertRegistered: undefined! assertRegistered: undefined!,
drain: undefined!
}; };
const watcher1 = new ExtHostFileSystemEventService(protocol, new NullLogService(), undefined!).createFileSystemWatcher('**/somethingInteresting', false, false, false); const watcher1 = new ExtHostFileSystemEventService(protocol, new NullLogService(), undefined!).createFileSystemWatcher('**/somethingInteresting', false, false, false);
@@ -298,7 +298,8 @@ suite('ExtHostWorkspace', function () {
const protocol: IMainContext = { const protocol: IMainContext = {
getProxy: () => { return undefined!; }, getProxy: () => { return undefined!; },
set: () => { return undefined!; }, set: () => { return undefined!; },
assertRegistered: () => { } assertRegistered: () => { },
drain: () => { return undefined!; },
}; };
const ws = createExtHostWorkspace(protocol, { id: 'foo', name: 'Test', folders: [] }, new NullLogService()); const ws = createExtHostWorkspace(protocol, { id: 'foo', name: 'Test', folders: [] }, new NullLogService());
@@ -32,6 +32,7 @@ suite('MainThreadDiagnostics', function () {
$acceptMarkersChange() { } $acceptMarkersChange() { }
}; };
} }
drain(): any { return null; }
}, },
markerService, markerService,
new class extends mock<IUriIdentityService>() { new class extends mock<IUriIdentityService>() {
@@ -66,6 +66,7 @@ suite('MainThreadHostTreeView', function () {
getProxy(): any { getProxy(): any {
return extHostTreeViewsShape; return extHostTreeViewsShape;
} }
drain(): any { return null; }
}, new TestViewsService(), new TestNotificationService(), testExtensionService, new NullLogService()); }, new TestViewsService(), new TestNotificationService(), testExtensionService, new NullLogService());
mainThreadTreeViews.$registerTreeViewDataProvider(testTreeViewId, { showCollapseAll: false, canSelectMany: false }); mainThreadTreeViews.$registerTreeViewDataProvider(testTreeViewId, { showCollapseAll: false, canSelectMany: false });
await testExtensionService.whenInstalledExtensionsRegistered(); await testExtensionService.whenInstalledExtensionsRegistered();
@@ -19,7 +19,8 @@ export function SingleProxyRPCProtocol(thing: any): IExtHostContext & IExtHostRp
set<T, R extends T>(identifier: ProxyIdentifier<T>, value: R): R { set<T, R extends T>(identifier: ProxyIdentifier<T>, value: R): R {
return value; return value;
}, },
assertRegistered: undefined! assertRegistered: undefined!,
drain: undefined!
}; };
} }
@@ -40,6 +41,10 @@ export class TestRPCProtocol implements IExtHostContext, IExtHostRpcService {
this._proxies = Object.create(null); this._proxies = Object.create(null);
} }
drain(): Promise<void> {
return Promise.resolve();
}
private get _callCount(): number { private get _callCount(): number {
return this._callCountValue; return this._callCountValue;
} }