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

View File

@@ -49,7 +49,7 @@
"rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-node-resolve": "^5.2.0",
"terser": "4.3.8",
"typescript": "^4.0.0-dev.20200715",
"typescript": "^4.0.0-dev.20200722",
"vsce": "1.48.0",
"vscode-telemetry-extractor": "^1.6.0",
"xml2js": "^0.4.17"

View File

@@ -3539,10 +3539,10 @@ typescript@^3.0.1:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977"
integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==
typescript@^4.0.0-dev.20200715:
version "4.0.0-dev.20200715"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.0-dev.20200715.tgz#d65961a5a6f13fde95a6f4db5f5946f15e4c59bc"
integrity sha512-gmPXoWktfXeutmWTM6el9U4vIn5kqOHGI1OESSOhPtLWrxodKqLfFuMygQtOUTtGjKLFQRFAJhHEwUhHZNOURA==
typescript@^4.0.0-dev.20200722:
version "4.0.0-dev.20200722"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.0-dev.20200722.tgz#b59dd5a3cd84a98d5aae0e4f3a3c58f0c81a3b9b"
integrity sha512-MmJ1YyPNK3JYeKLiTg5sQXdeZaMgt99Fg4BMRZhJmhoq1/x2V1cpXMYvE1rtIYl9K7NvmTDdU3WDW7ZOD6ybaw==
typical@^4.0.0:
version "4.0.0"

View File

@@ -1801,7 +1801,7 @@
"Ignore",
"ignore"
],
"filenames": [
"extensions": [
".gitignore_global",
".gitignore"
],

View File

@@ -74,7 +74,7 @@ async function createModel(context: ExtensionContext, outputChannel: OutputChann
new GitTimelineProvider(model)
);
// await checkGitVersion(info); {{SQL CARBON EDIT}} Don't check git version
// checkGitVersion(info); {{SQL CARBON EDIT}} Don't check git version
return model;
}

View File

@@ -3,7 +3,7 @@
"version": "0.0.1",
"description": "Dependencies shared by all extensions",
"dependencies": {
"typescript": "3.9.6"
"typescript": "3.9.7"
},
"scripts": {
"postinstall": "node ./postinstall"

View File

@@ -76,10 +76,10 @@ rimraf@^3.0.2:
dependencies:
glob "^7.1.3"
typescript@3.9.6:
version "3.9.6"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.6.tgz#8f3e0198a34c3ae17091b35571d3afd31999365a"
integrity sha512-Pspx3oKAPJtjNwE92YS05HQoY7z2SFyOpHo9MqJor3BXAGNaPUs83CuVp9VISFkSjyRfiTpmKuAYGJB7S7hOxw==
typescript@3.9.7:
version "3.9.7"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa"
integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==
wrappy@1:
version "1.0.2"

View File

@@ -37,9 +37,6 @@ if "%INTEGRATION_TEST_ELECTRON_PATH%"=="" (
:: Tests in the extension host
REM call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-notebook-tests\test --enable-proposed-api=vscode.vscode-notebook-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-notebook-tests --extensionTestsPath=%~dp0\..\extensions\vscode-notebook-tests\out --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR%
REM if %errorlevel% neq 0 exit /b %errorlevel%
REM call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-api-tests\testWorkspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\singlefolder-tests --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR%
REM if %errorlevel% neq 0 exit /b %errorlevel%
@@ -61,6 +58,9 @@ REM if %errorlevel% neq 0 exit /b %errorlevel%
call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\azurecore\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\azurecore --extensionTestsPath=%~dp0\..\extensions\azurecore\out\test --no-cached-data --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR%
if %errorlevel% neq 0 exit /b %errorlevel%
REM call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-notebook-tests\test --enable-proposed-api=vscode.vscode-notebook-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-notebook-tests --extensionTestsPath=%~dp0\..\extensions\vscode-notebook-tests\out --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR%
REM if %errorlevel% neq 0 exit /b %errorlevel%
for /f "delims=" %%i in ('node -p "require('fs').realpathSync.native(require('os').tmpdir())"') do set TEMPDIR=%%i
set GITWORKSPACE=%TEMPDIR%\git-%RANDOM%
mkdir %GITWORKSPACE%

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/emmet/out/test/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR
"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $(mktemp -d 2>/dev/null) --enable-proposed-api=vscode.git --extensionDevelopmentPath=$ROOT/extensions/git --extensionTestsPath=$ROOT/extensions/git/out/test --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR
# "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-notebook-tests/test --enable-proposed-api=vscode.vscode-notebook-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-notebook-tests --extensionTestsPath=$ROOT/extensions/vscode-notebook-tests/out/ --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR
"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-notebook-tests/test --enable-proposed-api=vscode.vscode-notebook-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-notebook-tests --extensionTestsPath=$ROOT/extensions/vscode-notebook-tests/out/ --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR
"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/azurecore/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/azurecore --extensionTestsPath=$ROOT/extensions/azurecore/out/test --no-cached-data --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR

View File

@@ -34,5 +34,5 @@ export interface IContextMenuDelegate {
actionRunner?: IActionRunner;
autoSelectFirstItem?: boolean;
anchorAlignment?: AnchorAlignment;
anchorAsContainer?: boolean;
domForShadowRoot?: HTMLElement;
}

View File

@@ -5,7 +5,6 @@
.monaco-action-bar {
text-align: right;
overflow: hidden;
white-space: nowrap;
}

View File

@@ -28,7 +28,7 @@ function initialize() {
delayer.schedule();
}
function formatRule(c: Codicon) {
export function formatRule(c: Codicon) {
let def = c.definition;
while (def instanceof Codicon) {
def = def.definition;

View File

@@ -10,6 +10,12 @@ import { IDisposable, toDisposable, Disposable, DisposableStore } from 'vs/base/
import { Range } from 'vs/base/common/range';
import { BrowserFeatures } from 'vs/base/browser/canIUse';
export const enum ContextViewDOMPosition {
ABSOLUTE = 1,
FIXED,
FIXED_SHADOW
}
export interface IAnchor {
x: number;
y: number;
@@ -105,32 +111,62 @@ export class ContextView extends Disposable {
private container: HTMLElement | null = null;
private view: HTMLElement;
private useFixedPosition: boolean;
private useShadowDOM: boolean;
private delegate: IDelegate | null = null;
private toDisposeOnClean: IDisposable = Disposable.None;
private toDisposeOnSetContainer: IDisposable = Disposable.None;
private shadowRoot: ShadowRoot | null = null;
private shadowRootHostElement: HTMLElement | null = null;
constructor(container: HTMLElement, useFixedPosition: boolean) {
constructor(container: HTMLElement, domPosition: ContextViewDOMPosition) {
super();
this.view = DOM.$('.context-view');
this.useFixedPosition = false;
this.useShadowDOM = false;
DOM.hide(this.view);
this.setContainer(container, useFixedPosition);
this.setContainer(container, domPosition);
this._register(toDisposable(() => this.setContainer(null, false)));
this._register(toDisposable(() => this.setContainer(null, ContextViewDOMPosition.ABSOLUTE)));
}
setContainer(container: HTMLElement | null, useFixedPosition: boolean): void {
setContainer(container: HTMLElement | null, domPosition: ContextViewDOMPosition): void {
if (this.container) {
this.toDisposeOnSetContainer.dispose();
this.container.removeChild(this.view);
if (this.shadowRoot) {
this.shadowRoot.removeChild(this.view);
this.shadowRoot = null;
DOM.removeNode(this.shadowRootHostElement!);
this.shadowRootHostElement = null;
} else {
this.container.removeChild(this.view);
}
this.container = null;
}
if (container) {
this.container = container;
this.container.appendChild(this.view);
this.useFixedPosition = domPosition !== ContextViewDOMPosition.ABSOLUTE;
this.useShadowDOM = domPosition === ContextViewDOMPosition.FIXED_SHADOW;
if (this.useShadowDOM) {
this.shadowRootHostElement = DOM.$('.shadow-root-host');
this.container.appendChild(this.shadowRootHostElement);
this.shadowRoot = this.shadowRootHostElement.attachShadow({ mode: 'closed' });
this.shadowRoot.innerHTML = `
<style>
${SHADOW_ROOT_CSS}
</style>
`;
this.shadowRoot.appendChild(this.view);
this.shadowRoot.appendChild(DOM.$('slot'));
} else {
this.container.appendChild(this.view);
}
const toDisposeOnSetContainer = new DisposableStore();
@@ -148,8 +184,6 @@ export class ContextView extends Disposable {
this.toDisposeOnSetContainer = toDisposeOnSetContainer;
}
this.useFixedPosition = useFixedPosition;
}
show(delegate: IDelegate): void {
@@ -162,6 +196,7 @@ export class ContextView extends Disposable {
this.view.className = 'context-view';
this.view.style.top = '0px';
this.view.style.left = '0px';
this.view.style.zIndex = '2500';
this.view.style.position = this.useFixedPosition ? 'fixed' : 'absolute';
DOM.show(this.view);
@@ -301,3 +336,45 @@ export class ContextView extends Disposable {
super.dispose();
}
}
let SHADOW_ROOT_CSS = /* css */ `
:host {
all: initial; /* 1st rule so subsequent properties are reset. */
}
@font-face {
font-family: "codicon";
src: url("./codicon.ttf?5d4d76ab2ce5108968ad644d591a16a6") format("truetype");
}
.codicon[class*='codicon-'] {
font: normal normal normal 16px/1 codicon;
display: inline-block;
text-decoration: none;
text-rendering: auto;
text-align: center;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
}
:host-context(.mac) { font-family: -apple-system, BlinkMacSystemFont, sans-serif; }
:host-context(.mac:lang(zh-Hans)) { font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB", sans-serif; }
:host-context(.mac:lang(zh-Hant)) { font-family: -apple-system, BlinkMacSystemFont, "PingFang TC", sans-serif; }
:host-context(.mac:lang(ja)) { font-family: -apple-system, BlinkMacSystemFont, "Hiragino Kaku Gothic Pro", sans-serif; }
:host-context(.mac:lang(ko)) { font-family: -apple-system, BlinkMacSystemFont, "Nanum Gothic", "Apple SD Gothic Neo", "AppleGothic", sans-serif; }
:host-context(.windows) { font-family: "Segoe WPC", "Segoe UI", sans-serif; }
:host-context(.windows:lang(zh-Hans)) { font-family: "Segoe WPC", "Segoe UI", "Microsoft YaHei", sans-serif; }
:host-context(.windows:lang(zh-Hant)) { font-family: "Segoe WPC", "Segoe UI", "Microsoft Jhenghei", sans-serif; }
:host-context(.windows:lang(ja)) { font-family: "Segoe WPC", "Segoe UI", "Yu Gothic UI", "Meiryo UI", sans-serif; }
:host-context(.windows:lang(ko)) { font-family: "Segoe WPC", "Segoe UI", "Malgun Gothic", "Dotom", sans-serif; }
:host-context(.mac).linux) { font-family: system-ui, "Ubuntu", "Droid Sans", sans-serif; }
:host-context(.mac).linux:lang(zh-Hans)) { font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans SC", "Source Han Sans CN", "Source Han Sans", sans-serif; }
:host-context(.mac).linux:lang(zh-Hant)) { font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans TC", "Source Han Sans TW", "Source Han Sans", sans-serif; }
:host-context(.mac).linux:lang(ja)) { font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans J", "Source Han Sans JP", "Source Han Sans", sans-serif; }
:host-context(.mac).linux:lang(ko)) { font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans K", "Source Han Sans JR", "Source Han Sans", "UnDotum", "FBaekmuk Gulim", sans-serif; }
`;

View File

@@ -270,7 +270,7 @@ export class DropdownMenu extends BaseDropdown {
onHide: () => this.onHide(),
actionRunner: this.menuOptions ? this.menuOptions.actionRunner : undefined,
anchorAlignment: this.menuOptions ? this.menuOptions.anchorAlignment : AnchorAlignment.LEFT,
anchorAsContainer: this.menuAsChild
domForShadowRoot: this.menuAsChild ? this.element : undefined
});
}
@@ -297,15 +297,17 @@ export class DropdownMenuActionViewItem extends BaseActionViewItem {
private _onDidChangeVisibility = this._register(new Emitter<boolean>());
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, actionProvider: IActionProvider, contextMenuProvider: IContextMenuProvider, actionViewItemProvider: IActionViewItemProvider | undefined, actionRunner: IActionRunner, keybindings: ((action: IAction) => ResolvedKeybinding) | undefined, clazz: string | undefined, anchorAlignmentProvider?: () => AnchorAlignment, menuAsChild?: boolean);
constructor(action: IAction, menuActionsOrProvider: ReadonlyArray<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, 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 | 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 | undefined, keybindings: ((action: IAction) => ResolvedKeybinding | undefined) | undefined, clazz: string | undefined, anchorAlignmentProvider?: () => AnchorAlignment, menuAsChild?: boolean) {
super(null, action);
this.menuActionsOrProvider = menuActionsOrProvider;
this.contextMenuProvider = contextMenuProvider;
this.actionViewItemProvider = actionViewItemProvider;
this.actionRunner = actionRunner;
if (actionRunner) {
this.actionRunner = actionRunner;
}
this.keybindings = keybindings;
this.clazz = clazz;
this.anchorAlignmentProvider = anchorAlignmentProvider;

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;
}

View File

@@ -3,13 +3,12 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./menu';
import * as nls from 'vs/nls';
import * as strings from 'vs/base/common/strings';
import { IActionRunner, IAction, Action } from 'vs/base/common/actions';
import { ActionBar, IActionViewItemProvider, ActionsOrientation, Separator, ActionViewItem, IActionViewItemOptions, BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { ResolvedKeybinding, KeyCode } from 'vs/base/common/keyCodes';
import { addClass, EventType, EventHelper, EventLike, removeTabIndexAndUpdateFocus, isAncestor, hasClass, addDisposableListener, removeClass, append, $, addClasses, removeClasses, clearNode } from 'vs/base/browser/dom';
import { addClass, EventType, EventHelper, EventLike, removeTabIndexAndUpdateFocus, isAncestor, hasClass, addDisposableListener, removeClass, append, $, addClasses, removeClasses, clearNode, createStyleSheet, isInShadowDOM } from 'vs/base/browser/dom';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { RunOnceScheduler } from 'vs/base/common/async';
import { DisposableStore } from 'vs/base/common/lifecycle';
@@ -20,6 +19,7 @@ import { Event } from 'vs/base/common/event';
import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview';
import { isLinux, isMacintosh } from 'vs/base/common/platform';
import { Codicon, registerIcon, stripCodicons } from 'vs/base/common/codicons';
import { formatRule } from 'vs/base/browser/ui/codicons/codiconStyles';
export const MENU_MNEMONIC_REGEX = /\(&([^\s&])\)|(^|[^&])&([^\s&])/;
export const MENU_ESCAPED_MNEMONIC_REGEX = /(&amp;)?(&amp;)([^\s&])/g;
@@ -71,6 +71,8 @@ export class Menu extends ActionBar {
private readonly menuDisposables: DisposableStore;
private scrollableElement: DomScrollableElement;
private menuElement: HTMLElement;
static globalStyleSheet: HTMLStyleElement;
protected styleSheet: HTMLStyleElement | undefined;
constructor(container: HTMLElement, actions: ReadonlyArray<IAction>, options: IMenuOptions = {}) {
addClass(container, 'monaco-menu-container');
@@ -96,6 +98,8 @@ export class Menu extends ActionBar {
this.menuDisposables = this._register(new DisposableStore());
this.initializeStyleSheet(container);
addDisposableListener(menuElement, EventType.KEY_DOWN, (e) => {
const event = new StandardKeyboardEvent(e);
@@ -215,6 +219,20 @@ export class Menu extends ActionBar {
});
}
private initializeStyleSheet(container: HTMLElement): void {
if (isInShadowDOM(container)) {
this.styleSheet = createStyleSheet(container);
this.styleSheet.innerHTML = MENU_WIDGET_CSS;
} else {
if (!Menu.globalStyleSheet) {
Menu.globalStyleSheet = createStyleSheet();
Menu.globalStyleSheet.innerHTML = MENU_WIDGET_CSS;
}
this.styleSheet = Menu.globalStyleSheet;
}
}
style(style: IMenuStyles): void {
const container = this.getContainer();
@@ -877,3 +895,298 @@ export function cleanMnemonic(label: string): string {
return label.replace(regex, mnemonicInText ? '$2$3' : '').trim();
}
let MENU_WIDGET_CSS: string = /* css */`
.monaco-menu {
font-size: 13px;
}
${formatRule(menuSelectionIcon)}
${formatRule(menuSubmenuIcon)}
.monaco-action-bar {
text-align: right;
overflow: hidden;
white-space: nowrap;
}
.monaco-action-bar .actions-container {
display: flex;
margin: 0 auto;
padding: 0;
width: 100%;
justify-content: flex-end;
}
.monaco-action-bar.vertical .actions-container {
display: inline-block;
}
.monaco-action-bar.reverse .actions-container {
flex-direction: row-reverse;
}
.monaco-action-bar .action-item {
cursor: pointer;
display: inline-block;
transition: transform 50ms ease;
position: relative; /* DO NOT REMOVE - this is the key to preventing the ghosting icon bug in Chrome 42 */
}
.monaco-action-bar .action-item.disabled {
cursor: default;
}
.monaco-action-bar.animated .action-item.active {
transform: scale(1.272019649, 1.272019649); /* 1.272019649 = √φ */
}
.monaco-action-bar .action-item .icon,
.monaco-action-bar .action-item .codicon {
display: inline-block;
}
.monaco-action-bar .action-item .codicon {
display: flex;
align-items: center;
}
.monaco-action-bar .action-label {
font-size: 11px;
margin-right: 4px;
}
.monaco-action-bar .action-item.disabled .action-label,
.monaco-action-bar .action-item.disabled .action-label:hover {
opacity: 0.4;
}
/* Vertical actions */
.monaco-action-bar.vertical {
text-align: left;
}
.monaco-action-bar.vertical .action-item {
display: block;
}
.monaco-action-bar.vertical .action-label.separator {
display: block;
border-bottom: 1px solid #bbb;
padding-top: 1px;
margin-left: .8em;
margin-right: .8em;
}
.monaco-action-bar.animated.vertical .action-item.active {
transform: translate(5px, 0);
}
.secondary-actions .monaco-action-bar .action-label {
margin-left: 6px;
}
/* Action Items */
.monaco-action-bar .action-item.select-container {
overflow: hidden; /* somehow the dropdown overflows its container, we prevent it here to not push */
flex: 1;
max-width: 170px;
min-width: 60px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 10px;
}
.monaco-menu .monaco-action-bar.vertical {
margin-left: 0;
overflow: visible;
}
.monaco-menu .monaco-action-bar.vertical .actions-container {
display: block;
}
.monaco-menu .monaco-action-bar.vertical .action-item {
padding: 0;
transform: none;
display: flex;
}
.monaco-menu .monaco-action-bar.vertical .action-item.active {
transform: none;
}
.monaco-menu .monaco-action-bar.vertical .action-menu-item {
flex: 1 1 auto;
display: flex;
height: 2em;
align-items: center;
position: relative;
}
.monaco-menu .monaco-action-bar.vertical .action-label {
flex: 1 1 auto;
text-decoration: none;
padding: 0 1em;
background: none;
font-size: 12px;
line-height: 1;
}
.monaco-menu .monaco-action-bar.vertical .keybinding,
.monaco-menu .monaco-action-bar.vertical .submenu-indicator {
display: inline-block;
flex: 2 1 auto;
padding: 0 1em;
text-align: right;
font-size: 12px;
line-height: 1;
}
.monaco-menu .monaco-action-bar.vertical .submenu-indicator {
height: 100%;
}
.monaco-menu .monaco-action-bar.vertical .submenu-indicator.codicon {
font-size: 16px !important;
display: flex;
align-items: center;
}
.monaco-menu .monaco-action-bar.vertical .submenu-indicator.codicon::before {
margin-left: auto;
margin-right: -20px;
}
.monaco-menu .monaco-action-bar.vertical .action-item.disabled .keybinding,
.monaco-menu .monaco-action-bar.vertical .action-item.disabled .submenu-indicator {
opacity: 0.4;
}
.monaco-menu .monaco-action-bar.vertical .action-label:not(.separator) {
display: inline-block;
box-sizing: border-box;
margin: 0;
}
.monaco-menu .monaco-action-bar.vertical .action-item {
position: static;
overflow: visible;
}
.monaco-menu .monaco-action-bar.vertical .action-item .monaco-submenu {
position: absolute;
}
.monaco-menu .monaco-action-bar.vertical .action-label.separator {
padding: 0.5em 0 0 0;
margin-bottom: 0.5em;
width: 100%;
height: 0px !important;
margin-left: .8em !important;
margin-right: .8em !important;
}
.monaco-menu .monaco-action-bar.vertical .action-label.separator.text {
padding: 0.7em 1em 0.1em 1em;
font-weight: bold;
opacity: 1;
}
.monaco-menu .monaco-action-bar.vertical .action-label:hover {
color: inherit;
}
.monaco-menu .monaco-action-bar.vertical .menu-item-check {
position: absolute;
visibility: hidden;
width: 1em;
height: 100%;
}
.monaco-menu .monaco-action-bar.vertical .action-menu-item.checked .menu-item-check {
visibility: visible;
display: flex;
align-items: center;
justify-content: center;
}
/* Context Menu */
.context-view.monaco-menu-container {
outline: 0;
border: none;
animation: fadeIn 0.083s linear;
}
.context-view.monaco-menu-container :focus,
.context-view.monaco-menu-container .monaco-action-bar.vertical:focus,
.context-view.monaco-menu-container .monaco-action-bar.vertical :focus {
outline: 0;
}
.monaco-menu .monaco-action-bar.vertical .action-item {
border: thin solid transparent; /* prevents jumping behaviour on hover or focus */
}
/* High Contrast Theming */
.hc-black .context-view.monaco-menu-container {
box-shadow: none;
}
.hc-black .monaco-menu .monaco-action-bar.vertical .action-item.focused {
background: none;
}
/* Vertical Action Bar Styles */
.monaco-menu .monaco-action-bar.vertical {
padding: .5em 0;
}
.monaco-menu .monaco-action-bar.vertical .action-menu-item {
height: 1.8em;
}
.monaco-menu .monaco-action-bar.vertical .action-label:not(.separator),
.monaco-menu .monaco-action-bar.vertical .keybinding {
font-size: inherit;
padding: 0 2em;
}
.monaco-menu .monaco-action-bar.vertical .menu-item-check {
font-size: inherit;
width: 2em;
}
.monaco-menu .monaco-action-bar.vertical .action-label.separator {
font-size: inherit;
padding: 0.2em 0 0 0;
margin-bottom: 0.2em;
}
linux .monaco-menu .monaco-action-bar.vertical .action-label.separator {
margin-left: 0;
margin-right: 0;
}
.monaco-menu .monaco-action-bar.vertical .submenu-indicator {
font-size: 60%;
padding: 0 1.8em;
}
:host-context(.linux) .monaco-menu .monaco-action-bar.vertical .submenu-indicator {
height: 100%;
mask-size: 10px 10px;
-webkit-mask-size: 10px 10px;
}
.monaco-menu .action-item {
cursor: default;
}
`;

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;
}

View File

@@ -3,6 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./menubar';
import * as browser from 'vs/base/browser/browser';
import * as DOM from 'vs/base/browser/dom';
import * as strings from 'vs/base/common/strings';

View File

@@ -3,8 +3,9 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { URI } from 'vs/base/common/uri';
import { VSBuffer } from 'vs/base/common/buffer';
import { regExpFlags } from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
export function stringify(obj: any): string {
return JSON.stringify(obj, replacer);
@@ -44,10 +45,23 @@ export function revive(obj: any, depth = 0): any {
case 2: return new RegExp(obj.source, obj.flags);
}
// walk object (or array)
for (let key in obj) {
if (Object.hasOwnProperty.call(obj, key)) {
obj[key] = revive(obj[key], depth + 1);
if (
obj instanceof VSBuffer
|| obj instanceof Uint8Array
) {
return obj;
}
if (Array.isArray(obj)) {
for (let i = 0; i < obj.length; ++i) {
obj[i] = revive(obj[i], depth + 1);
}
} else {
// walk object
for (const key in obj) {
if (Object.hasOwnProperty.call(obj, key)) {
obj[key] = revive(obj[key], depth + 1);
}
}
}
}

View File

@@ -16,6 +16,7 @@ export interface ISocket extends IDisposable {
onEnd(listener: () => void): IDisposable;
write(buffer: VSBuffer): void;
end(): void;
drain(): Promise<void>;
}
let emptyBuffer: VSBuffer | null = null;
@@ -277,6 +278,11 @@ class ProtocolWriter {
this._isDisposed = true;
}
public drain(): Promise<void> {
this.flush();
return this._socket.drain();
}
public flush(): void {
// flush
this._writeNow();
@@ -372,6 +378,10 @@ export class Protocol extends Disposable implements IMessagePassingProtocol {
this._register(this._socket.onClose(() => this._onClose.fire()));
}
drain(): Promise<void> {
return this._socketWriter.drain();
}
getSocket(): ISocket {
return this._socket;
}
@@ -619,6 +629,10 @@ export class PersistentProtocol implements IMessagePassingProtocol {
this._socketDisposables = dispose(this._socketDisposables);
}
drain(): Promise<void> {
return this._socketWriter.drain();
}
sendDisconnect(): void {
const msg = new ProtocolMessage(ProtocolMessageType.Disconnect, 0, 0, getEmptyBuffer());
this._socketWriter.write(msg);

View File

@@ -70,6 +70,10 @@ interface IHandler {
export interface IMessagePassingProtocol {
send(buffer: VSBuffer): void;
onMessage: Event<VSBuffer>;
/**
* Wait for the write buffer (if applicable) to become empty.
*/
drain?(): Promise<void>;
}
enum State {
@@ -482,10 +486,7 @@ export class ChannelClient implements IChannelClient, IDisposable {
return e(errors.canceled());
}
let uninitializedPromise: CancelablePromise<void> | null = createCancelablePromise(_ => this.whenInitialized());
uninitializedPromise.then(() => {
uninitializedPromise = null;
const doRequest = () => {
const handler: IHandler = response => {
switch (response.type) {
case ResponseType.PromiseSuccess:
@@ -510,7 +511,18 @@ export class ChannelClient implements IChannelClient, IDisposable {
this.handlers.set(id, handler);
this.sendRequest(request);
});
};
let uninitializedPromise: CancelablePromise<void> | null = null;
if (this.state === State.Idle) {
doRequest();
} else {
uninitializedPromise = createCancelablePromise(_ => this.whenInitialized());
uninitializedPromise.then(() => {
uninitializedPromise = null;
doRequest();
});
}
const cancel = () => {
if (uninitializedPromise) {

View File

@@ -12,6 +12,7 @@ import { generateUuid } from 'vs/base/common/uuid';
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { VSBuffer } from 'vs/base/common/buffer';
import { ISocket, Protocol, Client, ChunkStream } from 'vs/base/parts/ipc/common/ipc.net';
import { onUnexpectedError } from 'vs/base/common/errors';
export class NodeSocket implements ISocket {
public readonly socket: Socket;
@@ -57,12 +58,47 @@ export class NodeSocket implements ISocket {
// > https://nodejs.org/api/stream.html#stream_writable_write_chunk_encoding_callback
// > However, the false return value is only advisory and the writable stream will unconditionally
// > accept and buffer chunk even if it has not been allowed to drain.
this.socket.write(<Buffer>buffer.buffer);
try {
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 {
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 {
@@ -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 {

View File

@@ -90,6 +90,7 @@ export class CodeActionMenu extends Disposable {
const resolver = this._keybindingResolver.getResolver();
this._contextMenuService.showContextMenu({
domForShadowRoot: this._editor.getDomNode()!,
getAnchor: () => anchor,
getActions: () => menuActions,
onHide: () => {

View File

@@ -205,6 +205,8 @@ export class ContextMenuController implements IEditorContribution {
// Show menu
this._contextMenuIsBeingShownCount++;
this._contextMenuService.showContextMenu({
domForShadowRoot: this._editor.getDomNode(),
getAnchor: () => anchor!,
getActions: () => actions,

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.onKeyUp((e: IKeyboardEvent) => this.onEditorKeyUp(e)));
this._register(this._editor.onDidBlurEditorWidget(() => this.onEditorBlur()));
this._register(this._editor.onDidBlurEditorText(() => this.onEditorBlur()));
this._dndDecorationIds = [];
this._mouseDown = false;
this._modifierPressed = false;

View File

@@ -6,7 +6,7 @@
/* Find widget */
.monaco-editor .find-widget {
position: absolute;
z-index: 10;
z-index: 20;
height: 33px;
overflow: hidden;
line-height: 19px;

View File

@@ -50,7 +50,7 @@ export class ContextMenuHandler {
let menu: Menu | undefined;
const anchor = delegate.getAnchor();
let shadowRootElement = isHTMLElement(delegate.domForShadowRoot) ? delegate.domForShadowRoot : undefined;
this.contextViewService.showContextView({
getAnchor: () => delegate.getAnchor(),
canRelayout: false,
@@ -133,7 +133,7 @@ export class ContextMenuHandler {
this.focusToReturn.focus();
}
}
}, !!delegate.anchorAsContainer && isHTMLElement(anchor) ? anchor : undefined);
}, shadowRootElement, !!shadowRootElement);
}
private onActionRun(e: IRunEvent): void {

View File

@@ -15,7 +15,7 @@ export interface IContextViewService extends IContextViewProvider {
readonly _serviceBrand: undefined;
showContextView(delegate: IContextViewDelegate, container?: HTMLElement): IDisposable;
showContextView(delegate: IContextViewDelegate, container?: HTMLElement, shadowRoot?: boolean): IDisposable;
hideContextView(data?: any): void;
layout(): void;
anchorAlignment?: AnchorAlignment;

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { IContextViewService, IContextViewDelegate } from './contextView';
import { ContextView } from 'vs/base/browser/ui/contextview/contextview';
import { ContextView, ContextViewDOMPosition } from 'vs/base/browser/ui/contextview/contextview';
import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
@@ -21,7 +21,7 @@ export class ContextViewService extends Disposable implements IContextViewServic
super();
this.container = layoutService.container;
this.contextView = this._register(new ContextView(this.container, false));
this.contextView = this._register(new ContextView(this.container, ContextViewDOMPosition.ABSOLUTE));
this.layout();
this._register(layoutService.onLayout(() => this.layout()));
@@ -29,20 +29,20 @@ export class ContextViewService extends Disposable implements IContextViewServic
// ContextView
setContainer(container: HTMLElement, useFixedPosition?: boolean): void {
this.contextView.setContainer(container, !!useFixedPosition);
setContainer(container: HTMLElement, domPosition?: ContextViewDOMPosition): void {
this.contextView.setContainer(container, domPosition || ContextViewDOMPosition.ABSOLUTE);
}
showContextView(delegate: IContextViewDelegate, container?: HTMLElement): IDisposable {
showContextView(delegate: IContextViewDelegate, container?: HTMLElement, shadowRoot?: boolean): IDisposable {
if (container) {
if (container !== this.container) {
this.container = container;
this.setContainer(container, true);
this.setContainer(container, shadowRoot ? ContextViewDOMPosition.FIXED_SHADOW : ContextViewDOMPosition.FIXED);
}
} else {
if (this.container !== this.layoutService.container) {
this.container = this.layoutService.container;
this.setContainer(this.container, false);
this.setContainer(this.container, ContextViewDOMPosition.ABSOLUTE);
}
}

View File

@@ -149,8 +149,25 @@ export interface IExtensionIdentifier {
uuid?: string;
}
export const EXTENSION_CATEGORIES = ['Programming Languages', 'Snippets', 'Linters', 'Themes', 'Debuggers', 'Other', 'Keymaps', 'Formatters', 'Extension Packs',
'SCM Providers', 'Azure', 'Language Packs', 'Data Science', 'Machine Learning', 'Visualization', 'Testing', 'Notebooks'];
export const EXTENSION_CATEGORIES = [
'Azure',
'Data Science',
'Debuggers',
'Extension Packs',
'Formatters',
'Keymaps',
'Language Packs',
'Linters',
'Machine Learning',
'Notebooks',
'Programming Languages',
'SCM Providers',
'Snippets',
'Themes',
'Testing',
'Visualization',
'Other',
];
export interface IExtensionManifest {
readonly name: string;

View File

@@ -194,6 +194,9 @@ class BrowserSocket implements ISocket {
this.socket.close();
}
public drain(): Promise<void> {
return Promise.resolve();
}
}

View File

@@ -82,10 +82,19 @@ class RemovedResources {
let messages: string[] = [];
if (externalRemoval.length > 0) {
messages.push(nls.localize('externalRemoval', "The following files have been closed and modified on disk: {0}.", externalRemoval.join(', ')));
messages.push(
nls.localize(
{ key: 'externalRemoval', comment: ['{0} is a list of filenames'] },
"The following files have been closed and modified on disk: {0}.", externalRemoval.join(', ')
)
);
}
if (noParallelUniverses.length > 0) {
messages.push(nls.localize('noParallelUniverses', "The following files have been modified in an incompatible way: {0}.", noParallelUniverses.join(', ')));
messages.push(
nls.localize(
{ key: 'noParallelUniverses', comment: ['{0} is a list of filenames'] },
"The following files have been modified in an incompatible way: {0}.", noParallelUniverses.join(', ')
));
}
return messages.join('\n');
}
@@ -771,10 +780,26 @@ export class UndoRedoService implements IUndoRedoService {
private _checkWorkspaceUndo(strResource: string, element: WorkspaceStackElement, editStackSnapshot: EditStackSnapshot, checkInvalidatedResources: boolean): WorkspaceVerificationError | null {
if (element.removedResources) {
return this._tryToSplitAndUndo(strResource, element, element.removedResources, nls.localize('cannotWorkspaceUndo', "Could not undo '{0}' across all files. {1}", element.label, element.removedResources.createMessage()));
return this._tryToSplitAndUndo(
strResource,
element,
element.removedResources,
nls.localize(
{ key: 'cannotWorkspaceUndo', comment: ['{0} is a label for an operation. {1} is another message.'] },
"Could not undo '{0}' across all files. {1}", element.label, element.removedResources.createMessage()
)
);
}
if (checkInvalidatedResources && element.invalidatedResources) {
return this._tryToSplitAndUndo(strResource, element, element.invalidatedResources, nls.localize('cannotWorkspaceUndo', "Could not undo '{0}' across all files. {1}", element.label, element.invalidatedResources.createMessage()));
return this._tryToSplitAndUndo(
strResource,
element,
element.invalidatedResources,
nls.localize(
{ key: 'cannotWorkspaceUndo', comment: ['{0} is a label for an operation. {1} is another message.'] },
"Could not undo '{0}' across all files. {1}", element.label, element.invalidatedResources.createMessage()
)
);
}
// this must be the last past element in all the impacted resources!
@@ -785,7 +810,15 @@ export class UndoRedoService implements IUndoRedoService {
}
}
if (cannotUndoDueToResources.length > 0) {
return this._tryToSplitAndUndo(strResource, element, null, nls.localize('cannotWorkspaceUndoDueToChanges', "Could not undo '{0}' across all files because changes were made to {1}", element.label, cannotUndoDueToResources.join(', ')));
return this._tryToSplitAndUndo(
strResource,
element,
null,
nls.localize(
{ key: 'cannotWorkspaceUndoDueToChanges', comment: ['{0} is a label for an operation. {1} is a list of filenames.'] },
"Could not undo '{0}' across all files because changes were made to {1}", element.label, cannotUndoDueToResources.join(', ')
)
);
}
const cannotLockDueToResources: string[] = [];
@@ -795,12 +828,28 @@ export class UndoRedoService implements IUndoRedoService {
}
}
if (cannotLockDueToResources.length > 0) {
return this._tryToSplitAndUndo(strResource, element, null, nls.localize('cannotWorkspaceUndoDueToInProgressUndoRedo', "Could not undo '{0}' across all files because there is already an undo or redo operation running on {1}", element.label, cannotLockDueToResources.join(', ')));
return this._tryToSplitAndUndo(
strResource,
element,
null,
nls.localize(
{ key: 'cannotWorkspaceUndoDueToInProgressUndoRedo', comment: ['{0} is a label for an operation. {1} is a list of filenames.'] },
"Could not undo '{0}' across all files because there is already an undo or redo operation running on {1}", element.label, cannotLockDueToResources.join(', ')
)
);
}
// check if new stack elements were added in the meantime...
if (!editStackSnapshot.isValid()) {
return this._tryToSplitAndUndo(strResource, element, null, nls.localize('cannotWorkspaceUndoDueToInMeantimeUndoRedo', "Could not undo '{0}' across all files because an undo or redo operation occurred in the meantime", element.label));
return this._tryToSplitAndUndo(
strResource,
element,
null,
nls.localize(
{ key: 'cannotWorkspaceUndoDueToInMeantimeUndoRedo', comment: ['{0} is a label for an operation. {1} is a list of filenames.'] },
"Could not undo '{0}' across all files because an undo or redo operation occurred in the meantime", element.label
)
);
}
return null;
@@ -881,7 +930,10 @@ export class UndoRedoService implements IUndoRedoService {
return;
}
if (editStack.locked) {
const message = nls.localize('cannotResourceUndoDueToInProgressUndoRedo', "Could not undo '{0}' because there is already an undo or redo operation running.", element.label);
const message = nls.localize(
{ key: 'cannotResourceUndoDueToInProgressUndoRedo', comment: ['{0} is a label for an operation.'] },
"Could not undo '{0}' because there is already an undo or redo operation running.", element.label
);
this._notificationService.info(message);
return;
}
@@ -942,10 +994,26 @@ export class UndoRedoService implements IUndoRedoService {
private _checkWorkspaceRedo(strResource: string, element: WorkspaceStackElement, editStackSnapshot: EditStackSnapshot, checkInvalidatedResources: boolean): WorkspaceVerificationError | null {
if (element.removedResources) {
return this._tryToSplitAndRedo(strResource, element, element.removedResources, nls.localize('cannotWorkspaceRedo', "Could not redo '{0}' across all files. {1}", element.label, element.removedResources.createMessage()));
return this._tryToSplitAndRedo(
strResource,
element,
element.removedResources,
nls.localize(
{ key: 'cannotWorkspaceRedo', comment: ['{0} is a label for an operation. {1} is another message.'] },
"Could not redo '{0}' across all files. {1}", element.label, element.removedResources.createMessage()
)
);
}
if (checkInvalidatedResources && element.invalidatedResources) {
return this._tryToSplitAndRedo(strResource, element, element.invalidatedResources, nls.localize('cannotWorkspaceRedo', "Could not redo '{0}' across all files. {1}", element.label, element.invalidatedResources.createMessage()));
return this._tryToSplitAndRedo(
strResource,
element,
element.invalidatedResources,
nls.localize(
{ key: 'cannotWorkspaceRedo', comment: ['{0} is a label for an operation. {1} is another message.'] },
"Could not redo '{0}' across all files. {1}", element.label, element.invalidatedResources.createMessage()
)
);
}
// this must be the last future element in all the impacted resources!
@@ -956,7 +1024,15 @@ export class UndoRedoService implements IUndoRedoService {
}
}
if (cannotRedoDueToResources.length > 0) {
return this._tryToSplitAndRedo(strResource, element, null, nls.localize('cannotWorkspaceRedoDueToChanges', "Could not redo '{0}' across all files because changes were made to {1}", element.label, cannotRedoDueToResources.join(', ')));
return this._tryToSplitAndRedo(
strResource,
element,
null,
nls.localize(
{ key: 'cannotWorkspaceRedoDueToChanges', comment: ['{0} is a label for an operation. {1} is a list of filenames.'] },
"Could not redo '{0}' across all files because changes were made to {1}", element.label, cannotRedoDueToResources.join(', ')
)
);
}
const cannotLockDueToResources: string[] = [];
@@ -966,12 +1042,28 @@ export class UndoRedoService implements IUndoRedoService {
}
}
if (cannotLockDueToResources.length > 0) {
return this._tryToSplitAndRedo(strResource, element, null, nls.localize('cannotWorkspaceRedoDueToInProgressUndoRedo', "Could not redo '{0}' across all files because there is already an undo or redo operation running on {1}", element.label, cannotLockDueToResources.join(', ')));
return this._tryToSplitAndRedo(
strResource,
element,
null,
nls.localize(
{ key: 'cannotWorkspaceRedoDueToInProgressUndoRedo', comment: ['{0} is a label for an operation. {1} is a list of filenames.'] },
"Could not redo '{0}' across all files because there is already an undo or redo operation running on {1}", element.label, cannotLockDueToResources.join(', ')
)
);
}
// check if new stack elements were added in the meantime...
if (!editStackSnapshot.isValid()) {
return this._tryToSplitAndRedo(strResource, element, null, nls.localize('cannotWorkspaceRedoDueToInMeantimeUndoRedo', "Could not redo '{0}' across all files because an undo or redo operation occurred in the meantime", element.label));
return this._tryToSplitAndRedo(
strResource,
element,
null,
nls.localize(
{ key: 'cannotWorkspaceRedoDueToInMeantimeUndoRedo', comment: ['{0} is a label for an operation. {1} is a list of filenames.'] },
"Could not redo '{0}' across all files because an undo or redo operation occurred in the meantime", element.label
)
);
}
return null;
@@ -1015,7 +1107,10 @@ export class UndoRedoService implements IUndoRedoService {
return;
}
if (editStack.locked) {
const message = nls.localize('cannotResourceRedoDueToInProgressUndoRedo', "Could not redo '{0}' because there is already an undo or redo operation running.", element.label);
const message = nls.localize(
{ key: 'cannotResourceRedoDueToInProgressUndoRedo', comment: ['{0} is a label for an operation.'] },
"Could not redo '{0}' because there is already an undo or redo operation running.", element.label
);
this._notificationService.info(message);
return;
}

View File

@@ -32,6 +32,7 @@ type AutoSyncErrorClassification = {
const enablementKey = 'sync.enable';
const disableMachineEventuallyKey = 'sync.disableMachineEventually';
const sessionIdKey = 'sync.sessionId';
const storeUrlKey = 'sync.storeUrl';
export class UserDataAutoSyncEnablementService extends Disposable {
@@ -97,15 +98,20 @@ export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService i
this.syncTriggerDelayer = this._register(new Delayer<void>(0));
if (userDataSyncStoreService.userDataSyncStore) {
storageService.store(storeUrlKey, userDataSyncStoreService.userDataSyncStore.url.toString(), StorageScope.GLOBAL);
if (this.isEnabled()) {
this.logService.info('Auto Sync is enabled.');
} else {
this.logService.info('Auto Sync is disabled.');
}
this.updateAutoSync();
if (this.hasToDisableMachineEventually()) {
this.disableMachineEventually();
}
this._register(userDataSyncAccountService.onDidChangeAccount(() => this.updateAutoSync()));
this._register(userDataSyncStoreService.onDidChangeDonotMakeRequestsUntil(() => this.updateAutoSync()));
this._register(Event.debounce<string, string[]>(userDataSyncService.onDidChangeLocal, (last, source) => last ? [...last, source] : [source], 1000)(sources => this.triggerSync(sources, false)));

View File

@@ -868,22 +868,6 @@ declare module 'vscode' {
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 {
/**

View File

@@ -262,14 +262,6 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
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> {
if (sessionId) {
const session = this.debugService.getModel().getSession(sessionId, true);

View File

@@ -269,6 +269,14 @@ export class MainThreadTextEditor {
}
}));
const isValidCodeEditor = () => {
// Due to event timings, it is possible that there is a model change event not yet delivered to us.
// > e.g. a model change event is emitted to a listener which then decides to update editor options
// > In this case the editor configuration change event reaches us first.
// So simply check that the model is still attached to this code editor
return (this._codeEditor && this._codeEditor.getModel() === this._model);
};
const updateProperties = (selectionChangeSource: string | null) => {
// Some editor events get delivered faster than model content changes. This is
// problematic, as this leads to editor properties reaching the extension host
@@ -287,18 +295,30 @@ export class MainThreadTextEditor {
this._codeEditorListeners.add(this._codeEditor.onDidChangeCursorSelection((e) => {
// selection
if (!isValidCodeEditor()) {
return;
}
updateProperties(e.source);
}));
this._codeEditorListeners.add(this._codeEditor.onDidChangeConfiguration(() => {
this._codeEditorListeners.add(this._codeEditor.onDidChangeConfiguration((e) => {
// options
if (!isValidCodeEditor()) {
return;
}
updateProperties(null);
}));
this._codeEditorListeners.add(this._codeEditor.onDidLayoutChange(() => {
// visibleRanges
if (!isValidCodeEditor()) {
return;
}
updateProperties(null);
}));
this._codeEditorListeners.add(this._codeEditor.onDidScrollChange(() => {
// visibleRanges
if (!isValidCodeEditor()) {
return;
}
updateProperties(null);
}));
this._updatePropertiesNow(null);

View File

@@ -128,7 +128,7 @@ export class MainThreadExtensionService implements MainThreadExtensionServiceSha
}
}
$onExtensionHostExit(code: number): void {
async $onExtensionHostExit(code: number): Promise<void> {
this._extensionService._onExtensionHostExit(code);
}
}

View File

@@ -799,7 +799,7 @@ export interface MainThreadExtensionServiceShape extends IDisposable {
$onDidActivateExtension(extensionId: ExtensionIdentifier, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationReason: ExtensionActivationReason): void;
$onExtensionActivationError(extensionId: ExtensionIdentifier, error: ExtensionActivationError): Promise<void>;
$onExtensionRuntimeError(extensionId: ExtensionIdentifier, error: SerializedError): void;
$onExtensionHostExit(code: number): void;
$onExtensionHostExit(code: number): Promise<void>;
}
export interface SCMProviderFeatures {
@@ -882,7 +882,6 @@ export interface MainThreadDebugServiceShape extends IDisposable {
$stopDebugging(sessionId: DebugSessionUUID | undefined): Promise<void>;
$setDebugSessionName(id: DebugSessionUUID, name: string): void;
$customDebugAdapterRequest(id: DebugSessionUUID, command: string, args: any): Promise<any>;
$terminateDebugSession(id: DebugSessionUUID): Promise<void>;
$appendDebugConsole(value: string): void;
$startBreakpointEvents(): void;
$registerBreakpoints(breakpoints: Array<ISourceMultiBreakpointDto | IFunctionBreakpointDto | IDataBreakpointDto>): Promise<void>;

View File

@@ -957,10 +957,6 @@ export class ExtHostDebugSession implements vscode.DebugSession {
public customRequest(command: string, args: any): Promise<any> {
return this._debugServiceProxy.$customDebugAdapterRequest(this._id, command, args);
}
public terminate(): Promise<void> {
return this._debugServiceProxy.$terminateDebugSession(this._id);
}
}
export class ExtHostDebugConsole implements vscode.DebugConsole {

View File

@@ -557,7 +557,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
}
// after tests have run, we shutdown the host
this._gracefulExit(error || (typeof failures === 'number' && failures > 0) ? 1 /* ERROR */ : 0 /* OK */);
this._testRunnerExit(error || (typeof failures === 'number' && failures > 0) ? 1 /* ERROR */ : 0 /* OK */);
};
const runResult = testRunner!.run(extensionTestsPath, oldTestRunnerCallback);
@@ -567,11 +567,11 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
runResult
.then(() => {
c();
this._gracefulExit(0);
this._testRunnerExit(0);
})
.catch((err: Error) => {
e(err.toString());
this._gracefulExit(1);
this._testRunnerExit(1);
});
}
});
@@ -579,24 +579,20 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
// Otherwise make sure to shutdown anyway even in case of an error
else {
this._gracefulExit(1 /* ERROR */);
this._testRunnerExit(1 /* ERROR */);
}
return Promise.reject(new Error(requireError ? requireError.toString() : nls.localize('extensionTestError', "Path {0} does not point to a valid extension test runner.", extensionTestsPath)));
}
private _gracefulExit(code: number): void {
// to give the PH process a chance to flush any outstanding console
// messages to the main process, we delay the exit() by some time
setTimeout(() => {
// If extension tests are running, give the exit code to the renderer
if (this._initData.remote.isRemote && !!this._initData.environment.extensionTestsLocationURI) {
this._mainThreadExtensionsProxy.$onExtensionHostExit(code);
return;
}
private _testRunnerExit(code: number): void {
// wait at most 5000ms for the renderer to confirm our exit request and for the renderer socket to drain
// (this is to ensure all outstanding messages reach the renderer)
const exitPromise = this._mainThreadExtensionsProxy.$onExtensionHostExit(code);
const drainPromise = this._extHostContext.drain();
Promise.race([Promise.all([exitPromise, drainPromise]), timeout(5000)]).then(() => {
this._hostUtils.exit(code);
}, 500);
});
}
private _startExtensionHost(): Promise<void> {

View File

@@ -18,12 +18,12 @@ export class ExtHostRpcService implements IExtHostRpcService {
readonly getProxy: <T>(identifier: ProxyIdentifier<T>) => T;
readonly set: <T, R extends T> (identifier: ProxyIdentifier<T>, instance: R) => R;
readonly assertRegistered: (identifiers: ProxyIdentifier<any>[]) => void;
readonly drain: () => Promise<void>;
constructor(rpcProtocol: IRPCProtocol) {
this.getProxy = rpcProtocol.getProxy.bind(rpcProtocol);
this.set = rpcProtocol.set.bind(rpcProtocol);
this.assertRegistered = rpcProtocol.assertRegistered.bind(rpcProtocol);
this.drain = rpcProtocol.drain.bind(rpcProtocol);
}
}

View File

@@ -51,7 +51,10 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService {
// fetch JS sources as text and create a new function around it
const source = await response.text();
const initFn = new Function('module', 'exports', 'require', `${source}\n//# sourceURL=${module.toString(true)}`);
// Here we append #vscode-extension to serve as a marker, such that source maps
// can be adjusted for the extra wrapping function.
const sourceURL = `${module.toString(true)}#vscode-extension`;
const initFn = new Function('module', 'exports', 'require', `${source}\n//# sourceURL=${sourceURL}`);
// define commonjs globals: `module`, `exports`, and `require`
const _exports = {};

View File

@@ -33,6 +33,7 @@ import { ContextSubMenu } from 'vs/base/browser/contextmenu';
import { IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService';
import { AuthenticationSession } from 'vs/editor/common/modes';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
export class ViewContainerActivityAction extends ActivityAction {
@@ -98,6 +99,8 @@ export class ViewContainerActivityAction extends ActivityAction {
}
}
export const ACCOUNTS_VISIBILITY_PREFERENCE_KEY = 'workbench.activity.showAccounts';
export class AccountsActionViewItem extends ActivityActionViewItem {
constructor(
action: ActivityAction,
@@ -107,7 +110,8 @@ export class AccountsActionViewItem extends ActivityActionViewItem {
@IMenuService protected menuService: IMenuService,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IAuthenticationService private readonly authenticationService: IAuthenticationService,
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
@IStorageService private readonly storageService: IStorageService
) {
super(action, { draggable: false, colors, icon: true }, themeService);
}
@@ -190,6 +194,15 @@ export class AccountsActionViewItem extends ActivityActionViewItem {
}
});
if (menus.length) {
menus.push(new Separator());
}
menus.push(new Action('hide', nls.localize('hide', "Hide"), undefined, true, _ => {
this.storageService.store(ACCOUNTS_VISIBILITY_PREFERENCE_KEY, false, StorageScope.GLOBAL);
return Promise.resolve();
}));
return menus;
}

View File

@@ -5,11 +5,10 @@
import 'vs/css!./media/activitybarpart';
import * as nls from 'vs/nls';
import { ActionsOrientation, ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { ActionsOrientation, ActionBar, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { GLOBAL_ACTIVITY_ID, IActivity, ACCOUNTS_ACTIIVTY_ID } from 'vs/workbench/common/activity';
import { Part } from 'vs/workbench/browser/part';
import { GlobalActivityActionViewItem, ViewContainerActivityAction, PlaceHolderToggleCompositePinnedAction, PlaceHolderViewContainerActivityAction, HomeAction, HomeActionViewItem } from 'vs/workbench/browser/parts/activitybar/activitybarActions';
import { AccountsActionViewItem } from 'sql/workbench/browser/parts/activitybar/activitybarActions'; // {{ SQL CARBON EDIT }} - use the ADS account management action
import { GlobalActivityActionViewItem, ViewContainerActivityAction, PlaceHolderToggleCompositePinnedAction, PlaceHolderViewContainerActivityAction, HomeAction, HomeActionViewItem, ACCOUNTS_VISIBILITY_PREFERENCE_KEY } from 'vs/workbench/browser/parts/activitybar/activitybarActions';
import { IBadge, NumberBadge } from 'vs/workbench/services/activity/common/activity';
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@@ -43,6 +42,8 @@ import { Event } from 'vs/base/common/event';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
import { AccountsActionViewItem } from 'sql/workbench/browser/parts/activitybar/activitybarActions'; // {{ SQL CARBON EDIT }} - use the ADS account management action
interface IPlaceholderViewContainer {
id: string;
name?: string;
@@ -76,7 +77,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
static readonly PINNED_VIEW_CONTAINERS = 'workbench.activity.pinnedViewlets2';
private static readonly PLACEHOLDER_VIEW_CONTAINERS = 'workbench.activity.placeholderViewlets';
private static readonly HOME_BAR_VISIBILITY_PREFERENCE = 'workbench.activity.showHomeIndicator';
private static readonly ACCOUNTS_ACTION_INDEX = 0;
//#region IView
readonly minimumWidth: number = 48;
@@ -165,6 +166,18 @@ export class ActivitybarPart extends Part implements IActivityBarService {
actions.push(this.instantiationService.createInstance(ToggleMenuBarAction, ToggleMenuBarAction.ID, menuBarVisibility === 'compact' ? nls.localize('hideMenu', "Hide Menu") : nls.localize('showMenu', "Show Menu")));
}
const toggleAccountsVisibilityAction = new Action(
'toggleAccountsVisibility',
nls.localize('accounts', "Accounts"),
undefined,
true,
async () => { this.accountsVisibilityPreference = !this.accountsVisibilityPreference; }
);
toggleAccountsVisibilityAction.checked = !!this.accountsActivityAction;
actions.push(toggleAccountsVisibilityAction);
actions.push(new Separator());
actions.push(new Action(
ToggleActivityBarVisibilityAction.ID,
nls.localize('hideActivitBar', "Hide Activity Bar"),
@@ -588,17 +601,35 @@ export class ActivitybarPart extends Part implements IActivityBarService {
cssClass: Codicon.settingsGear.classNames
});
this.accountsActivityAction = new ActivityAction({
id: 'workbench.actions.accounts',
name: nls.localize('accounts', "Accounts"),
cssClass: Codicon.account.classNames
});
if (this.accountsVisibilityPreference) {
this.accountsActivityAction = new ActivityAction({
id: 'workbench.actions.accounts',
name: nls.localize('accounts', "Accounts"),
cssClass: Codicon.account.classNames
});
this.globalActivityActionBar.push(this.accountsActivityAction);
this.globalActivityActionBar.push(this.accountsActivityAction, { index: ActivitybarPart.ACCOUNTS_ACTION_INDEX });
}
this.globalActivityActionBar.push(this.globalActivityAction);
}
private toggleAccountsActivity() {
if (this.globalActivityActionBar) {
if (this.accountsActivityAction) {
this.globalActivityActionBar.pull(ActivitybarPart.ACCOUNTS_ACTION_INDEX);
this.accountsActivityAction = undefined;
} else {
this.accountsActivityAction = new ActivityAction({
id: 'workbench.actions.accounts',
name: nls.localize('accounts', "Accounts"),
cssClass: Codicon.account.classNames
});
this.globalActivityActionBar.push(this.accountsActivityAction, { index: ActivitybarPart.ACCOUNTS_ACTION_INDEX });
}
}
}
private getCompositeActions(compositeId: string): { activityAction: ViewContainerActivityAction, pinnedAction: ToggleCompositePinnedAction } {
let compositeActions = this.compositeActions.get(compositeId);
if (!compositeActions) {
@@ -828,6 +859,10 @@ export class ActivitybarPart extends Part implements IActivityBarService {
if (e.key === ActivitybarPart.HOME_BAR_VISIBILITY_PREFERENCE && e.scope === StorageScope.GLOBAL) {
this.onDidChangeHomeBarVisibility();
}
if (e.key === ACCOUNTS_VISIBILITY_PREFERENCE_KEY && e.scope === StorageScope.GLOBAL) {
this.toggleAccountsActivity();
}
}
private saveCachedViewContainers(): void {
@@ -965,6 +1000,14 @@ export class ActivitybarPart extends Part implements IActivityBarService {
this.storageService.store(ActivitybarPart.HOME_BAR_VISIBILITY_PREFERENCE, value, StorageScope.GLOBAL);
}
private get accountsVisibilityPreference(): boolean {
return this.storageService.getBoolean(ACCOUNTS_VISIBILITY_PREFERENCE_KEY, StorageScope.GLOBAL, true);
}
private set accountsVisibilityPreference(value: boolean) {
this.storageService.store(ACCOUNTS_VISIBILITY_PREFERENCE_KEY, value, StorageScope.GLOBAL);
}
private migrateFromOldCachedViewContainersValue(): void {
const value = this.storageService.get('workbench.activity.pinnedViewlets', StorageScope.GLOBAL);
if (value !== undefined) {

View File

@@ -68,18 +68,18 @@ export abstract class Viewlet extends PaneComposite implements IViewlet {
}
getSecondaryActions(): IAction[] {
const viewSecondaryActions = this.viewPaneContainer.getViewsVisibilityActions();
const viewVisibilityActions = this.viewPaneContainer.getViewsVisibilityActions();
const secondaryActions = this.viewPaneContainer.getSecondaryActions();
if (viewSecondaryActions.length <= 1) {
if (viewVisibilityActions.length <= 1 || viewVisibilityActions.every(({ enabled }) => !enabled)) {
return secondaryActions;
}
if (secondaryActions.length === 0) {
return viewSecondaryActions;
return viewVisibilityActions;
}
return [
new ContextSubMenu(nls.localize('views', "Views"), viewSecondaryActions),
new ContextSubMenu(nls.localize('views', "Views"), viewVisibilityActions),
new Separator(),
...secondaryActions
];

View File

@@ -689,7 +689,7 @@ export class Breakpoint extends BaseBreakpoint implements IBreakpoint {
toJSON(): any {
const result = super.toJSON();
result.uri = this.uri;
result.uri = this._uri;
result.lineNumber = this._lineNumber;
result.column = this._column;
result.adapterData = this.adapterData;

View File

@@ -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 {
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 {
private query: Query;

View File

@@ -11,23 +11,23 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle';
import { Event as EventOf, Emitter } from 'vs/base/common/event';
import { IAction, Action } from 'vs/base/common/actions';
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { Separator, IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { IViewlet } from 'vs/workbench/common/viewlet';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { append, $, addClass, toggleClass, Dimension, hide, show } from 'vs/base/browser/dom';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IExtensionsWorkbenchService, IExtensionsViewPaneContainer, VIEWLET_ID, AutoUpdateConfigurationKey, ShowRecommendationsOnlyOnDemandKey, CloseExtensionDetailsOnViewChangeKey } from '../common/extensions';
import { IExtensionsWorkbenchService, IExtensionsViewPaneContainer, VIEWLET_ID, AutoUpdateConfigurationKey, CloseExtensionDetailsOnViewChangeKey } from '../common/extensions';
import {
ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowRecommendedExtensionsAction, /*ShowPopularExtensionsAction,*/ ShowDisabledExtensionsAction,
ShowOutdatedExtensionsAction, ClearExtensionsInputAction, ChangeSortAction, UpdateAllAction, CheckForUpdatesAction, DisableAllAction, EnableAllAction,
EnableAutoUpdateAction, DisableAutoUpdateAction, ShowBuiltInExtensionsAction, InstallVSIXAction
ShowRecommendedExtensionsAction, /*ShowPopularExtensionsAction,*/
ClearExtensionsInputAction, ChangeSortAction, UpdateAllAction, CheckForUpdatesAction, DisableAllAction, EnableAllAction,
EnableAutoUpdateAction, DisableAutoUpdateAction, ShowBuiltInExtensionsAction, InstallVSIXAction, SearchCategoryAction, /*RecentlyPublishedExtensionsAction, */ShowInstalledExtensionsAction, ShowOutdatedExtensionsAction, ShowDisabledExtensionsAction, ShowEnabledExtensionsAction
} from 'vs/workbench/contrib/extensions/browser/extensionsActions';
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IWorkbenchExtensionEnablementService, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput';
import { ExtensionsListView, EnabledExtensionsView, DisabledExtensionsView, RecommendedExtensionsView, WorkspaceRecommendedExtensionsView, BuiltInExtensionsView, BuiltInThemesExtensionsView, BuiltInBasicsExtensionsView, ServerExtensionsView, DefaultRecommendedExtensionsView } from 'vs/workbench/contrib/extensions/browser/extensionsViews';
import { ExtensionsListView, EnabledExtensionsView, DisabledExtensionsView, RecommendedExtensionsView, WorkspaceRecommendedExtensionsView, BuiltInFeatureExtensionsView, BuiltInThemesExtensionsView, BuiltInProgrammingLanguageExtensionsView, ServerExtensionsView, DefaultRecommendedExtensionsView, OutdatedExtensionsView, InstalledExtensionsView, SearchBuiltInExtensionsView } from 'vs/workbench/contrib/extensions/browser/extensionsViews';
import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import Severity from 'vs/base/common/severity';
@@ -50,9 +50,8 @@ import { SuggestEnabledInput, attachSuggestEnabledInputBoxStyler } from 'vs/work
import { alert } from 'vs/base/browser/ui/aria/aria';
import { createErrorWithActions } from 'vs/base/common/errorsWithActions';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ExtensionType } from 'vs/platform/extensions/common/extensions';
import { ExtensionType, EXTENSION_CATEGORIES } from 'vs/platform/extensions/common/extensions';
import { Registry } from 'vs/platform/registry/common/platform';
import { RemoteNameContext } from 'vs/workbench/browser/contextkeys';
import { ILabelService } from 'vs/platform/label/common/label';
import { MementoObject } from 'vs/workbench/common/memento';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
@@ -60,6 +59,8 @@ import { IPreferencesService } from 'vs/workbench/services/preferences/common/pr
import { DragAndDropObserver } from 'vs/workbench/browser/dnd';
import { URI } from 'vs/base/common/uri';
import { SIDE_BAR_DRAG_AND_DROP_BACKGROUND } from 'vs/workbench/common/theme';
import { ContextSubMenu } from 'vs/base/browser/contextmenu';
import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdown';
const NonEmptyWorkspaceContext = new RawContextKey<boolean>('nonEmptyWorkspace', false);
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 SearchDisabledExtensionsContext = new RawContextKey<boolean>('searchDisabledExtensions', false);
const HasInstalledExtensionsContext = new RawContextKey<boolean>('hasInstalledExtensions', true);
const BuiltInExtensionsContext = new RawContextKey<boolean>('builtInExtensions', false);
const SearchBuiltInExtensionsContext = new RawContextKey<boolean>('searchBuiltInExtensions', 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 {
@@ -103,223 +88,233 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
}
private registerViews(): void {
let viewDescriptors: IViewDescriptor[] = [];
viewDescriptors.push(this.createMarketPlaceExtensionsListViewDescriptor());
viewDescriptors.push(this.createDefaultEnabledExtensionsListViewDescriptor());
viewDescriptors.push(this.createDefaultDisabledExtensionsListViewDescriptor());
// {{SQL CARBON EDIT}}
// viewDescriptors.push(this.createDefaultPopularExtensionsListViewDescriptor());
viewDescriptors.push(this.createEnabledExtensionsListViewDescriptor());
viewDescriptors.push(this.createDisabledExtensionsListViewDescriptor());
viewDescriptors.push(this.createBuiltInExtensionsListViewDescriptor());
viewDescriptors.push(this.createBuiltInBasicsExtensionsListViewDescriptor());
viewDescriptors.push(this.createBuiltInThemesExtensionsListViewDescriptor());
viewDescriptors.push(this.createDefaultRecommendedExtensionsListViewDescriptor());
viewDescriptors.push(this.createOtherRecommendedExtensionsListViewDescriptor());
viewDescriptors.push(this.createWorkspaceRecommendedExtensionsListViewDescriptor());
const viewDescriptors: IViewDescriptor[] = [];
if (this.extensionManagementServerService.localExtensionManagementServer) {
viewDescriptors.push(...this.createExtensionsViewDescriptorsForServer(this.extensionManagementServerService.localExtensionManagementServer));
}
if (this.extensionManagementServerService.remoteExtensionManagementServer) {
viewDescriptors.push(...this.createExtensionsViewDescriptorsForServer(this.extensionManagementServerService.remoteExtensionManagementServer));
}
/* Default views */
viewDescriptors.push(...this.createDefaultExtensionsViewDescriptors());
/* Search views */
viewDescriptors.push(...this.createSearchExtensionsViewDescriptors());
/* Recommendations views */
viewDescriptors.push(...this.createRecommendedExtensionsViewDescriptors());
/* Built-in extensions views */
viewDescriptors.push(...this.createBuiltinExtensionsViewDescriptors());
Registry.as<IViewsRegistry>(Extensions.ViewsRegistry).registerViews(viewDescriptors, this.container);
}
// View used for any kind of searching
private createMarketPlaceExtensionsListViewDescriptor(): IViewDescriptor {
const id = 'extensions.listView';
return {
id,
name: viewIdNameMappings[id],
private createDefaultExtensionsViewDescriptors(): IViewDescriptor[] {
const viewDescriptors: IViewDescriptor[] = [];
/*
* Default popular extensions view
* Separate view for popular extensions required as we need to show popular and recommended sections
* in the default view when there is no search text, and user has no installed extensions.
*/
// viewDescriptors.push({ {{SQL CARBON EDIT}} remove popular
// id: 'workbench.views.extensions.popular',
// name: localize('popularExtensions', "Popular"),
// ctorDescriptor: new SyncDescriptor(ExtensionsListView),
// when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.not('hasInstalledExtensions')),
// weight: 60,
// order: 1,
// });
/*
* Default installed extensions views - Shows all user installed extensions.
*/
const servers: IExtensionManagementServer[] = [];
if (this.extensionManagementServerService.localExtensionManagementServer) {
servers.push(this.extensionManagementServerService.localExtensionManagementServer);
}
if (this.extensionManagementServerService.remoteExtensionManagementServer) {
servers.push(this.extensionManagementServerService.remoteExtensionManagementServer);
}
const getViewName = (viewTitle: string, server: IExtensionManagementServer): string => {
return servers.length > 1 ? `${server.label} - ${viewTitle}` : viewTitle;
};
for (const server of servers) {
const getInstalledViewName = (): string => getViewName(localize('installed', "Installed"), server);
const onDidChangeServerLabel: EventOf<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),
when: ContextKeyExpr.and(ContextKeyExpr.has('searchMarketplaceExtensions')),
weight: 100
};
}
});
// Separate view for enabled extensions required as we need to show enabled, disabled and recommended sections
// in the default view when there is no search text, but user has installed extensions.
private createDefaultEnabledExtensionsListViewDescriptor(): IViewDescriptor {
const id = 'extensions.enabledExtensionList';
return {
id,
name: viewIdNameMappings[id],
ctorDescriptor: new SyncDescriptor(EnabledExtensionsView),
when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), RemoteNameContext.isEqualTo('')),
weight: 40,
canToggleVisibility: true,
order: 1
};
}
// Separate view for disabled extensions required as we need to show enabled, disabled and recommended sections
// in the default view when there is no search text, but user has installed extensions.
private createDefaultDisabledExtensionsListViewDescriptor(): IViewDescriptor {
const id = 'extensions.disabledExtensionList';
return {
id,
name: viewIdNameMappings[id],
ctorDescriptor: new SyncDescriptor(DisabledExtensionsView),
when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), RemoteNameContext.isEqualTo('')),
weight: 10,
canToggleVisibility: true,
order: 3,
collapsed: true
};
}
/* // {{SQL CARBON EDIT}}
// Separate view for popular extensions required as we need to show popular and recommended sections
// in the default view when there is no search text, and user has no installed extensions.
private createDefaultPopularExtensionsListViewDescriptor(): IViewDescriptor {
const id = 'extensions.popularExtensionsList';
return {
id,
name: viewIdNameMappings[id],
ctorDescriptor: new SyncDescriptor(ExtensionsListView),
when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.not('hasInstalledExtensions')),
weight: 60,
order: 1
};
}
*/
private createExtensionsViewDescriptorsForServer(server: IExtensionManagementServer): IViewDescriptor[] {
const getViewName = (viewTitle: string, server: IExtensionManagementServer): string => {
const serverLabel = server.label;
if (viewTitle && this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer) {
return `${serverLabel} - ${viewTitle}`;
}
return viewTitle ? viewTitle : serverLabel;
};
const getInstalledViewName = (): string => getViewName(localize('installed', "Installed"), server);
const getOutdatedViewName = (): string => getViewName(localize('outdated', "Outdated"), server);
const onDidChangeServerLabel: EventOf<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())]),
/*
* View used for searching all installed extensions
*/
viewDescriptors.push({
id: 'workbench.views.extensions.searchInstalled',
name: localize('installed', "Installed"),
ctorDescriptor: new SyncDescriptor(InstalledExtensionsView),
when: ContextKeyExpr.and(ContextKeyExpr.has('searchInstalledExtensions')),
weight: 100
}, {
id: `extensions.${server.id}.outdated`,
get name() { return getOutdatedViewName(); },
ctorDescriptor: new SyncDescriptor(ServerExtensionsView, [server, EventOf.map<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
// When user has no installed extensions, this is shown along with the view for popular extensions
private createDefaultRecommendedExtensionsListViewDescriptor(): IViewDescriptor {
const id = 'extensions.recommendedList';
return {
id,
name: viewIdNameMappings[id],
ctorDescriptor: new SyncDescriptor(DefaultRecommendedExtensionsView),
when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('defaultRecommendedExtensions')),
weight: 40,
order: 2,
canToggleVisibility: true
};
}
// Separate view for recommedations that are not workspace recommendations.
// Shown along with view for workspace recommendations, when using the command that shows recommendations
private createOtherRecommendedExtensionsListViewDescriptor(): IViewDescriptor {
const id = 'extensions.otherrecommendedList';
return {
id,
name: viewIdNameMappings[id],
ctorDescriptor: new SyncDescriptor(RecommendedExtensionsView),
when: ContextKeyExpr.has('recommendedExtensions'),
weight: 50,
order: 2
};
}
// Separate view for workspace recommendations.
// Shown along with view for other recommendations, when using the command that shows recommendations
private createWorkspaceRecommendedExtensionsListViewDescriptor(): IViewDescriptor {
const id = 'extensions.workspaceRecommendedList';
return {
id,
name: viewIdNameMappings[id],
ctorDescriptor: new SyncDescriptor(WorkspaceRecommendedExtensionsView),
when: ContextKeyExpr.and(ContextKeyExpr.has('recommendedExtensions'), ContextKeyExpr.has('nonEmptyWorkspace')),
weight: 50,
order: 1
};
}
private createEnabledExtensionsListViewDescriptor(): IViewDescriptor {
const id = 'extensions.enabledExtensionList2';
return {
id,
name: viewIdNameMappings[id],
/*
* View used for searching enabled extensions
*/
viewDescriptors.push({
id: 'workbench.views.extensions.searchEnabled',
name: localize('enabled', "Enabled"),
ctorDescriptor: new SyncDescriptor(EnabledExtensionsView),
when: ContextKeyExpr.and(ContextKeyExpr.has('searchEnabledExtensions')),
weight: 40,
order: 1
};
}
});
private createDisabledExtensionsListViewDescriptor(): IViewDescriptor {
const id = 'extensions.disabledExtensionList2';
return {
id,
name: viewIdNameMappings[id],
/*
* View used for searching disabled extensions
*/
viewDescriptors.push({
id: 'workbench.views.extensions.searchDisabled',
name: localize('disabled', "Disabled"),
ctorDescriptor: new SyncDescriptor(DisabledExtensionsView),
when: ContextKeyExpr.and(ContextKeyExpr.has('searchDisabledExtensions')),
weight: 10,
order: 3,
collapsed: true
};
});
/*
* View used for searching outdated extensions
*/
viewDescriptors.push({
id: 'workbench.views.extensions.searchOutdated',
name: localize('outdated', "Outdated"),
ctorDescriptor: new SyncDescriptor(OutdatedExtensionsView),
when: ContextKeyExpr.and(ContextKeyExpr.has('searchOutdatedExtensions')),
});
/*
* View used for searching builtin extensions
*/
viewDescriptors.push({
id: 'workbench.views.extensions.searchBuiltin',
name: localize('builtin', "Builtin"),
ctorDescriptor: new SyncDescriptor(SearchBuiltInExtensionsView),
when: ContextKeyExpr.and(ContextKeyExpr.has('searchBuiltInExtensions')),
});
return viewDescriptors;
}
private createBuiltInExtensionsListViewDescriptor(): IViewDescriptor {
const id = 'extensions.builtInExtensionsList';
return {
id,
name: viewIdNameMappings[id],
ctorDescriptor: new SyncDescriptor(BuiltInExtensionsView),
when: ContextKeyExpr.has('searchBuiltInExtensions'),
weight: 100
};
private createRecommendedExtensionsViewDescriptors(): IViewDescriptor[] {
const viewDescriptors: IViewDescriptor[] = [];
viewDescriptors.push({
id: 'workbench.views.extensions.workspaceRecommendations',
name: localize('workspaceRecommendedExtensions', "Workspace Recommendations"),
ctorDescriptor: new SyncDescriptor(WorkspaceRecommendedExtensionsView),
when: ContextKeyExpr.and(ContextKeyExpr.has('recommendedExtensions'), ContextKeyExpr.has('nonEmptyWorkspace')),
order: 1
});
viewDescriptors.push({
id: 'workbench.views.extensions.otherRecommendations',
name: localize('otherRecommendedExtensions', "Other Recommendations"),
ctorDescriptor: new SyncDescriptor(RecommendedExtensionsView),
when: ContextKeyExpr.has('recommendedExtensions'),
order: 2
});
return viewDescriptors;
}
private createBuiltInThemesExtensionsListViewDescriptor(): IViewDescriptor {
const id = 'extensions.builtInThemesExtensionsList';
return {
id,
name: viewIdNameMappings[id],
private createBuiltinExtensionsViewDescriptors(): IViewDescriptor[] {
const viewDescriptors: IViewDescriptor[] = [];
viewDescriptors.push({
id: 'workbench.views.extensions.builtinFeatureExtensions',
name: localize('builtinFeatureExtensions', "Features"),
ctorDescriptor: new SyncDescriptor(BuiltInFeatureExtensionsView),
when: ContextKeyExpr.has('builtInExtensions'),
});
viewDescriptors.push({
id: 'workbench.views.extensions.builtinThemeExtensions',
name: localize('builtInThemesExtensions', "Themes"),
ctorDescriptor: new SyncDescriptor(BuiltInThemesExtensionsView),
when: ContextKeyExpr.has('searchBuiltInExtensions'),
weight: 100
};
}
when: ContextKeyExpr.has('builtInExtensions'),
});
private createBuiltInBasicsExtensionsListViewDescriptor(): IViewDescriptor {
const id = 'extensions.builtInBasicsExtensionsList';
return {
id,
name: viewIdNameMappings[id],
ctorDescriptor: new SyncDescriptor(BuiltInBasicsExtensionsView),
when: ContextKeyExpr.has('searchBuiltInExtensions'),
weight: 100
};
viewDescriptors.push({
id: 'workbench.views.extensions.builtinProgrammingLanguageExtensions',
name: localize('builtinProgrammingLanguageExtensions', "Programming Languages"),
ctorDescriptor: new SyncDescriptor(BuiltInProgrammingLanguageExtensionsView),
when: ContextKeyExpr.has('builtInExtensions'),
});
return viewDescriptors;
}
}
@@ -336,15 +331,13 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE
private searchEnabledExtensionsContextKey: IContextKey<boolean>;
private searchDisabledExtensionsContextKey: IContextKey<boolean>;
private hasInstalledExtensionsContextKey: IContextKey<boolean>;
private builtInExtensionsContextKey: IContextKey<boolean>;
private searchBuiltInExtensionsContextKey: IContextKey<boolean>;
private recommendedExtensionsContextKey: IContextKey<boolean>;
private defaultRecommendedExtensionsContextKey: IContextKey<boolean>;
private searchDelayer: Delayer<void>;
private root: HTMLElement | undefined;
private searchBox: SuggestEnabledInput | undefined;
private primaryActions: IAction[] | undefined;
private secondaryActions: IAction[] | null = null;
private readonly searchViewletState: MementoObject;
constructor(
@@ -364,7 +357,7 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE
@IContextMenuService contextMenuService: IContextMenuService,
@IExtensionService extensionService: IExtensionService,
@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
@IPreferencesService private readonly preferencesService: IPreferencesService
@IPreferencesService private readonly preferencesService: IPreferencesService,
) {
super(VIEWLET_ID, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService, viewDescriptorService);
@@ -377,10 +370,9 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE
this.searchEnabledExtensionsContextKey = SearchEnabledExtensionsContext.bindTo(contextKeyService);
this.searchDisabledExtensionsContextKey = SearchDisabledExtensionsContext.bindTo(contextKeyService);
this.hasInstalledExtensionsContextKey = HasInstalledExtensionsContext.bindTo(contextKeyService);
this.builtInExtensionsContextKey = BuiltInExtensionsContext.bindTo(contextKeyService);
this.searchBuiltInExtensionsContextKey = SearchBuiltInExtensionsContext.bindTo(contextKeyService);
this.recommendedExtensionsContextKey = RecommendedExtensionsContext.bindTo(contextKeyService);
this.defaultRecommendedExtensionsContextKey = DefaultRecommendedExtensionsContext.bindTo(contextKeyService);
this.defaultRecommendedExtensionsContextKey.set(!this.configurationService.getValue<boolean>(ShowRecommendationsOnlyOnDemandKey));
this._register(this.viewletService.onDidViewletOpen(this.onViewletOpen, this));
this.searchViewletState = this.getMemento(StorageScope.WORKSPACE);
@@ -390,12 +382,8 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE
this._register(this.configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration(AutoUpdateConfigurationKey)) {
this.secondaryActions = null;
this.updateTitleArea();
}
if (e.affectedKeys.indexOf(ShowRecommendationsOnlyOnDemandKey) > -1) {
this.defaultRecommendedExtensionsContextKey.set(!this.configurationService.getValue<boolean>(ShowRecommendationsOnlyOnDemandKey));
}
}, this));
}
@@ -509,40 +497,57 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE
}
getActions(): IAction[] {
if (!this.primaryActions) {
this.primaryActions = [
this.instantiationService.createInstance(ClearExtensionsInputAction, ClearExtensionsInputAction.ID, ClearExtensionsInputAction.LABEL, this.onSearchChange, this.searchBox ? this.searchBox.getValue() : '')
];
return [
new Action('workbench.extensions.action.filterExtensions', localize('filterExtensions', "Filter Extensions..."), 'codicon-filter', true),
this.instantiationService.createInstance(ClearExtensionsInputAction, ClearExtensionsInputAction.ID, ClearExtensionsInputAction.LABEL, this.onSearchChange, this.searchBox ? this.searchBox.getValue() : ''),
];
}
getActionViewItem(action: IAction): IActionViewItem | undefined {
if (action.id === 'workbench.extensions.action.filterExtensions') {
return new DropdownMenuActionViewItem(action,
[
// this.instantiationService.createInstance(ShowPopularExtensionsAction, ShowPopularExtensionsAction.ID, localize('most popular filter', "Most Popular")), // {{SQL CARBON EDIT}}
// this.instantiationService.createInstance(RecentlyPublishedExtensionsAction, RecentlyPublishedExtensionsAction.ID, localize('recently published filter', "Recently Published")), // {{SQL CARBON EDIT}}
this.instantiationService.createInstance(ShowRecommendedExtensionsAction, ShowRecommendedExtensionsAction.ID, localize('recomended filter', "Recommended")),
new ContextSubMenu(localize('filter by category', "Category"), EXTENSION_CATEGORIES.map(category => this.instantiationService.createInstance(SearchCategoryAction, `extensions.actions.searchByCategory.${category}`, category, category))),
new Separator(),
this.instantiationService.createInstance(ShowBuiltInExtensionsAction, ShowBuiltInExtensionsAction.ID, localize('builtin filter', "Built-in")),
this.instantiationService.createInstance(ShowInstalledExtensionsAction, ShowInstalledExtensionsAction.ID, localize('installed filter', "Installed")),
this.instantiationService.createInstance(ShowEnabledExtensionsAction, ShowEnabledExtensionsAction.ID, localize('enabled filter', "Enabled")),
this.instantiationService.createInstance(ShowDisabledExtensionsAction, ShowDisabledExtensionsAction.ID, localize('disabled filter', "Disabled")),
this.instantiationService.createInstance(ShowOutdatedExtensionsAction, ShowOutdatedExtensionsAction.ID, localize('outdated filter', "Outdated")),
new Separator(),
new ContextSubMenu(localize('sorty by', "Sort By"), [
// this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.install', localize('sort by installs', "Install Count"), this.onSearchChange, 'installs'), // {{SQL CARBON EDIT}}
// this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.rating', localize('sort by rating', "Rating"), this.onSearchChange, 'rating'), // {{SQL CARBON EDIT}}
this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.name', localize('sort by name', "Name"), this.onSearchChange, 'name'),
this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.publishedDate', localize('sort by date', "Published Date"), this.onSearchChange, 'publishedDate'),
]),
],
this.contextMenuService, undefined, undefined, undefined, 'codicon-filter', undefined, true);
}
return this.primaryActions;
return super.getActionViewItem(action);
}
getSecondaryActions(): IAction[] {
if (!this.secondaryActions) {
this.secondaryActions = [
this.instantiationService.createInstance(ShowInstalledExtensionsAction, ShowInstalledExtensionsAction.ID, ShowInstalledExtensionsAction.LABEL),
this.instantiationService.createInstance(ShowOutdatedExtensionsAction, ShowOutdatedExtensionsAction.ID, ShowOutdatedExtensionsAction.LABEL),
this.instantiationService.createInstance(ShowEnabledExtensionsAction, ShowEnabledExtensionsAction.ID, ShowEnabledExtensionsAction.LABEL),
this.instantiationService.createInstance(ShowDisabledExtensionsAction, ShowDisabledExtensionsAction.ID, ShowDisabledExtensionsAction.LABEL),
this.instantiationService.createInstance(ShowBuiltInExtensionsAction, ShowBuiltInExtensionsAction.ID, ShowBuiltInExtensionsAction.LABEL),
this.instantiationService.createInstance(ShowRecommendedExtensionsAction, ShowRecommendedExtensionsAction.ID, ShowRecommendedExtensionsAction.LABEL),
// this.instantiationService.createInstance(ShowPopularExtensionsAction, ShowPopularExtensionsAction.ID, ShowPopularExtensionsAction.LABEL), // {{SQL CARBON EDIT}}
new Separator(),
// {{SQL CARBON EDIT}}
//this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.install', localize('sort by installs', "Sort By: Install Count"), this.onSearchChange, 'installs'),
//this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.rating', localize('sort by rating', "Sort By: Rating"), this.onSearchChange, 'rating'),
this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.name', localize('sort by name', "Sort By: Name"), this.onSearchChange, 'name'),
new Separator(),
this.instantiationService.createInstance(CheckForUpdatesAction, CheckForUpdatesAction.ID, CheckForUpdatesAction.LABEL),
...(this.configurationService.getValue(AutoUpdateConfigurationKey) ? [this.instantiationService.createInstance(DisableAutoUpdateAction, DisableAutoUpdateAction.ID, DisableAutoUpdateAction.LABEL)] : [this.instantiationService.createInstance(UpdateAllAction, UpdateAllAction.ID, UpdateAllAction.LABEL), this.instantiationService.createInstance(EnableAutoUpdateAction, EnableAutoUpdateAction.ID, EnableAutoUpdateAction.LABEL)]),
this.instantiationService.createInstance(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL),
new Separator(),
this.instantiationService.createInstance(DisableAllAction, DisableAllAction.ID, DisableAllAction.LABEL),
this.instantiationService.createInstance(EnableAllAction, EnableAllAction.ID, EnableAllAction.LABEL)
];
}
const actions: IAction[] = [];
return this.secondaryActions;
actions.push(this.instantiationService.createInstance(CheckForUpdatesAction, CheckForUpdatesAction.ID, CheckForUpdatesAction.LABEL));
if (this.configurationService.getValue(AutoUpdateConfigurationKey)) {
actions.push(this.instantiationService.createInstance(DisableAutoUpdateAction, DisableAutoUpdateAction.ID, DisableAutoUpdateAction.LABEL));
} else {
actions.push(this.instantiationService.createInstance(UpdateAllAction, UpdateAllAction.ID, UpdateAllAction.LABEL), this.instantiationService.createInstance(EnableAutoUpdateAction, EnableAutoUpdateAction.ID, EnableAutoUpdateAction.LABEL));
}
actions.push(this.instantiationService.createInstance(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL));
actions.push(new Separator());
actions.push(this.instantiationService.createInstance(DisableAllAction, DisableAllAction.ID, DisableAllAction.LABEL));
actions.push(this.instantiationService.createInstance(EnableAllAction, EnableAllAction.ID, EnableAllAction.LABEL));
return actions;
}
search(value: string, refresh: boolean = false): void {
@@ -580,7 +585,8 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE
this.searchOutdatedExtensionsContextKey.set(ExtensionsListView.isOutdatedExtensionsQuery(value));
this.searchEnabledExtensionsContextKey.set(ExtensionsListView.isEnabledExtensionsQuery(value));
this.searchDisabledExtensionsContextKey.set(ExtensionsListView.isDisabledExtensionsQuery(value));
this.searchBuiltInExtensionsContextKey.set(ExtensionsListView.isBuiltInExtensionsQuery(value));
this.searchBuiltInExtensionsContextKey.set(ExtensionsListView.isSearchBuiltInExtensionsQuery(value));
this.builtInExtensionsContextKey.set(ExtensionsListView.isBuiltInExtensionsQuery(value));
this.recommendedExtensionsContextKey.set(isRecommendedExtensionsQuery);
this.searchMarketplaceExtensionsContextKey.set(!!value && !ExtensionsListView.isLocalExtensionsQuery(value) && !isRecommendedExtensionsQuery);
this.nonEmptyWorkspaceContextKey.set(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY);
@@ -602,19 +608,20 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE
}
private alertSearchResult(count: number, viewId: string): void {
const view = this.viewContainerModel.visibleViewDescriptors.find(view => view.id === viewId);
switch (count) {
case 0:
break;
case 1:
if (viewIdNameMappings[viewId]) {
alert(localize('extensionFoundInSection', "1 extension found in the {0} section.", viewIdNameMappings[viewId]));
if (view) {
alert(localize('extensionFoundInSection', "1 extension found in the {0} section.", view.name));
} else {
alert(localize('extensionFound', "1 extension found."));
}
break;
default:
if (viewIdNameMappings[viewId]) {
alert(localize('extensionsFoundInSection', "{0} extensions found in the {1} section.", count, viewIdNameMappings[viewId]));
if (view) {
alert(localize('extensionsFoundInSection', "{0} extensions found in the {1} section.", count, view.name));
} else {
alert(localize('extensionsFound', "{0} extensions found.", count));
}

View File

@@ -197,6 +197,7 @@ export class ExtensionsListView extends ViewPane {
case 'installs': options = assign(options, { sortBy: SortBy.InstallCount }); break;
case 'rating': options = assign(options, { sortBy: SortBy.WeightedRating }); break;
case 'name': options = assign(options, { sortBy: SortBy.Title }); break;
case 'publishedDate': options = assign(options, { sortBy: SortBy.PublishedDate }); break;
}
const successCallback = (model: IPagedModel<IExtension>) => {
@@ -882,16 +883,21 @@ export class ExtensionsListView extends ViewPane {
this.list = null;
}
static isBuiltInExtensionsQuery(query: string): boolean {
return /^\s*@builtin\s*$/i.test(query);
}
static isLocalExtensionsQuery(query: string): boolean {
return this.isInstalledExtensionsQuery(query)
|| this.isOutdatedExtensionsQuery(query)
|| this.isEnabledExtensionsQuery(query)
|| this.isDisabledExtensionsQuery(query)
|| this.isBuiltInExtensionsQuery(query);
|| this.isBuiltInExtensionsQuery(query)
|| this.isSearchBuiltInExtensionsQuery(query);
}
static isSearchBuiltInExtensionsQuery(query: string): boolean {
return /@builtin\s.+/i.test(query);
}
static isBuiltInExtensionsQuery(query: string): boolean {
return /@builtin$/i.test(query.trim());
}
static isInstalledExtensionsQuery(query: string): boolean {
@@ -977,7 +983,7 @@ export class ServerExtensionsView extends ExtensionsListView {
async show(query: string): Promise<IPagedModel<IExtension>> {
query = query ? query : '@installed';
if (!ExtensionsListView.isLocalExtensionsQuery(query) && !ExtensionsListView.isBuiltInExtensionsQuery(query)) {
if (!ExtensionsListView.isLocalExtensionsQuery(query)) {
query = query += ' @installed';
}
return super.show(query.trim());
@@ -1009,7 +1015,29 @@ export class DisabledExtensionsView extends ExtensionsListView {
}
}
export class BuiltInExtensionsView extends ExtensionsListView {
export class OutdatedExtensionsView extends ExtensionsListView {
async show(query: string): Promise<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>> {
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>> {
return (query && query.trim() !== '@builtin') ? this.showEmptyModel() : super.show('@builtin:basics');
}

View File

@@ -13,7 +13,8 @@ export const CELL_RUN_GUTTER = 28;
export const CODE_CELL_LEFT_MARGIN = 32;
export const EDITOR_TOOLBAR_HEIGHT = 0;
export const BOTTOM_CELL_TOOLBAR_HEIGHT = 28;
export const BOTTOM_CELL_TOOLBAR_HEIGHT = 18;
export const BOTTOM_CELL_TOOLBAR_OFFSET = 12;
export const CELL_STATUSBAR_HEIGHT = 22;
// Margin above editor

View File

@@ -74,7 +74,8 @@ const FOCUS_OUT_OUTPUT_COMMAND_ID = 'notebook.cell.focusOutOutput';
export const NOTEBOOK_ACTIONS_CATEGORY = { value: localize('notebookActions.category', "Notebook"), original: 'Notebook' };
export const CELL_TITLE_GROUP_ID = 'inline';
export const CELL_TITLE_CELL_GROUP_ID = 'inline/cell';
export const CELL_TITLE_OUTPUT_GROUP_ID = 'inline/output';
const EDITOR_WIDGET_ACTION_WEIGHT = KeybindingWeight.EditorContrib; // smaller than Suggest Widget, etc
@@ -407,6 +408,11 @@ registerAction2(class extends NotebookCellAction {
weight: KeybindingWeight.WorkbenchContrib
},
precondition: ContextKeyExpr.and(NOTEBOOK_IS_ACTIVE_EDITOR),
menu: {
id: MenuId.NotebookCellTitle,
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE),
group: '2_edit',
}
});
}
@@ -425,6 +431,11 @@ registerAction2(class extends NotebookCellAction {
primary: KeyCode.KEY_M,
weight: KeybindingWeight.WorkbenchContrib
},
menu: {
id: MenuId.NotebookCellTitle,
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE),
group: '2_edit',
}
});
}
@@ -601,7 +612,7 @@ registerAction2(class extends NotebookCellAction {
NOTEBOOK_CELL_MARKDOWN_EDIT_MODE.toNegated(),
NOTEBOOK_CELL_EDITABLE),
order: CellToolbarOrder.EditCell,
group: CELL_TITLE_GROUP_ID
group: CELL_TITLE_CELL_GROUP_ID
},
icon: { id: 'codicon/pencil' }
});
@@ -625,7 +636,7 @@ registerAction2(class extends NotebookCellAction {
NOTEBOOK_CELL_MARKDOWN_EDIT_MODE,
NOTEBOOK_CELL_EDITABLE),
order: CellToolbarOrder.SaveCell,
group: CELL_TITLE_GROUP_ID
group: CELL_TITLE_CELL_GROUP_ID
},
icon: { id: 'codicon/check' },
keybinding: {
@@ -659,7 +670,7 @@ registerAction2(class extends NotebookCellAction {
id: MenuId.NotebookCellTitle,
order: CellToolbarOrder.DeleteCell,
when: NOTEBOOK_EDITOR_EDITABLE,
group: CELL_TITLE_GROUP_ID
group: CELL_TITLE_CELL_GROUP_ID
},
keybinding: {
primary: KeyCode.Delete,
@@ -758,6 +769,11 @@ registerAction2(class extends NotebookCellAction {
primary: KeyMod.CtrlCmd | KeyCode.KEY_C,
weight: EDITOR_WIDGET_ACTION_WEIGHT
},
menu: {
id: MenuId.NotebookCellTitle,
when: NOTEBOOK_EDITOR_FOCUSED,
group: '1_copy',
}
});
}
@@ -780,6 +796,11 @@ registerAction2(class extends NotebookCellAction {
primary: KeyMod.CtrlCmd | KeyCode.KEY_X,
weight: EDITOR_WIDGET_ACTION_WEIGHT
},
menu: {
id: MenuId.NotebookCellTitle,
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE),
group: '1_copy',
}
});
}
@@ -864,6 +885,11 @@ registerAction2(class extends NotebookAction {
primary: KeyMod.CtrlCmd | KeyCode.KEY_V,
weight: EDITOR_WIDGET_ACTION_WEIGHT
},
menu: {
id: MenuId.NotebookCellTitle,
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE),
group: '1_copy',
}
});
}
@@ -1176,7 +1202,7 @@ registerAction2(class extends NotebookCellAction {
id: MenuId.NotebookCellTitle,
when: ContextKeyExpr.and(NOTEBOOK_CELL_TYPE.isEqualTo('code'), NOTEBOOK_EDITOR_RUNNABLE),
order: CellToolbarOrder.ClearCellOutput,
group: CELL_TITLE_GROUP_ID
group: CELL_TITLE_OUTPUT_GROUP_ID
},
keybinding: {
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey), NOTEBOOK_CELL_HAS_OUTPUTS),
@@ -1339,7 +1365,11 @@ registerAction2(class extends NotebookCellAction {
id: MenuId.NotebookCellTitle,
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE, InputFocusedContext),
order: CellToolbarOrder.SplitCell,
group: CELL_TITLE_GROUP_ID
group: CELL_TITLE_CELL_GROUP_ID,
// alt: {
// id: JOIN_CELL_BELOW_COMMAND_ID,
// title: localize('notebookActions.joinCellBelow', "Join with Next Cell")
// }
},
icon: { id: 'codicon/split-vertical' },
keybinding: {
@@ -1392,6 +1422,11 @@ registerAction2(class extends NotebookCellAction {
when: NOTEBOOK_EDITOR_FOCUSED,
primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.KEY_J,
weight: KeybindingWeight.WorkbenchContrib
},
menu: {
id: MenuId.NotebookCellTitle,
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE),
group: '2_edit',
}
});
}

View File

@@ -166,8 +166,7 @@ export class KernelStatus extends Disposable implements IWorkbenchContribution {
const activeEditor = getActiveNotebookEditor(this._editorService);
if (activeEditor && activeEditor.multipleKernelsAvailable) {
this.showKernelStatus(activeEditor.activeKernel);
if (activeEditor) {
this._editorDisposable.add(activeEditor.onDidChangeKernel(() => {
if (activeEditor.multipleKernelsAvailable) {
this.showKernelStatus(activeEditor.activeKernel);
@@ -175,6 +174,18 @@ export class KernelStatus extends Disposable implements IWorkbenchContribution {
this.kernelInfoElement.clear();
}
}));
this._editorDisposable.add(activeEditor.onDidChangeAvailableKernels(() => {
if (activeEditor.multipleKernelsAvailable) {
this.showKernelStatus(activeEditor.activeKernel);
} else {
this.kernelInfoElement.clear();
}
}));
}
if (activeEditor && activeEditor.multipleKernelsAvailable) {
this.showKernelStatus(activeEditor.activeKernel);
} else {
this.kernelInfoElement.clear();
}

View File

@@ -488,6 +488,9 @@
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container {
position: absolute;
display: flex;
justify-content: center;
z-index: 30; /* over the focus outline on the editor */
width: 100%;
opacity: 0;
transition: opacity 0.2s ease-in-out;
cursor: auto;
@@ -531,19 +534,8 @@
align-items: center;
}
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container .separator {
height: 1px;
flex-grow: 1;
align-self: center;
}
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container .action-item:first-child::after {
content: ' ';
display: block;
height: 1px;
width: 16px;
align-self: center;
margin: 0px 8px;
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container .action-item:first-child {
margin-right: 16px;
}
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container span.codicon {
@@ -699,7 +691,7 @@
.monaco-workbench .notebookOverlay > .cell-list-container .notebook-folding-indicator .codicon {
visibility: visible;
padding: 8px 0 0 10px;
padding: 10px 0 0 10px;
}
/** Theming */
@@ -739,3 +731,11 @@
.monaco-workbench.vs-dark .monaco-workbench .notebookOverlay .cell.markdown table > tbody > tr > td {
border-color: rgba(255, 255, 255, 0.18);
} */
.monaco-action-bar .action-item.verticalSeparator {
width: 1px !important;
background-color: #bbb;
height: 16px !important;
margin: 5px 4px !important;
cursor: none;
}

View File

@@ -180,6 +180,7 @@ export interface INotebookEditor extends IEditor {
isNotebookEditor: boolean;
activeKernel: INotebookKernelInfo | INotebookKernelInfo2 | undefined;
multipleKernelsAvailable: boolean;
readonly onDidChangeAvailableKernels: Event<void>;
readonly onDidChangeKernel: Event<void>;
isDisposed: boolean;
@@ -240,10 +241,16 @@ export interface INotebookEditor extends IEditor {
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
*/
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).
*/

View File

@@ -75,6 +75,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
private _overlayContainer!: HTMLElement;
private _body!: HTMLElement;
private _webview: BackLayerWebView | null = null;
private _webviewResolved: boolean = false;
private _webviewResolvePromise: Promise<BackLayerWebView | null> | null = null;
private _webviewTransparentCover: HTMLElement | null = null;
private _list: INotebookCellList | undefined;
private _dndController: CellDragAndDropController | null = null;
@@ -135,6 +137,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
private _activeKernel: INotebookKernelInfo | INotebookKernelInfo2 | undefined = undefined;
private readonly _onDidChangeKernel = this._register(new Emitter<void>());
readonly onDidChangeKernel: Event<void> = this._onDidChangeKernel.event;
private readonly _onDidChangeAvailableKernels = this._register(new Emitter<void>());
readonly onDidChangeAvailableKernels: Event<void> = this._onDidChangeAvailableKernels.event;
get activeKernel() {
return this._activeKernel;
@@ -150,7 +154,16 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
}
private _currentKernelTokenSource: CancellationTokenSource | undefined = undefined;
multipleKernelsAvailable: boolean = false;
private _multipleKernelsAvailable: boolean = false;
get multipleKernelsAvailable() {
return this._multipleKernelsAvailable;
}
set multipleKernelsAvailable(state: boolean) {
this._multipleKernelsAvailable = state;
this._onDidChangeAvailableKernels.fire();
}
private readonly _onDidChangeActiveEditor = this._register(new Emitter<this>());
readonly onDidChangeActiveEditor: Event<this> = this._onDidChangeActiveEditor.event;
@@ -572,7 +585,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
// @deprecated
if (provider && provider.kernel) {
// it has a builtin kernel, don't automatically choose a kernel
this._loadKernelPreloads(provider.providerExtensionLocation, provider.kernel);
await this._loadKernelPreloads(provider.providerExtensionLocation, provider.kernel);
tokenSource.dispose();
return;
}
@@ -591,7 +604,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
// the provider doesn't have a builtin kernel, choose a kernel
this.activeKernel = availableKernels[0];
if (this.activeKernel) {
this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel);
await this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel);
}
tokenSource.dispose();
@@ -611,7 +624,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
}
if (this.activeKernel) {
this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel);
await this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel);
await (this.activeKernel as INotebookKernelInfo2).resolve(this.viewModel!.uri, this.getId(), tokenSource.token); // {{SQL CARBON EDIT}} strict-null-checks
}
@@ -624,7 +637,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
if (kernelsFromSameExtension.length) {
const preferedKernel = kernelsFromSameExtension.find(kernel => kernel.isPreferred) || kernelsFromSameExtension[0];
this.activeKernel = preferedKernel;
this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel);
await this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel);
await preferedKernel.resolve(this.viewModel!.uri, this.getId(), tokenSource.token);
tokenSource.dispose();
return;
@@ -633,15 +646,16 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
// the provider doesn't have a builtin kernel, choose a kernel
this.activeKernel = kernels[0];
if (this.activeKernel) {
this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel);
await this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel);
await this.activeKernel.resolve(this.viewModel!.uri, this.getId(), tokenSource.token);
}
tokenSource.dispose();
}
private _loadKernelPreloads(extensionLocation: URI, kernel: INotebookKernelInfoDto) {
if (kernel.preloads) {
private async _loadKernelPreloads(extensionLocation: URI, kernel: INotebookKernelInfoDto) {
if (kernel.preloads && kernel.preloads.length) {
await this._resolveWebview();
this._webview?.updateKernelPreloads([extensionLocation], kernel.preloads.map(preload => URI.revive(preload)));
}
}
@@ -656,34 +670,63 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
this._notebookExecuting?.set(notebookMetadata.runState === NotebookRunState.Running);
}
private async _resolveWebview(): Promise<BackLayerWebView | null> {
if (!this.textModel) {
return null;
}
if (this._webviewResolvePromise) {
return this._webviewResolvePromise;
}
if (!this._webview) {
this._webview = this.instantiationService.createInstance(BackLayerWebView, this, this.getId(), this.textModel!.uri);
// attach the webview container to the DOM tree first
this._list?.rowsContainer.insertAdjacentElement('afterbegin', this._webview.element);
}
this._webviewResolvePromise = new Promise(async resolve => {
await this._webview!.createWebview();
this._webview!.webview!.onDidBlur(() => {
this._outputFocus?.set(false);
this.updateEditorFocus();
if (this._overlayContainer.contains(document.activeElement)) {
this._webiewFocused = false;
}
});
this._webview!.webview!.onDidFocus(() => {
this._outputFocus?.set(true);
this.updateEditorFocus();
this._onDidFocusEmitter.fire();
if (this._overlayContainer.contains(document.activeElement)) {
this._webiewFocused = true;
}
});
this._localStore.add(this._webview!.onMessage(({ message, forRenderer }) => {
if (this.viewModel) {
this.notebookService.onDidReceiveMessage(this.viewModel.viewType, this.getId(), forRenderer, message);
}
}));
if (this.viewModel && this.viewModel!.renderers.size) {
this._webview?.updateRendererPreloads(this.viewModel!.renderers);
}
this._webviewResolved = true;
resolve(this._webview!);
});
return this._webviewResolvePromise;
}
private async _createWebview(id: string, resource: URI): Promise<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);
await this._webview.createWebview();
this._webview.webview.onDidBlur(() => {
this._outputFocus?.set(false);
this.updateEditorFocus();
if (this._overlayContainer.contains(document.activeElement)) {
this._webiewFocused = false;
}
});
this._webview.webview.onDidFocus(() => {
this._outputFocus?.set(true);
this.updateEditorFocus();
this._onDidFocusEmitter.fire();
if (this._overlayContainer.contains(document.activeElement)) {
this._webiewFocused = true;
}
});
this._localStore.add(this._webview.onMessage(({ message, forRenderer }) => {
if (this.viewModel) {
this.notebookService.onDidReceiveMessage(this.viewModel.viewType, this.getId(), forRenderer, message);
}
}));
}
private async _attachModel(textModel: NotebookTextModel, viewState: INotebookEditorViewState | undefined) {
@@ -717,10 +760,17 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
}
}
this._webview?.updateRendererPreloads(this.viewModel.renderers);
if (this.viewModel.renderers.size) {
await this._resolveWebview();
this._webview?.updateRendererPreloads(this.viewModel.renderers);
}
this._localStore.add(this._list!.onWillScroll(e => {
this._webview!.updateViewScrollTop(-e.scrollTop, []);
if (!this._webviewResolved) {
return;
}
this._webview?.updateViewScrollTop(-e.scrollTop, []);
this._webviewTransparentCover!.style.top = `${e.scrollTop}px`;
}));
@@ -732,6 +782,11 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
const scrollTop = this._list?.scrollTop || 0;
const scrollHeight = this._list?.scrollHeight || 0;
if (!this._webviewResolved) {
return;
}
this._webview!.element.style.height = `${scrollHeight}px`;
if (this._webview?.insetMapping) {
@@ -1128,6 +1183,15 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
return this._moveCellToIndex(originalIdx, newIdx);
}
async moveCellToIdx(cell: ICellViewModel, index: number): Promise<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> {
if (index === newIdx) {
return null;
@@ -1356,6 +1420,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
return;
}
await this._resolveWebview();
let preloads = this._notebookViewModel!.renderers;
if (!this._webview!.insetMapping.has(output)) {
@@ -1370,7 +1436,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
}
removeInset(output: IProcessedOutput) {
if (!this._webview) {
if (!this._webview || !this._webviewResolved) {
return;
}
@@ -1378,7 +1444,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
}
hideInset(output: IProcessedOutput) {
if (!this._webview) {
if (!this._webview || !this._webviewResolved) {
return;
}
@@ -1390,10 +1456,14 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
}
postMessage(forRendererId: string | undefined, message: any) {
if (!this._webview || !this._webviewResolved) {
return;
}
if (forRendererId === undefined) {
this._webview?.webview.postMessage(message);
this._webview.webview?.postMessage(message);
} else {
this._webview?.postRendererMessage(forRendererId, message);
this._webview.postRendererMessage(forRendererId, message);
}
}
@@ -1600,13 +1670,13 @@ registerThemingParticipant((theme, collector) => {
collector.addRule(`.notebookOverlay .cell-statusbar-container { border-top: solid 1px ${editorBackgroundColor}; }`);
collector.addRule(`.notebookOverlay .monaco-list-row > .monaco-toolbar { background-color: ${editorBackgroundColor}; }`);
collector.addRule(`.notebookOverlay .monaco-list-row.cell-drag-image { background-color: ${editorBackgroundColor}; }`);
collector.addRule(`.notebookOverlay .cell-bottom-toolbar-container .action-item { background-color: ${editorBackgroundColor} }`);
}
const cellToolbarSeperator = theme.getColor(CELL_TOOLBAR_SEPERATOR);
if (cellToolbarSeperator) {
collector.addRule(`.notebookOverlay .cell-bottom-toolbar-container .separator { background-color: ${cellToolbarSeperator} }`);
collector.addRule(`.notebookOverlay .cell-bottom-toolbar-container .action-item:first-child::after { background-color: ${cellToolbarSeperator} }`);
collector.addRule(`.notebookOverlay .monaco-list-row > .monaco-toolbar { border: solid 1px ${cellToolbarSeperator}; }`);
collector.addRule(`.notebookOverlay .cell-bottom-toolbar-container .action-item { border: solid 1px ${cellToolbarSeperator} }`);
}
const focusedCellBackgroundColor = theme.getColor(focusedCellBackground);
@@ -1643,14 +1713,10 @@ registerThemingParticipant((theme, collector) => {
collector.addRule(`.notebookOverlay .monaco-list-row.cell-editor-focus .cell-editor-part:before { outline: solid 1px ${focusedEditorBorderColorColor}; }`);
}
const editorBorderColor = theme.getColor(notebookCellBorder);
if (editorBorderColor) {
collector.addRule(`.notebookOverlay .monaco-list-row .cell-editor-part:before { outline: solid 1px ${editorBorderColor}; }`);
}
const headingBorderColor = theme.getColor(notebookCellBorder);
if (headingBorderColor) {
collector.addRule(`.notebookOverlay .cell.markdown h1 { border-color: ${headingBorderColor}; }`);
const cellBorderColor = theme.getColor(notebookCellBorder);
if (cellBorderColor) {
collector.addRule(`.notebookOverlay .cell.markdown h1 { border-color: ${cellBorderColor}; }`);
collector.addRule(`.notebookOverlay .monaco-list-row .cell-editor-part:before { outline: solid 1px ${cellBorderColor}; }`);
}
const cellStatusSuccessIcon = theme.getColor(cellStatusIconSuccess);
@@ -1701,10 +1767,8 @@ registerThemingParticipant((theme, collector) => {
collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row > div.cell.code { margin-left: ${CODE_CELL_LEFT_MARGIN}px; }`);
collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row { padding-top: ${EDITOR_TOP_MARGIN}px; }`);
collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .markdown-cell-row { padding-bottom: ${CELL_BOTTOM_MARGIN}px; }`);
collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .markdown-cell-row .cell-bottom-toolbar-container { margin-top: ${CELL_BOTTOM_MARGIN}px; }`);
collector.addRule(`.notebookOverlay .output { margin: 0px ${CELL_MARGIN}px 0px ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER}px; }`);
collector.addRule(`.notebookOverlay .output { width: calc(100% - ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER + (CELL_MARGIN * 2)}px); }`);
collector.addRule(`.notebookOverlay .cell-bottom-toolbar-container { width: calc(100% - ${CELL_MARGIN * 2 + CELL_RUN_GUTTER}px); margin: 0px ${CELL_MARGIN * 2}px 0px ${CELL_MARGIN + CELL_RUN_GUTTER}px; }`);
collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row > div.cell.markdown { padding-left: ${CELL_RUN_GUTTER}px; }`);
collector.addRule(`.notebookOverlay .cell .run-button-container { width: ${CELL_RUN_GUTTER}px; }`);

View File

@@ -219,7 +219,7 @@ export interface INotebookWebviewMessage {
let version = 0;
export class BackLayerWebView extends Disposable {
element: HTMLElement;
webview!: WebviewElement;
webview: WebviewElement | undefined = undefined;
insetMapping: Map<IProcessedOutput, ICachedInset> = new Map();
hiddenInsetMapping: Set<IProcessedOutput> = new Set();
reversedInsetMapping: Map<string, IProcessedOutput> = new Map();
@@ -714,7 +714,7 @@ ${loaderJs}
return;
}
this.webview.focus();
this.webview?.focus();
}
focusOutput(cellId: string) {
@@ -722,7 +722,7 @@ ${loaderJs}
return;
}
this.webview.focus();
this.webview?.focus();
setTimeout(() => { // Need this, or focus decoration is not shown. No clue.
this._sendMessageToWebview({
type: 'focus-output',
@@ -814,6 +814,10 @@ ${loaderJs}
}
private _updatePreloads(resources: IPreloadResource[], source: 'renderer' | 'kernel') {
if (!this.webview) {
return;
}
const mixedResourceRoots = [...(this.localResourceRootsCache || []), ...this.rendererRootsCache, ...this.kernelRootsCache];
this.webview.localResourcesRoot = mixedResourceRoots;
@@ -830,7 +834,7 @@ ${loaderJs}
return;
}
this.webview.postMessage(message);
this.webview?.postMessage(message);
}
clearPreloadsCache() {
@@ -839,7 +843,7 @@ ${loaderJs}
dispose() {
this._disposed = true;
this.webview.dispose();
this.webview?.dispose();
super.dispose();
}
}

View File

@@ -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;
}

View File

@@ -3,16 +3,12 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IAction } from 'vs/base/common/actions';
import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
import { IMenu, IMenuService, MenuId } from 'vs/platform/actions/common/actions';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
export class CellMenus {
constructor(
@IMenuService private readonly menuService: IMenuService,
@IContextMenuService private readonly contextMenuService: IContextMenuService
) { }
getCellTitleMenu(contextKeyService: IContextKeyService): IMenu {
@@ -26,11 +22,6 @@ export class CellMenus {
private getMenu(menuId: MenuId, contextKeyService: IContextKeyService): IMenu {
const menu = this.menuService.createMenu(menuId, contextKeyService);
const primary: IAction[] = [];
const secondary: IAction[] = [];
const result = { primary, secondary };
createAndFillInContextMenuActions(menu, { shouldForwardArgs: true }, result, this.contextMenuService, g => /^inline/.test(g));
return menu;
}

View File

@@ -36,17 +36,18 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { BOTTOM_CELL_TOOLBAR_HEIGHT, EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT, EDITOR_TOP_MARGIN, EDITOR_TOP_PADDING, CELL_BOTTOM_MARGIN } from 'vs/workbench/contrib/notebook/browser/constants';
import { CancelCellAction, ChangeCellLanguageAction, ExecuteCellAction, INotebookCellActionContext, CELL_TITLE_GROUP_ID } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions';
import { BaseCellRenderTemplate, CellEditState, CodeCellRenderTemplate, ICellViewModel, INotebookCellList, INotebookEditor, MarkdownCellRenderTemplate, isCodeCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_BOTTOM_MARGIN, EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT, EDITOR_TOP_MARGIN, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants';
import { CancelCellAction, ChangeCellLanguageAction, ExecuteCellAction, INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions';
import { BaseCellRenderTemplate, CellEditState, CodeCellRenderTemplate, ICellViewModel, INotebookCellList, INotebookEditor, isCodeCellRenderTemplate, MarkdownCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys';
import { CellMenus } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellMenus';
import { CodeCell } from 'vs/workbench/contrib/notebook/browser/view/renderers/codeCell';
import { StatefullMarkdownCell } from 'vs/workbench/contrib/notebook/browser/view/renderers/markdownCell';
import { StatefulMarkdownCell } from 'vs/workbench/contrib/notebook/browser/view/renderers/markdownCell';
import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel';
import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel';
import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
import { CellKind, NotebookCellRunState, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys';
import { CellKind, NotebookCellMetadata, NotebookCellRunState } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { VerticalSeparator, createAndFillInActionBarActionsWithVerticalSeparators, VerticalSeparatorViewItem } from './cellActionView';
const $ = DOM.$;
@@ -204,9 +205,6 @@ abstract class AbstractCellRenderer {
}
});
toolbar.getContainer().style.height = `${BOTTOM_CELL_TOOLBAR_HEIGHT}px`;
container.style.height = `${BOTTOM_CELL_TOOLBAR_HEIGHT}px`;
const cellMenu = this.instantiationService.createInstance(CellMenus);
const menu = disposables.add(cellMenu.getCellInsertionMenu(contextKeyService));
@@ -220,25 +218,28 @@ abstract class AbstractCellRenderer {
templateData.betweenCellToolbar.context = context;
const container = templateData.bottomCellContainer;
if (element instanceof CodeCellViewModel) {
const bottomToolbarOffset = element.layoutInfo.bottomToolbarOffset;
container.style.top = `${bottomToolbarOffset}px`;
templateData.elementDisposables.add(element.onDidChangeLayout(() => {
const bottomToolbarOffset = element.layoutInfo.bottomToolbarOffset;
container.style.top = `${bottomToolbarOffset}px`;
templateData.elementDisposables.add(element.onDidChangeLayout(() => {
const bottomToolbarOffset = element.layoutInfo.bottomToolbarOffset;
container.style.top = `${bottomToolbarOffset}px`;
}));
}
}));
}
protected createToolbar(container: HTMLElement): ToolBar {
const toolbar = new ToolBar(container, this.contextMenuService, {
getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id),
actionViewItemProvider: action => {
if (action instanceof MenuItemAction) {
const item = new ContextAwareMenuEntryActionViewItem(action, this.keybindingService, this.notificationService, this.contextMenuService);
return item;
}
if (action.id === VerticalSeparator.ID) {
return new VerticalSeparatorViewItem(undefined, action);
}
return undefined;
}
});
@@ -249,16 +250,11 @@ abstract class AbstractCellRenderer {
private getCellToolbarActions(menu: IMenu): { primary: IAction[], secondary: IAction[] } {
const primary: IAction[] = [];
const secondary: IAction[] = [];
const actions = menu.getActions({ shouldForwardArgs: true });
for (let [id, menuActions] of actions) {
if (id === CELL_TITLE_GROUP_ID) {
primary.push(...menuActions);
} else {
secondary.push(...menuActions);
}
}
const result = { primary, secondary };
return { primary, secondary };
createAndFillInActionBarActionsWithVerticalSeparators(menu, { shouldForwardArgs: true }, result, g => /^inline/.test(g));
return result;
}
protected setupCellToolbarActions(templateData: BaseCellRenderTemplate, disposables: DisposableStore): void {
@@ -351,9 +347,7 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR
const foldingIndicator = DOM.append(focusIndicator, DOM.$('.notebook-folding-indicator'));
const bottomCellContainer = DOM.append(container, $('.cell-bottom-toolbar-container'));
DOM.append(bottomCellContainer, $('.separator'));
const betweenCellToolbar = disposables.add(this.createBetweenCellToolbar(bottomCellContainer, disposables, contextKeyService));
DOM.append(bottomCellContainer, $('.separator'));
const statusBar = this.instantiationService.createInstance(CellEditorStatusBar, editorPart);
const titleMenu = disposables.add(this.cellMenus.getCellTitleMenu(contextKeyService));
@@ -439,7 +433,8 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR
this.setBetweenCellToolbarContext(templateData, element, toolbarContext);
const markdownCell = this.instantiationService.createInstance(StatefullMarkdownCell, this.notebookEditor, element, templateData, this.editorOptions.value, this.renderedEditors);
const scopedInstaService = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, templateData.contextKeyService]));
const markdownCell = scopedInstaService.createInstance(StatefulMarkdownCell, this.notebookEditor, element, templateData, this.editorOptions.value, this.renderedEditors);
elementDisposables.add(this.editorOptions.onDidChange(newValue => markdownCell.updateEditorOptions(newValue)));
elementDisposables.add(markdownCell);
@@ -611,6 +606,18 @@ export class CellDragAndDropController extends Disposable {
private onCellDrop(event: CellDragEvent): void {
const draggedCell = this.currentDraggedCell!;
let draggedCells: ICellViewModel[] = [draggedCell];
if (draggedCell.cellKind === CellKind.Markdown) {
const currCellIndex = this.notebookEditor.viewModel!.getCellIndex(draggedCell);
const nextVisibleCellIndex = this.notebookEditor.viewModel!.getNextVisibleCellIndex(currCellIndex);
if (nextVisibleCellIndex > currCellIndex + 1) {
// folding ;)
draggedCells = this.notebookEditor.viewModel!.viewCells.slice(currCellIndex, nextVisibleCellIndex);
}
}
this.dragCleanup();
const isCopy = (event.browserEvent.ctrlKey && !platform.isMacintosh) || (event.browserEvent.altKey && platform.isMacintosh);
@@ -625,9 +632,9 @@ export class CellDragAndDropController extends Disposable {
}
if (isCopy) {
this.copyCell(draggedCell, event.draggedOverCell, dropDirection);
this.copyCells(draggedCells, event.draggedOverCell, dropDirection);
} else {
this.moveCell(draggedCell, event.draggedOverCell, dropDirection);
this.moveCells(draggedCells, event.draggedOverCell, dropDirection);
}
}
@@ -673,16 +680,37 @@ export class CellDragAndDropController extends Disposable {
}));
}
private async moveCell(draggedCell: ICellViewModel, ontoCell: ICellViewModel, direction: 'above' | 'below') {
await this.notebookEditor.moveCell(draggedCell, ontoCell, direction);
private async moveCells(draggedCells: ICellViewModel[], ontoCell: ICellViewModel, direction: 'above' | 'below') {
const relativeToIndex = this.notebookEditor!.viewModel!.getCellIndex(ontoCell);
const newIdx = direction === 'above' ? relativeToIndex : relativeToIndex + 1;
this.notebookEditor.textModel!.pushStackElement('Move Cells');
for (let i = draggedCells.length - 1; i >= 0; i--) {
await this.notebookEditor.moveCellToIdx(draggedCells[i], newIdx);
}
this.notebookEditor.textModel!.pushStackElement('Move Cells');
}
private copyCell(draggedCell: ICellViewModel, ontoCell: ICellViewModel, direction: 'above' | 'below') {
const editState = draggedCell.editState;
const newCell = this.notebookEditor.insertNotebookCell(ontoCell, draggedCell.cellKind, direction, draggedCell.getText());
if (newCell) {
this.notebookEditor.focusNotebookCell(newCell, editState === CellEditState.Editing ? 'editor' : 'container');
private copyCells(draggedCells: ICellViewModel[], ontoCell: ICellViewModel, direction: 'above' | 'below') {
this.notebookEditor.textModel!.pushStackElement('Copy Cells');
let firstNewCell: ICellViewModel | undefined = undefined;
let firstNewCellState: CellEditState = CellEditState.Preview;
for (let i = 0; i < draggedCells.length; i++) {
const draggedCell = draggedCells[i];
const newCell = this.notebookEditor.insertNotebookCell(ontoCell, draggedCell.cellKind, direction, draggedCell.getText());
if (newCell && !firstNewCell) {
firstNewCell = newCell;
firstNewCellState = draggedCell.editState;
}
}
if (firstNewCell) {
this.notebookEditor.focusNotebookCell(firstNewCell, firstNewCellState === CellEditState.Editing ? 'editor' : 'container');
}
this.notebookEditor.textModel!.pushStackElement('Copy Cells');
}
}
@@ -912,11 +940,8 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende
const focusSinkElement = DOM.append(container, $('.cell-editor-focus-sink'));
focusSinkElement.setAttribute('tabindex', '0');
const bottomCellContainer = DOM.append(container, $('.cell-bottom-toolbar-container'));
DOM.append(bottomCellContainer, $('.separator'));
const betweenCellToolbar = this.createBetweenCellToolbar(bottomCellContainer, disposables, contextKeyService);
DOM.append(bottomCellContainer, $('.separator'));
const focusIndicatorBottom = DOM.append(container, $('.cell-focus-indicator.cell-focus-indicator-bottom'));
const betweenCellToolbar = this.createBetweenCellToolbar(bottomCellContainer, disposables, contextKeyService);
const titleMenu = disposables.add(this.cellMenus.getCellTitleMenu(contextKeyService));

View File

@@ -21,7 +21,7 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/renderers/sizeObserver';
export class StatefullMarkdownCell extends Disposable {
export class StatefulMarkdownCell extends Disposable {
private editor: CodeEditorWidget | null = null;
private markdownContainer: HTMLElement;
@@ -95,7 +95,7 @@ export class StatefullMarkdownCell extends Disposable {
this._register(viewCell.onDidChangeLayout((e) => {
const layoutInfo = this.editor?.getLayoutInfo();
if (e.outerWidth && layoutInfo && layoutInfo.width !== viewCell.layoutInfo.editorWidth) {
if (e.outerWidth && this.viewCell.editState === CellEditState.Editing && layoutInfo && layoutInfo.width !== viewCell.layoutInfo.editorWidth) {
this.onCellEditorWidthChange();
} else if (e.totalHeight || e.outerWidth) {
this.relayoutCell();

View File

@@ -8,7 +8,7 @@ import * as UUID from 'vs/base/common/uuid';
import * as editorCommon from 'vs/editor/common/editorCommon';
import * as model from 'vs/editor/common/model';
import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer';
import { BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_MARGIN, CELL_RUN_GUTTER, CELL_STATUSBAR_HEIGHT, EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT, EDITOR_TOP_MARGIN, EDITOR_TOP_PADDING, CELL_BOTTOM_MARGIN, CODE_CELL_LEFT_MARGIN } from 'vs/workbench/contrib/notebook/browser/constants';
import { BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_MARGIN, CELL_RUN_GUTTER, CELL_STATUSBAR_HEIGHT, EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT, EDITOR_TOP_MARGIN, EDITOR_TOP_PADDING, CELL_BOTTOM_MARGIN, CODE_CELL_LEFT_MARGIN, BOTTOM_CELL_TOOLBAR_OFFSET } from 'vs/workbench/contrib/notebook/browser/constants';
import { CellEditState, CellFindMatch, CodeCellLayoutChangeEvent, CodeCellLayoutInfo, ICellViewModel, NotebookLayoutInfo, CodeCellLayoutState } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { CellKind, NotebookCellOutputsSplice, INotebookSearchOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon';
@@ -122,7 +122,7 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
const indicatorHeight = editorHeight + CELL_STATUSBAR_HEIGHT + outputTotalHeight;
const outputContainerOffset = EDITOR_TOOLBAR_HEIGHT + EDITOR_TOP_MARGIN + editorHeight + CELL_STATUSBAR_HEIGHT;
const bottomToolbarOffset = totalHeight - BOTTOM_CELL_TOOLBAR_HEIGHT;
const bottomToolbarOffset = totalHeight - BOTTOM_CELL_TOOLBAR_HEIGHT - BOTTOM_CELL_TOOLBAR_OFFSET;
const editorWidth = state.outerWidth !== undefined ? this.computeEditorWidth(state.outerWidth) : this._layoutInfo?.editorWidth;
this._layoutInfo = {

View File

@@ -8,7 +8,7 @@ import { Emitter, Event } from 'vs/base/common/event';
import * as UUID from 'vs/base/common/uuid';
import * as editorCommon from 'vs/editor/common/editorCommon';
import * as model from 'vs/editor/common/model';
import { BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_MARGIN, CELL_STATUSBAR_HEIGHT, EDITOR_TOP_MARGIN, CELL_BOTTOM_MARGIN, CODE_CELL_LEFT_MARGIN } from 'vs/workbench/contrib/notebook/browser/constants';
import { BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_MARGIN, CELL_STATUSBAR_HEIGHT, EDITOR_TOP_MARGIN, CELL_BOTTOM_MARGIN, CODE_CELL_LEFT_MARGIN, BOTTOM_CELL_TOOLBAR_OFFSET } from 'vs/workbench/contrib/notebook/browser/constants';
import { CellFindMatch, ICellViewModel, MarkdownCellLayoutChangeEvent, MarkdownCellLayoutInfo, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { MarkdownRenderer } from 'vs/workbench/contrib/notebook/browser/view/renderers/mdRenderer';
import { BaseCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel';
@@ -93,13 +93,14 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie
layoutChange(state: MarkdownCellLayoutChangeEvent) {
// recompute
const editorWidth = state.outerWidth !== undefined ? this.computeEditorWidth(state.outerWidth) : this._layoutInfo.editorWidth;
const totalHeight = state.totalHeight === undefined ? this._layoutInfo.totalHeight : state.totalHeight;
this._layoutInfo = {
fontInfo: state.font || this._layoutInfo.fontInfo,
editorWidth,
editorHeight: this._editorHeight,
bottomToolbarOffset: BOTTOM_CELL_TOOLBAR_HEIGHT,
totalHeight: state.totalHeight === undefined ? this._layoutInfo.totalHeight : state.totalHeight
bottomToolbarOffset: totalHeight - BOTTOM_CELL_TOOLBAR_HEIGHT - BOTTOM_CELL_TOOLBAR_OFFSET,
totalHeight
};
this._onDidChangeLayout.fire(state);
@@ -115,6 +116,7 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie
totalHeight: totalHeight,
editorHeight: this._editorHeight
};
this.layoutChange({});
}
}

View File

@@ -31,7 +31,7 @@ export class InsertCellEdit implements IResourceUndoRedoElement {
) {
}
undo(): void | Promise<void> {
undo(): void {
if (!this.editingDelegate.deleteCell) {
throw new Error('Notebook Delete Cell not implemented for Undo/Redo');
}
@@ -41,7 +41,7 @@ export class InsertCellEdit implements IResourceUndoRedoElement {
this.editingDelegate.emitSelections(this.beforedSelections);
}
}
redo(): void | Promise<void> {
redo(): void {
if (!this.editingDelegate.insertCell) {
throw new Error('Notebook Insert Cell not implemented for Undo/Redo');
}
@@ -70,7 +70,7 @@ export class DeleteCellEdit implements IResourceUndoRedoElement {
// this._rawCell.source = [cell.getText()];
}
undo(): void | Promise<void> {
undo(): void {
if (!this.editingDelegate.insertCell) {
throw new Error('Notebook Insert Cell not implemented for Undo/Redo');
}
@@ -81,7 +81,7 @@ export class DeleteCellEdit implements IResourceUndoRedoElement {
}
}
redo(): void | Promise<void> {
redo(): void {
if (!this.editingDelegate.deleteCell) {
throw new Error('Notebook Delete Cell not implemented for Undo/Redo');
}
@@ -95,7 +95,7 @@ export class DeleteCellEdit implements IResourceUndoRedoElement {
export class MoveCellEdit implements IResourceUndoRedoElement {
type: UndoRedoElementType.Resource = UndoRedoElementType.Resource;
label: string = 'Delete Cell';
label: string = 'Move Cell';
constructor(
public resource: URI,
@@ -107,7 +107,7 @@ export class MoveCellEdit implements IResourceUndoRedoElement {
) {
}
undo(): void | Promise<void> {
undo(): void {
if (!this.editingDelegate.moveCell) {
throw new Error('Notebook Move Cell not implemented for Undo/Redo');
}
@@ -118,7 +118,7 @@ export class MoveCellEdit implements IResourceUndoRedoElement {
}
}
redo(): void | Promise<void> {
redo(): void {
if (!this.editingDelegate.moveCell) {
throw new Error('Notebook Move Cell not implemented for Undo/Redo');
}
@@ -142,7 +142,7 @@ export class SpliceCellsEdit implements IResourceUndoRedoElement {
) {
}
undo(): void | Promise<void> {
undo(): void {
if (!this.editingDelegate.deleteCell || !this.editingDelegate.insertCell) {
throw new Error('Notebook Insert/Delete Cell not implemented for Undo/Redo');
}
@@ -162,7 +162,7 @@ export class SpliceCellsEdit implements IResourceUndoRedoElement {
}
}
redo(): void | Promise<void> {
redo(): void {
if (!this.editingDelegate.deleteCell || !this.editingDelegate.insertCell) {
throw new Error('Notebook Insert/Delete Cell not implemented for Undo/Redo');
}

View File

@@ -10,7 +10,7 @@ import { URI } from 'vs/base/common/uri';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { INotebookTextModel, NotebookCellOutputsSplice, NotebookCellTextModelSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, CellEditType, CellUri, ICellInsertEdit, NotebookCellsChangedEvent, CellKind, IProcessedOutput, notebookDocumentMetadataDefaults, diff, ICellDeleteEdit, NotebookCellsChangeType, ICellDto2, IMainCellDto } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { ITextSnapshot } from 'vs/editor/common/model';
import { IUndoRedoService, UndoRedoElementType } from 'vs/platform/undoRedo/common/undoRedo';
import { IUndoRedoService, UndoRedoElementType, IUndoRedoElement, IResourceUndoRedoElement } from 'vs/platform/undoRedo/common/undoRedo';
import { InsertCellEdit, DeleteCellEdit, MoveCellEdit, SpliceCellsEdit } from 'vs/workbench/contrib/notebook/common/model/cellEdit';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
@@ -66,6 +66,53 @@ export class NotebookTextModelSnapshot implements ITextSnapshot {
}
class StackOperation implements IResourceUndoRedoElement {
type: UndoRedoElementType.Resource;
private _operations: IUndoRedoElement[] = [];
constructor(readonly resource: URI, readonly label: string) {
this.type = UndoRedoElementType.Resource;
}
pushEditOperation(element: IUndoRedoElement) {
this._operations.push(element);
}
undo(): void {
this._operations.reverse().forEach(o => o.undo());
}
redo(): void | Promise<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 {
private _cellhandlePool: number = 0;
@@ -112,6 +159,8 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
protected readonly _onDidChangeDirty = this._register(new Emitter<void>());
readonly onDidChangeDirty = this._onDidChangeDirty.event;
private _operationManager: NotebookOperationManager;
constructor(
public handle: number,
public viewType: string,
@@ -122,6 +171,8 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
) {
super();
this.cells = [];
this._operationManager = new NotebookOperationManager(this._undoService, uri);
}
get isDirty() {
@@ -173,6 +224,10 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
this._increaseVersionId();
}
pushStackElement(label: string) {
this._operationManager.pushStackElement(label);
}
$applyEdit(modelVersionId: number, rawEdits: ICellEditOperation[], synchronous: boolean): boolean {
if (modelVersionId !== this._versionId) {
return false;
@@ -255,7 +310,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
return [diff[0], deletedCells, diff[2]] as [number, NotebookCellTextModel[], NotebookCellTextModel[]];
});
this._undoService.pushElement(new SpliceCellsEdit(this.uri, undoDiff, {
this._operationManager.pushEditOperation(new SpliceCellsEdit(this.uri, undoDiff, {
insertCell: this._insertCellDelegate.bind(this),
deleteCell: this._deleteCellDelegate.bind(this),
emitSelections: this._emitSelectionsDelegate.bind(this)
@@ -266,7 +321,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
}
$handleEdit(label: string | undefined, undo: () => void, redo: () => void): void {
this._undoService.pushElement({
this._operationManager.pushEditOperation({
type: UndoRedoElementType.Resource,
resource: this.uri,
label: label ?? nls.localize('defaultEditLabel', "Edit"),
@@ -502,7 +557,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
const cell = this.createCellTextModel(source, language, type, [], metadata);
if (pushUndoStop) {
this._undoService.pushElement(new InsertCellEdit(this.uri, index, cell, {
this._operationManager.pushEditOperation(new InsertCellEdit(this.uri, index, cell, {
insertCell: this._insertCellDelegate.bind(this),
deleteCell: this._deleteCellDelegate.bind(this),
emitSelections: this._emitSelectionsDelegate.bind(this)
@@ -522,7 +577,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
insertCell2(index: number, cell: NotebookCellTextModel, synchronous: boolean, pushUndoStop: boolean): void {
if (pushUndoStop) {
this._undoService.pushElement(new InsertCellEdit(this.uri, index, cell, {
this._operationManager.pushEditOperation(new InsertCellEdit(this.uri, index, cell, {
insertCell: this._insertCellDelegate.bind(this),
deleteCell: this._deleteCellDelegate.bind(this),
emitSelections: this._emitSelectionsDelegate.bind(this)
@@ -536,7 +591,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
deleteCell2(index: number, synchronous: boolean, pushUndoStop: boolean, beforeSelections: number[] | undefined, endSelections: number[] | undefined) {
const cell = this.cells[index];
if (pushUndoStop) {
this._undoService.pushElement(new DeleteCellEdit(this.uri, index, cell, {
this._operationManager.pushEditOperation(new DeleteCellEdit(this.uri, index, cell, {
insertCell: this._insertCellDelegate.bind(this),
deleteCell: this._deleteCellDelegate.bind(this),
emitSelections: this._emitSelectionsDelegate.bind(this)
@@ -553,7 +608,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
moveCellToIdx2(index: number, newIdx: number, synchronous: boolean, pushedToUndoStack: boolean, beforeSelections: number[] | undefined, endSelections: number[] | undefined): boolean {
const cell = this.cells[index];
if (pushedToUndoStack) {
this._undoService.pushElement(new MoveCellEdit(this.uri, index, newIdx, {
this._operationManager.pushEditOperation(new MoveCellEdit(this.uri, index, newIdx, {
moveCell: (fromIndex: number, toIndex: number, beforeSelections: number[] | undefined, endSelections: number[] | undefined) => {
this.moveCellToIdx2(fromIndex, toIndex, true, false, beforeSelections, endSelections);
},

View File

@@ -63,6 +63,8 @@ export class TestNotebookEditor implements INotebookEditor {
) { }
multipleKernelsAvailable: boolean = false;
onDidChangeAvailableKernels: Event<void> = new Emitter<void>().event;
uri?: URI | undefined;
textModel?: NotebookTextModel | undefined;
@@ -158,6 +160,10 @@ export class TestNotebookEditor implements INotebookEditor {
throw new Error('Method not implemented.');
}
moveCellToIdx(cell: ICellViewModel, index: number): Promise<ICellViewModel | null> {
throw new Error('Method not implemented.');
}
moveCell(cell: ICellViewModel, relativeToCell: ICellViewModel, direction: 'above' | 'below'): Promise<ICellViewModel | null> {
throw new Error('Method not implemented.');
}

View File

@@ -883,7 +883,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
resolve(undefined);
}
} else {
resolve(this.executeTask(task, resolver));
resolve(this.executeTask(task, resolver, runSource));
}
}).then((value) => {
if (runSource === TaskRunSource.User) {
@@ -1452,7 +1452,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
};
}
private executeTask(task: Task, resolver: ITaskResolver): Promise<ITaskSummary> {
private executeTask(task: Task, resolver: ITaskResolver, runSource?: TaskRunSource): Promise<ITaskSummary> {
enum SaveBeforeRunConfigOptions {
Always = 'always',
Never = 'never',
@@ -1464,7 +1464,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
const execTask = async (task: Task, resolver: ITaskResolver): Promise<ITaskSummary> => {
return ProblemMatcherRegistry.onReady().then(() => {
let executeResult = this.getTaskSystem().run(task, resolver);
return this.handleExecuteResult(executeResult);
return this.handleExecuteResult(executeResult, runSource);
});
};
@@ -1501,7 +1501,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
}
}
private async handleExecuteResult(executeResult: ITaskExecuteResult): Promise<ITaskSummary> {
private async handleExecuteResult(executeResult: ITaskExecuteResult, runSource?: TaskRunSource): Promise<ITaskSummary> {
if (executeResult.task.taskLoadMessages && executeResult.task.taskLoadMessages.length > 0) {
executeResult.task.taskLoadMessages.forEach(loadMessage => {
this._outputChannel.append(loadMessage + '\n');
@@ -1509,7 +1509,9 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
this.showOutput();
}
await this.setRecentlyUsedTask(executeResult.task);
if (runSource === TaskRunSource.User) {
await this.setRecentlyUsedTask(executeResult.task);
}
if (executeResult.kind === TaskExecuteKind.Active) {
let active = executeResult.active;
if (active && active.same) {

View File

@@ -840,6 +840,9 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
setTimeout(() => this.layout(this._timeoutDimension!), 0);
}
}
if (!visible) {
this._widgetManager.hideHovers();
}
}
public scrollDownLine(): void {

View File

@@ -110,6 +110,8 @@ export class TerminalViewPane extends ViewPane {
} else {
this.layoutBody(this._bodyDimensions.height, this._bodyDimensions.width);
}
} else {
this._terminalService.getActiveTab()?.setVisible(false);
}
}));

View File

@@ -5,11 +5,15 @@
import { IDisposable } from 'vs/base/common/lifecycle';
import { ITerminalWidget } from 'vs/workbench/contrib/terminal/browser/widgets/widgets';
import { IHoverService } from 'vs/workbench/services/hover/browser/hover';
export class TerminalWidgetManager implements IDisposable {
private _container: HTMLElement | undefined;
private _attached: Map<string, ITerminalWidget> = new Map();
constructor(@IHoverService private readonly _hoverService: IHoverService) {
}
attachToElement(terminalWrapper: HTMLElement) {
if (!this._container) {
this._container = document.createElement('div');
@@ -25,6 +29,10 @@ export class TerminalWidgetManager implements IDisposable {
}
}
hideHovers(): void {
this._hoverService.hideHover();
}
attachWidget(widget: ITerminalWidget): IDisposable | undefined {
if (!this._container) {
return undefined; // {{SQL CARBON EDIT}} strict-null-check

View File

@@ -16,7 +16,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable, dispose } from 'vs/base/common/lifecycle';
import { Codicon } from 'vs/base/common/codicons';
import { IUserDataSyncWorkbenchService, getSyncAreaLabel, IUserDataSyncPreview, IUserDataSyncResource, MANUAL_SYNC_VIEW_ID } from 'vs/workbench/services/userDataSync/common/userDataSync';
import { IUserDataSyncWorkbenchService, getSyncAreaLabel, IUserDataSyncPreview, IUserDataSyncResource, SYNC_MERGES_VIEW_ID } from 'vs/workbench/services/userDataSync/common/userDataSync';
import { isEqual, basename } from 'vs/base/common/resources';
import { IDecorationsProvider, IDecorationData, IDecorationsService } from 'vs/workbench/services/decorations/browser/decorations';
import { IProgressService } from 'vs/platform/progress/common/progress';
@@ -39,7 +39,7 @@ import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { Severity } from 'vs/platform/notification/common/notification';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
export class UserDataManualSyncViewPane extends TreeViewPane {
export class UserDataSyncMergesViewPane extends TreeViewPane {
private userDataSyncPreview: IUserDataSyncPreview;
@@ -83,7 +83,7 @@ export class UserDataManualSyncViewPane extends TreeViewPane {
this.createButtons(container);
const that = this;
this.treeView.message = localize('explanation', "Please go through each entry and accept the change to enable sync.");
this.treeView.message = localize('explanation', "Please go through each entry and merge to enable sync.");
this.treeView.dataProvider = { getChildren() { return that.getTreeItems(); } };
}
@@ -164,7 +164,7 @@ export class UserDataManualSyncViewPane extends TreeViewPane {
icon: Codicon.cloudDownload,
menu: {
id: MenuId.ViewItemContext,
when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', MANUAL_SYNC_VIEW_ID), ContextKeyExpr.equals('viewItem', 'sync-resource-preview')),
when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', SYNC_MERGES_VIEW_ID), ContextKeyExpr.equals('viewItem', 'sync-resource-preview')),
group: 'inline',
order: 1,
},
@@ -184,7 +184,7 @@ export class UserDataManualSyncViewPane extends TreeViewPane {
icon: Codicon.cloudUpload,
menu: {
id: MenuId.ViewItemContext,
when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', MANUAL_SYNC_VIEW_ID), ContextKeyExpr.equals('viewItem', 'sync-resource-preview')),
when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', SYNC_MERGES_VIEW_ID), ContextKeyExpr.equals('viewItem', 'sync-resource-preview')),
group: 'inline',
order: 2,
},
@@ -204,7 +204,7 @@ export class UserDataManualSyncViewPane extends TreeViewPane {
icon: Codicon.merge,
menu: {
id: MenuId.ViewItemContext,
when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', MANUAL_SYNC_VIEW_ID), ContextKeyExpr.equals('viewItem', 'sync-resource-preview')),
when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', SYNC_MERGES_VIEW_ID), ContextKeyExpr.equals('viewItem', 'sync-resource-preview')),
group: 'inline',
order: 3,
},
@@ -224,7 +224,7 @@ export class UserDataManualSyncViewPane extends TreeViewPane {
icon: Codicon.discard,
menu: {
id: MenuId.ViewItemContext,
when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', MANUAL_SYNC_VIEW_ID), ContextKeyExpr.or(ContextKeyExpr.equals('viewItem', 'sync-resource-accepted'), ContextKeyExpr.equals('viewItem', 'sync-resource-conflict'))),
when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', SYNC_MERGES_VIEW_ID), ContextKeyExpr.or(ContextKeyExpr.equals('viewItem', 'sync-resource-accepted'), ContextKeyExpr.equals('viewItem', 'sync-resource-conflict'))),
group: 'inline',
order: 3,
},
@@ -270,7 +270,7 @@ export class UserDataManualSyncViewPane extends TreeViewPane {
previewResource = this.userDataSyncPreview.resources.find(({ local }) => isEqual(local, previewResource.local))!;
await this.reopen(previewResource);
if (previewResource.mergeState === MergeState.Conflict) {
await this.dialogService.show(Severity.Warning, localize('conflicts detected', "Conflicts Detected."), [], {
await this.dialogService.show(Severity.Warning, localize('conflicts detected', "Conflicts Detected"), [], {
detail: localize('resolve', "Unable to merge due to conflicts. Please resolve them to continue.")
});
}
@@ -373,7 +373,7 @@ export class UserDataManualSyncViewPane extends TreeViewPane {
}
private withProgress(task: () => Promise<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);
}
}

View File

@@ -30,13 +30,13 @@ import { IStorageService } from 'vs/platform/storage/common/storage';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IAction, Action } from 'vs/base/common/actions';
import { IUserDataSyncWorkbenchService, CONTEXT_SYNC_STATE, getSyncAreaLabel, CONTEXT_ACCOUNT_STATE, AccountStatus, CONTEXT_ENABLE_ACTIVITY_VIEWS, SHOW_SYNC_LOG_COMMAND_ID, CONFIGURE_SYNC_COMMAND_ID, MANUAL_SYNC_VIEW_ID, CONTEXT_ENABLE_MANUAL_SYNC_VIEW } from 'vs/workbench/services/userDataSync/common/userDataSync';
import { IUserDataSyncWorkbenchService, CONTEXT_SYNC_STATE, getSyncAreaLabel, CONTEXT_ACCOUNT_STATE, AccountStatus, CONTEXT_ENABLE_ACTIVITY_VIEWS, SHOW_SYNC_LOG_COMMAND_ID, CONFIGURE_SYNC_COMMAND_ID, SYNC_MERGES_VIEW_ID, CONTEXT_ENABLE_SYNC_MERGES_VIEW } from 'vs/workbench/services/userDataSync/common/userDataSync';
import { IUserDataSyncMachinesService, IUserDataSyncMachine } from 'vs/platform/userDataSync/common/userDataSyncMachines';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { TreeView } from 'vs/workbench/contrib/views/browser/treeView';
import { flatten } from 'vs/base/common/arrays';
import { UserDataManualSyncViewPane } from 'vs/workbench/contrib/userDataSync/browser/userDataManualSyncView';
import { UserDataSyncMergesViewPane } from 'vs/workbench/contrib/userDataSync/browser/userDataSyncMergesView';
export class UserDataSyncViewPaneContainer extends ViewPaneContainer {
@@ -86,7 +86,7 @@ export class UserDataSyncDataViews extends Disposable {
}
private registerViews(container: ViewContainer): void {
this.registerManualSyncView(container);
this.registerMergesView(container);
this.registerActivityView(container, true);
this.registerMachinesView(container);
@@ -94,17 +94,17 @@ export class UserDataSyncDataViews extends Disposable {
this.registerActivityView(container, false);
}
private registerManualSyncView(container: ViewContainer): void {
private registerMergesView(container: ViewContainer): void {
const viewsRegistry = Registry.as<IViewsRegistry>(Extensions.ViewsRegistry);
const viewName = localize('manual sync', "Manual Sync");
const viewName = localize('merges', "Merges");
viewsRegistry.registerViews([<ITreeViewDescriptor>{
id: MANUAL_SYNC_VIEW_ID,
id: SYNC_MERGES_VIEW_ID,
name: viewName,
ctorDescriptor: new SyncDescriptor(UserDataManualSyncViewPane),
when: CONTEXT_ENABLE_MANUAL_SYNC_VIEW,
ctorDescriptor: new SyncDescriptor(UserDataSyncMergesViewPane),
when: CONTEXT_ENABLE_SYNC_MERGES_VIEW,
canToggleVisibility: false,
canMoveView: false,
treeView: this.instantiationService.createInstance(TreeView, MANUAL_SYNC_VIEW_ID, viewName),
treeView: this.instantiationService.createInstance(TreeView, SYNC_MERGES_VIEW_ID, viewName),
collapsed: false,
order: 100,
}], container);

View File

@@ -49,7 +49,12 @@ export async function openEditorWith(
return undefined; // {{SQL CARBON EDIT}} strict-null-checks
}
const overrideToUse = typeof id === 'string' && allEditorOverrides.find(([_, entry]) => entry.id === id);
let overrideToUse;
if (typeof id === 'string') {
overrideToUse = allEditorOverrides.find(([_, entry]) => entry.id === id);
} else if (allEditorOverrides.length === 1) {
overrideToUse = allEditorOverrides[0];
}
if (overrideToUse) {
return overrideToUse[0].open(input, overrideOptions, group, OpenEditorContext.NEW_EDITOR)?.override;
}

View File

@@ -184,6 +184,7 @@ export class ExtensionHostManager extends Disposable {
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),
assertRegistered: (identifiers: ProxyIdentifier<any>[]): void => this._rpcProtocol!.assertRegistered(identifiers),
drain: (): Promise<void> => this._rpcProtocol!.drain(),
};
// Named customers

View File

@@ -18,6 +18,11 @@ export interface IRPCProtocol {
* Assert these identifiers are already registered via `.set`.
*/
assertRegistered(identifiers: ProxyIdentifier<any>[]): void;
/**
* Wait for the write buffer (if applicable) to become empty.
*/
drain(): Promise<void>;
}
export class ProxyIdentifier<T> {

View File

@@ -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 {
if (this._unacknowledgedCount === 0) {
// Since this is the first request we are sending in a while,

View File

@@ -96,10 +96,10 @@ let onTerminate = function () {
nativeExit();
};
function _createExtHostProtocol(): Promise<IMessagePassingProtocol> {
function _createExtHostProtocol(): Promise<PersistentProtocol> {
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;
@@ -163,7 +163,7 @@ function _createExtHostProtocol(): Promise<IMessagePassingProtocol> {
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, () => {
socket.removeListener('error', reject);
@@ -203,6 +203,10 @@ async function createExtHostProtocol(): Promise<IMessagePassingProtocol> {
protocol.send(msg);
}
}
drain(): Promise<void> {
return protocol.drain();
}
};
}

View File

@@ -6,7 +6,7 @@
import { IUserDataSyncService, IAuthenticationProvider, getUserDataSyncStore, isAuthenticationProvider, IUserDataAutoSyncService, SyncResource, IResourcePreview, ISyncResourcePreview, Change, IManualSyncTask } from 'vs/platform/userDataSync/common/userDataSync';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IUserDataSyncWorkbenchService, IUserDataSyncAccount, AccountStatus, CONTEXT_SYNC_ENABLEMENT, CONTEXT_SYNC_STATE, CONTEXT_ACCOUNT_STATE, SHOW_SYNC_LOG_COMMAND_ID, getSyncAreaLabel, IUserDataSyncPreview, IUserDataSyncResource, CONTEXT_ENABLE_MANUAL_SYNC_VIEW, MANUAL_SYNC_VIEW_ID, CONTEXT_ENABLE_ACTIVITY_VIEWS, SYNC_VIEW_CONTAINER_ID } from 'vs/workbench/services/userDataSync/common/userDataSync';
import { IUserDataSyncWorkbenchService, IUserDataSyncAccount, AccountStatus, CONTEXT_SYNC_ENABLEMENT, CONTEXT_SYNC_STATE, CONTEXT_ACCOUNT_STATE, SHOW_SYNC_LOG_COMMAND_ID, getSyncAreaLabel, IUserDataSyncPreview, IUserDataSyncResource, CONTEXT_ENABLE_SYNC_MERGES_VIEW, SYNC_MERGES_VIEW_ID, CONTEXT_ENABLE_ACTIVITY_VIEWS, SYNC_VIEW_CONTAINER_ID } from 'vs/workbench/services/userDataSync/common/userDataSync';
import { AuthenticationSession, AuthenticationSessionsChangeEvent } from 'vs/editor/common/modes';
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
import { Emitter, Event } from 'vs/base/common/event';
@@ -79,7 +79,7 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat
private readonly syncEnablementContext: IContextKey<boolean>;
private readonly syncStatusContext: IContextKey<string>;
private readonly accountStatusContext: IContextKey<string>;
private readonly manualSyncViewEnablementContext: IContextKey<boolean>;
private readonly mergesViewEnablementContext: IContextKey<boolean>;
private readonly activityViewsEnablementContext: IContextKey<boolean>;
readonly userDataSyncPreview: UserDataSyncPreview = this._register(new UserDataSyncPreview(this.userDataSyncService));
@@ -110,7 +110,7 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat
this.syncStatusContext = CONTEXT_SYNC_STATE.bindTo(contextKeyService);
this.accountStatusContext = CONTEXT_ACCOUNT_STATE.bindTo(contextKeyService);
this.activityViewsEnablementContext = CONTEXT_ENABLE_ACTIVITY_VIEWS.bindTo(contextKeyService);
this.manualSyncViewEnablementContext = CONTEXT_ENABLE_MANUAL_SYNC_VIEW.bindTo(contextKeyService);
this.mergesViewEnablementContext = CONTEXT_ENABLE_SYNC_MERGES_VIEW.bindTo(contextKeyService);
if (this.authenticationProviders.length) {
@@ -302,16 +302,16 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat
const result = await this.dialogService.show(
Severity.Info,
localize('preferences sync', "Preferences Sync"),
localize('merge or replace', "Merge or Replace"),
[
localize('merge', "Merge"),
localize('replace local', "Replace Local"),
localize('sync manually', "Sync Manually..."),
localize('merge Manually', "Merge Manually..."),
localize('cancel', "Cancel"),
],
{
cancelId: 3,
detail: localize('first time sync detail', "It looks like you last synced from another machine.\nWould you like to replace or merge with your data in the cloud or sync manually?"),
detail: localize('first time sync detail', "It looks like you last synced from another machine.\nWould you like to merge or replace with your data in the cloud?"),
}
);
switch (result.choice) {
@@ -333,18 +333,18 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat
const visibleViewContainer = this.viewsService.getVisibleViewContainer(ViewContainerLocation.Sidebar);
this.userDataSyncPreview.setManualSyncPreview(task, preview);
this.manualSyncViewEnablementContext.set(true);
this.mergesViewEnablementContext.set(true);
await this.waitForActiveSyncViews();
await this.viewsService.openView(MANUAL_SYNC_VIEW_ID);
await this.viewsService.openView(SYNC_MERGES_VIEW_ID);
const error = await Event.toPromise(this.userDataSyncPreview.onDidCompleteManualSync);
this.userDataSyncPreview.unsetManualSyncPreview();
this.manualSyncViewEnablementContext.set(false);
this.mergesViewEnablementContext.set(false);
if (visibleViewContainer) {
this.viewsService.openViewContainer(visibleViewContainer.id);
} else {
const viewContainer = this.viewDescriptorService.getViewContainerByViewId(MANUAL_SYNC_VIEW_ID);
const viewContainer = this.viewDescriptorService.getViewContainerByViewId(SYNC_MERGES_VIEW_ID);
this.viewsService.closeViewContainer(viewContainer!.id);
}

View File

@@ -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_ACCOUNT_STATE = new RawContextKey<string>('userDataSyncAccountStatus', AccountStatus.Uninitialized);
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
export const CONFIGURE_SYNC_COMMAND_ID = 'workbench.userDataSync.actions.configure';
@@ -90,4 +90,4 @@ export const SHOW_SYNC_LOG_COMMAND_ID = 'workbench.userDataSync.actions.showLog'
// VIEWS
export const SYNC_VIEW_CONTAINER_ID = 'workbench.view.sync';
export const MANUAL_SYNC_VIEW_ID = 'workbench.views.manualSyncView';
export const SYNC_MERGES_VIEW_ID = 'workbench.views.sync.merges';

View File

@@ -389,6 +389,9 @@ suite('ExtHostDiagnostics', () => {
assertRegistered(): void {
}
drain() {
return undefined!;
}
}, new NullLogService());
let collection1 = diags.createDiagnosticCollection(nullExtensionDescription.identifier, 'foo');
@@ -438,6 +441,9 @@ suite('ExtHostDiagnostics', () => {
assertRegistered(): void {
}
drain() {
return undefined!;
}
}, new NullLogService());

View File

@@ -15,7 +15,8 @@ suite('ExtHostFileSystemEventService', () => {
const protocol: IMainContext = {
getProxy: () => { return undefined!; },
set: undefined!,
assertRegistered: undefined!
assertRegistered: undefined!,
drain: undefined!
};
const watcher1 = new ExtHostFileSystemEventService(protocol, new NullLogService(), undefined!).createFileSystemWatcher('**/somethingInteresting', false, false, false);

View File

@@ -298,7 +298,8 @@ suite('ExtHostWorkspace', function () {
const protocol: IMainContext = {
getProxy: () => { return undefined!; },
set: () => { return undefined!; },
assertRegistered: () => { }
assertRegistered: () => { },
drain: () => { return undefined!; },
};
const ws = createExtHostWorkspace(protocol, { id: 'foo', name: 'Test', folders: [] }, new NullLogService());

View File

@@ -32,6 +32,7 @@ suite('MainThreadDiagnostics', function () {
$acceptMarkersChange() { }
};
}
drain(): any { return null; }
},
markerService,
new class extends mock<IUriIdentityService>() {

View File

@@ -66,6 +66,7 @@ suite('MainThreadHostTreeView', function () {
getProxy(): any {
return extHostTreeViewsShape;
}
drain(): any { return null; }
}, new TestViewsService(), new TestNotificationService(), testExtensionService, new NullLogService());
mainThreadTreeViews.$registerTreeViewDataProvider(testTreeViewId, { showCollapseAll: false, canSelectMany: false });
await testExtensionService.whenInstalledExtensionsRegistered();

View File

@@ -19,7 +19,8 @@ export function SingleProxyRPCProtocol(thing: any): IExtHostContext & IExtHostRp
set<T, R extends T>(identifier: ProxyIdentifier<T>, value: R): R {
return value;
},
assertRegistered: undefined!
assertRegistered: undefined!,
drain: undefined!
};
}
@@ -40,6 +41,10 @@ export class TestRPCProtocol implements IExtHostContext, IExtHostRpcService {
this._proxies = Object.create(null);
}
drain(): Promise<void> {
return Promise.resolve();
}
private get _callCount(): number {
return this._callCountValue;
}