Merge from vscode e5834d3280fcd04898efeac32b9cf1b893f9b127 (#9385)

* Merge from vscode e5834d3280fcd04898efeac32b9cf1b893f9b127

* distro
This commit is contained in:
Anthony Dresser
2020-02-28 00:37:06 -08:00
committed by GitHub
parent 70851716f7
commit 5d13ebf0d2
143 changed files with 1711 additions and 934 deletions

View File

@@ -103,7 +103,8 @@ const indentationFilter = [
'!extensions/mssql/notebooks/**',
'!extensions/integration-tests/testData/**',
'!extensions/big-data-cluster/src/bigDataCluster/controller/apiGenerated.ts',
'!extensions/big-data-cluster/src/bigDataCluster/controller/clusterApiGenerated2.ts'
'!extensions/big-data-cluster/src/bigDataCluster/controller/clusterApiGenerated2.ts',
'!resources/linux/snap/electron-launch'
];
const copyrightFilter = [

View File

@@ -1825,37 +1825,37 @@
},
"viewsWelcome": [
{
"view": "workbench.scm",
"view": "scm",
"contents": "%view.workbench.scm.disabled%",
"when": "!config.git.enabled"
},
{
"view": "workbench.scm",
"view": "scm",
"contents": "%view.workbench.scm.missing%",
"when": "config.git.enabled && git.missing"
},
{
"view": "workbench.scm",
"view": "scm",
"contents": "%view.workbench.scm.empty%",
"when": "config.git.enabled && !git.missing && workbenchState == empty"
},
{
"view": "workbench.scm",
"view": "scm",
"contents": "%view.workbench.scm.folder%",
"when": "config.git.enabled && !git.missing && workbenchState == folder"
},
{
"view": "workbench.scm",
"view": "scm",
"contents": "%view.workbench.scm.workspace%",
"when": "config.git.enabled && !git.missing && workbenchState == workspace && workspaceFolderCount != 0"
},
{
"view": "workbench.scm",
"view": "scm",
"contents": "%view.workbench.scm.emptyWorkspace%",
"when": "config.git.enabled && !git.missing && workbenchState == workspace && workspaceFolderCount == 0"
},
{
"view": "workbench.explorer.emptyView",
"view": "explorer",
"contents": "%view.workbench.cloneRepository%"
}
]

View File

@@ -150,11 +150,11 @@
"colors.ignored": "Color for ignored resources.",
"colors.conflict": "Color for resources with conflicts.",
"colors.submodule": "Color for submodule resources.",
"view.workbench.scm.missing": "A valid git installation was not detected, more details can be found in the [git output](command:git.showOutput).\nPlease [install git](https://git-scm.com/), or learn more about how to use Git and source control in Azure Data Studio in [our docs](https://aka.ms/vscode-scm).\nIf you're using a different version control system, you can [search the Marketplace](command:workbench.extensions.search?%22%40category%3A%5C%22scm%20providers%5C%22%22) for additional extensions.",
"view.workbench.scm.disabled": "If you would like to use git features, please enable git in your [settings](command:workbench.action.openSettings?%5B%22git.enabled%22%5D).\nTo learn more about how to use Git and source control in Azure Data Studio [read our docs](https://aka.ms/vscode-scm).",
"view.workbench.scm.empty": "In order to use git features, you can open a folder containing a git repository or clone from a URL.\n[Open Folder](command:vscode.openFolder)\n[Clone Repository](command:git.clone)\nTo learn more about how to use Git and source control in Azure Data Studio [read our docs](https://aka.ms/vscode-scm).",
"view.workbench.scm.folder": "The folder currently open doesn't have a git repository.\n[Initialize Repository](command:git.init?%5Btrue%5D)\nTo learn more about how to use Git and source control in Azure Data Studio [read our docs](https://aka.ms/vscode-scm).",
"view.workbench.scm.workspace": "The workspace currently open doesn't have any folders containing git repositories.\n[Initialize Repository](command:git.init)\nTo learn more about how to use Git and source control in Azure Data Studio [read our docs](https://aka.ms/vscode-scm).",
"view.workbench.scm.emptyWorkspace": "The workspace currently open doesn't have any folders containing git repositories.\n[Add Folder to Workspace](command:workbench.action.addRootFolder)\nTo learn more about how to use Git and source control in Azure Data Studio [read our docs](https://aka.ms/vscode-scm).",
"view.workbench.cloneRepository": "You can also clone a repository from a URL. To learn more about how to use Git and source control in Azure Data Studio [read our docs](https://aka.ms/vscode-scm).\n[Clone Repository](command:git.clone)"
"view.workbench.scm.missing": "A valid git installation was not detected, more details can be found in the [git output](command:git.showOutput).\nPlease [install git](https://git-scm.com/), or learn more about how to use git and source control in Azure Data Studio in [our docs](https://aka.ms/vscode-scm).\nIf you're using a different version control system, you can [search the Marketplace](command:workbench.extensions.search?%22%40category%3A%5C%22scm%20providers%5C%22%22) for additional extensions.",
"view.workbench.scm.disabled": "If you would like to use git features, please enable git in your [settings](command:workbench.action.openSettings?%5B%22git.enabled%22%5D).\nTo learn more about how to use git and source control in Azure Data Studio [read our docs](https://aka.ms/vscode-scm).",
"view.workbench.scm.empty": "In order to use git features, you can open a folder containing a git repository or clone from a URL.\n[Open Folder](command:vscode.openFolder)\n[Clone Repository](command:git.clone)\nTo learn more about how to use git and source control in Azure Data Studio [read our docs](https://aka.ms/vscode-scm).",
"view.workbench.scm.folder": "The folder currently open doesn't have a git repository.\n[Initialize Repository](command:git.init?%5Btrue%5D)\nTo learn more about how to use git and source control in Azure Data Studio [read our docs](https://aka.ms/vscode-scm).",
"view.workbench.scm.workspace": "The workspace currently open doesn't have any folders containing git repositories.\n[Initialize Repository](command:git.init)\nTo learn more about how to use git and source control in Azure Data Studio [read our docs](https://aka.ms/vscode-scm).",
"view.workbench.scm.emptyWorkspace": "The workspace currently open doesn't have any folders containing git repositories.\n[Add Folder to Workspace](command:workbench.action.addRootFolder)\nTo learn more about how to use git and source control in Azure Data Studio [read our docs](https://aka.ms/vscode-scm).",
"view.workbench.cloneRepository": "You can also clone a repository from a URL. To learn more about how to use git and source control in Azure Data Studio [read our docs](https://aka.ms/vscode-scm).\n[Clone Repository](command:git.clone)"
}

View File

@@ -1397,12 +1397,16 @@ export class CommandCenter {
opts.signoff = true;
}
const smartCommitChanges = config.get<'all' | 'tracked'>('smartCommitChanges');
if (
(
// no changes
(noStagedChanges && noUnstagedChanges)
// or no staged changes and not `all`
|| (!opts.all && noStagedChanges)
// no staged changes and no tracked unstaged changes
|| (noStagedChanges && smartCommitChanges === 'tracked' && repository.workingTreeGroup.resourceStates.every(r => r.type === Status.UNTRACKED))
)
&& !opts.empty
) {
@@ -1416,7 +1420,7 @@ export class CommandCenter {
return false;
}
if (opts.all && config.get<'all' | 'tracked'>('smartCommitChanges') === 'tracked') {
if (opts.all && smartCommitChanges === 'tracked') {
opts.all = 'tracked';
}
@@ -2353,7 +2357,7 @@ export class CommandCenter {
else if (item.previousRef === 'HEAD' && item.ref === '~') {
title = localize('git.title.index', '{0} (Index)', basename);
} else {
title = localize('git.title.diffRefs', '{0} ({1}) \u27f7 {0} ({2})', basename, item.shortPreviousRef, item.shortRef);
title = localize('git.title.diffRefs', '{0} ({1}) {0} ({2})', basename, item.shortPreviousRef, item.shortRef);
}
return commands.executeCommand('vscode.diff', toGitUri(uri, item.previousRef), item.ref === '' ? uri : toGitUri(uri, item.ref), title);

View File

@@ -16,7 +16,7 @@ export async function activate(context: vscode.ExtensionContext) {
await loginService.initialize();
vscode.authentication.registerAuthenticationProvider({
id: 'GitHub',
id: 'github',
displayName: 'GitHub',
onDidChangeSessions: onDidChangeSessions.event,
getSessions: () => Promise.resolve(loginService.sessions),

View File

@@ -71,7 +71,7 @@ export class GitHubAuthenticationProvider {
id: session.id,
accountName: session.accountName,
scopes: session.scopes,
accessToken: () => Promise.resolve(session.accessToken)
getAccessToken: () => Promise.resolve(session.accessToken)
};
});
} catch (e) {
@@ -84,7 +84,7 @@ export class GitHubAuthenticationProvider {
private async storeSessions(): Promise<void> {
const sessionData: SessionData[] = await Promise.all(this._sessions.map(async session => {
const resolvedAccessToken = await session.accessToken();
const resolvedAccessToken = await session.getAccessToken();
return {
id: session.id,
accountName: session.accountName,
@@ -111,7 +111,7 @@ export class GitHubAuthenticationProvider {
const userInfo = await this._githubServer.getUserInfo(token);
return {
id: userInfo.id,
accessToken: () => Promise.resolve(token),
getAccessToken: () => Promise.resolve(token),
accountName: userInfo.accountName,
scopes: scopes
};

View File

@@ -17,7 +17,7 @@
"Other"
],
"activationEvents": [
"onWebviewEditor:imagePreview.previewEditor",
"onCustomEditor:imagePreview.previewEditor",
"onCommand:imagePreview.zoomIn",
"onCommand:imagePreview.zoomOut"
],

View File

@@ -27,7 +27,7 @@
"onCommand:markdown.api.render",
"onCommand:notebook.showPreview",
"onWebviewPanel:markdown.preview",
"onWebviewEditor:vscode.markdown.preview.editor"
"onCustomEditor:vscode.markdown.preview.editor"
],
"contributes": {
"commands": [

View File

@@ -39,5 +39,8 @@
"tslint": "^5.12.1",
"@types/node": "^10.12.21",
"@types/keytar": "^4.0.1"
},
"dependencies": {
"vscode-nls": "^4.1.1"
}
}

View File

@@ -1,6 +1,6 @@
{
"displayName": "Microsoft Account",
"description": "Microsoft authentication provider",
"signIn": "Sign in",
"signOut": "Sign out"
"signIn": "Sign In",
"signOut": "Sign Out"
}

View File

@@ -184,7 +184,7 @@ export class AzureActiveDirectoryService {
private convertToSession(token: IToken): vscode.AuthenticationSession {
return {
id: token.sessionId,
accessToken: () => this.resolveAccessToken(token),
getAccessToken: () => this.resolveAccessToken(token),
accountName: token.accountName,
scopes: token.scope.split(' ')
};
@@ -192,7 +192,9 @@ export class AzureActiveDirectoryService {
private async resolveAccessToken(token: IToken): Promise<string> {
if (token.accessToken && (!token.expiresAt || token.expiresAt > Date.now())) {
Logger.info('Token available from cache');
token.expiresAt
? Logger.info(`Token available from cache, expires in ${token.expiresAt - Date.now()} milliseconds`)
: Logger.info('Token available from cache');
return Promise.resolve(token.accessToken);
}

View File

@@ -5,6 +5,9 @@
import * as vscode from 'vscode';
import { AzureActiveDirectoryService, onDidChangeSessions } from './AADHelper';
import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle();
export const DEFAULT_SCOPES = 'https://management.core.windows.net/.default offline_access';
@@ -15,7 +18,7 @@ export async function activate(context: vscode.ExtensionContext) {
await loginService.initialize();
context.subscriptions.push(vscode.authentication.registerAuthenticationProvider({
id: 'MSA',
id: 'microsoft',
displayName: 'Microsoft',
onDidChangeSessions: onDidChangeSessions.event,
getSessions: () => Promise.resolve(loginService.sessions),
@@ -45,6 +48,7 @@ export async function activate(context: vscode.ExtensionContext) {
if (sessions.length === 1) {
await loginService.logout(loginService.sessions[0].id);
onDidChangeSessions.fire();
vscode.window.showInformationMessage(localize('signedOut', "Successfully signed out."));
return;
}
@@ -58,6 +62,7 @@ export async function activate(context: vscode.ExtensionContext) {
if (selectedSession) {
await loginService.logout(selectedSession.id);
onDidChangeSessions.fire();
vscode.window.showInformationMessage(localize('signedOut', "Successfully signed out."));
return;
}
}));

View File

@@ -635,6 +635,11 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1:
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
vscode-nls@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c"
integrity sha512-4R+2UoUUU/LdnMnFjePxfLqNhBS8lrAFyX7pjb2ud/lqDkrUavFUTcG7wR0HBZFakae0Q6KLBFjMS6W93F403A==
which-pm-runs@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb"

View File

@@ -1,7 +1,7 @@
{
"name": "azuredatastudio",
"version": "1.16.0",
"distro": "4672f37b373e16688fc9cda03d25a138669718e9",
"distro": "4e85ca0eb0c1b17aceab11996f7c2cf460868e8a",
"author": {
"name": "Microsoft Corporation"
},
@@ -184,7 +184,7 @@
"vinyl": "^2.0.0",
"vinyl-fs": "^3.0.0",
"vsce": "1.48.0",
"vscode-debugprotocol": "1.39.0-pre.0",
"vscode-debugprotocol": "1.39.0",
"vscode-nls-dev": "^3.3.1",
"webpack": "^4.16.5",
"webpack-cli": "^3.3.8",

View File

@@ -2,7 +2,7 @@
# On Fedora $SNAP is under /var and there is some magic to map it to /snap.
# We need to handle that case and reset $SNAP
SNAP=$(echo $SNAP | sed -e "s|/var/lib/snapd||g")
SNAP=$(echo "$SNAP" | sed -e "s|/var/lib/snapd||g")
if [ "$SNAP_ARCH" == "amd64" ]; then
ARCH="x86_64-linux-gnu"
@@ -14,21 +14,21 @@ else
ARCH="$SNAP_ARCH-linux-gnu"
fi
export XDG_CACHE_HOME=$SNAP_USER_COMMON/.cache
if [[ -d $SNAP_USER_DATA/.cache && ! -e $XDG_CACHE_HOME ]]; then
GDK_CACHE_DIR="$SNAP_USER_COMMON/.cache"
if [[ -d "$SNAP_USER_DATA/.cache" && ! -e "$GDK_CACHE_DIR" ]]; then
# the .cache directory used to be stored under $SNAP_USER_DATA, migrate it
mv $SNAP_USER_DATA/.cache $SNAP_USER_COMMON/
mv "$SNAP_USER_DATA/.cache" "$SNAP_USER_COMMON/"
fi
mkdir -p $XDG_CACHE_HOME
[ ! -d "$GDK_CACHE_DIR" ] && mkdir -p "$GDK_CACHE_DIR"
# Gdk-pixbuf loaders
export GDK_PIXBUF_MODULE_FILE=$XDG_CACHE_HOME/gdk-pixbuf-loaders.cache
export GDK_PIXBUF_MODULEDIR=$SNAP/usr/lib/$ARCH/gdk-pixbuf-2.0/2.10.0/loaders
if [ -f $SNAP/usr/lib/$ARCH/gdk-pixbuf-2.0/gdk-pixbuf-query-loaders ]; then
$SNAP/usr/lib/$ARCH/gdk-pixbuf-2.0/gdk-pixbuf-query-loaders > $GDK_PIXBUF_MODULE_FILE
export GDK_PIXBUF_MODULE_FILE="$GDK_CACHE_DIR/gdk-pixbuf-loaders.cache"
export GDK_PIXBUF_MODULEDIR="$SNAP/usr/lib/$ARCH/gdk-pixbuf-2.0/2.10.0/loaders"
if [ -f "$SNAP/usr/lib/$ARCH/gdk-pixbuf-2.0/gdk-pixbuf-query-loaders" ]; then
"$SNAP/usr/lib/$ARCH/gdk-pixbuf-2.0/gdk-pixbuf-query-loaders" > "$GDK_PIXBUF_MODULE_FILE"
fi
# Create $XDG_RUNTIME_DIR if not exists (to be removed when https://pad.lv/1656340 is fixed)
[ -n "$XDG_RUNTIME_DIR" ] && mkdir -p $XDG_RUNTIME_DIR -m 700
[ -n "$XDG_RUNTIME_DIR" ] && mkdir -p "$XDG_RUNTIME_DIR" -m 700
exec "$@"

View File

@@ -5,7 +5,7 @@
@font-face {
font-family: "codicon";
src: url("./codicon.ttf?279add2ec8b3d516ca20a123230cbf9f") format("truetype");
src: url("./codicon.ttf?b5dd8f5aa953889dc1f4c9fa9b44d3dd") format("truetype");
}
.codicon[class*='codicon-'] {
@@ -415,5 +415,6 @@
.codicon-group-by-ref-type:before { content: "\eb97" }
.codicon-ungroup-by-ref-type:before { content: "\eb98" }
.codicon-bell-dot:before { content: "\f101" }
.codicon-debug-alt-2:before { content: "\f102" }
.codicon-debug-alt:before { content: "\f103" }
.codicon-bell-progress:before { content: "\f102" }
.codicon-debug-alt-2:before { content: "\f103" }
.codicon-debug-alt:before { content: "\f104" }

View File

@@ -554,7 +554,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
if (!item.row) {
item.row = this.cache.alloc(item.templateId);
const role = this.ariaProvider.getRole ? this.ariaProvider.getRole(item.element) : 'treeitem';
const role = this.ariaProvider.getRole ? this.ariaProvider.getRole(item.element) : 'listitem';
item.row!.domNode!.setAttribute('role', role);
const checked = this.ariaProvider.isChecked ? this.ariaProvider.isChecked(item.element) : undefined;
if (typeof checked !== 'undefined') {

View File

@@ -196,7 +196,7 @@ function asListOptions<T, TFilterData, TRef>(modelProvider: () => ITreeModel<T,
} : undefined,
getRole: options.ariaProvider && options.ariaProvider.getRole ? (node) => {
return options.ariaProvider!.getRole!(node.element);
} : undefined
} : () => 'treeitem'
}
};
}

View File

@@ -267,7 +267,7 @@ function asObjectTreeOptions<TInput, T, TFilterData>(options?: IAsyncDataTreeOpt
},
getRole: options.ariaProvider!.getRole ? (el) => {
return options.ariaProvider!.getRole!(el.element as T);
} : undefined,
} : () => 'treeitem',
isChecked: options.ariaProvider!.isChecked ? (e) => {
return options.ariaProvider?.isChecked!(e.element as T);
} : undefined

View File

@@ -23,7 +23,7 @@ export class LinkedText {
}
}
const LINK_REGEX = /\[([^\]]+)\]\(((?:https?:\/\/|command:)[^\)\s]+)(?: "([^"]+)")?\)/gi;
const LINK_REGEX = /\[([^\]]+)\]\(((?:https?:\/\/|command:)[^\)\s]+)(?: ("|')([^\3]+)(\3))?\)/gi;
export function parseLinkedText(text: string): LinkedText {
const result: LinkedTextNode[] = [];
@@ -36,7 +36,7 @@ export function parseLinkedText(text: string): LinkedText {
result.push(text.substring(index, match.index));
}
const [, label, href, title] = match;
const [, label, href, , title] = match;
if (title) {
result.push({ label, href, title });

View File

@@ -21,4 +21,5 @@ export class CompositeDragAndDropData implements IDragAndDropData {
export interface ICompositeDragAndDrop {
drop(data: IDragAndDropData, target: string | undefined, originalEvent: DragEvent): void;
onDragOver(data: IDragAndDropData, target: string | undefined, originalEvent: DragEvent): boolean;
onDragEnter(data: IDragAndDropData, target: string | undefined, originalEvent: DragEvent): boolean;
}

View File

@@ -397,7 +397,7 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
private _valueSelection: Readonly<[number, number]> | undefined;
private valueSelectionUpdated = true;
private _validationMessage: string | undefined;
private _ok = false;
private _ok: boolean | 'default' = 'default';
private _customButton = false;
private _customButtonLabel: string | undefined;
private _customButtonHover: string | undefined;
@@ -566,7 +566,7 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
return this._ok;
}
set ok(showOkButton: boolean) {
set ok(showOkButton: boolean | 'default') {
this._ok = showOkButton;
this.update();
}
@@ -575,6 +575,10 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
return this.visible ? this.ui.inputBox.hasFocus() : false;
}
public focusOnInput() {
this.ui.inputBox.setFocus();
}
onDidChangeSelection = this.onDidChangeSelectionEmitter.event;
onDidTriggerItemButton = this.onDidTriggerItemButtonEmitter.event;
@@ -753,7 +757,8 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
if (!this.visible) {
return;
}
this.ui.setVisibilities(this.canSelectMany ? { title: !!this.title || !!this.step, description: !!this.description, checkAll: true, inputBox: true, visibleCount: true, count: true, ok: this.ok, list: true, message: !!this.validationMessage, customButton: this.customButton } : { title: !!this.title || !!this.step, description: !!this.description, inputBox: true, visibleCount: true, list: true, message: !!this.validationMessage, customButton: this.customButton, ok: this.ok });
const ok = this.ok === 'default' ? this.canSelectMany : this.ok;
this.ui.setVisibilities(this.canSelectMany ? { title: !!this.title || !!this.step, description: !!this.description, checkAll: true, inputBox: true, visibleCount: true, count: true, ok, list: true, message: !!this.validationMessage, customButton: this.customButton } : { title: !!this.title || !!this.step, description: !!this.description, inputBox: true, visibleCount: true, list: true, message: !!this.validationMessage, customButton: this.customButton, ok });
super.update();
if (this.ui.inputBox.value !== this.value) {
this.ui.inputBox.value = this.value;

View File

@@ -113,7 +113,7 @@ export interface IInputOptions {
placeHolder?: string;
/**
* set to true to show a password prompt that will not show the typed value
* Controls if a password input is shown. Password input hides the typed text.
*/
password?: boolean;
@@ -162,7 +162,7 @@ export interface IQuickPick<T extends IQuickPickItem> extends IQuickInput {
readonly onDidAccept: Event<void>;
ok: boolean;
ok: boolean | 'default';
readonly onDidCustom: Event<void>;
@@ -209,6 +209,8 @@ export interface IQuickPick<T extends IQuickPickItem> extends IQuickInput {
validationMessage: string | undefined;
inputHasFocus(): boolean;
focusOnInput(): void;
}
export interface IInputBox extends IQuickInput {

View File

@@ -27,7 +27,8 @@ export interface IUpdateRequest {
}
export interface IStorageItemsChangeEvent {
items: Map<string, string>;
changed?: Map<string, string>;
deleted?: Set<string>;
}
export interface IStorageDatabase {
@@ -104,10 +105,11 @@ export class Storage extends Disposable implements IStorage {
// items that change external require us to update our
// caches with the values. we just accept the value and
// emit an event if there is a change.
e.items.forEach((value, key) => this.accept(key, value));
e.changed?.forEach((value, key) => this.accept(key, value));
e.deleted?.forEach(key => this.accept(key, undefined));
}
private accept(key: string, value: string): void {
private accept(key: string, value: string | undefined): void {
if (this.state === StorageState.Closed) {
return; // Return early if we are already closed
}

View File

@@ -124,28 +124,27 @@ suite('Storage Library', () => {
changes.clear();
// Nothing happens if changing to same value
const change = new Map<string, string>();
change.set('foo', 'bar');
database.fireDidChangeItemsExternal({ items: change });
const changed = new Map<string, string>();
changed.set('foo', 'bar');
database.fireDidChangeItemsExternal({ changed });
equal(changes.size, 0);
// Change is accepted if valid
change.set('foo', 'bar1');
database.fireDidChangeItemsExternal({ items: change });
changed.set('foo', 'bar1');
database.fireDidChangeItemsExternal({ changed });
ok(changes.has('foo'));
equal(storage.get('foo'), 'bar1');
changes.clear();
// Delete is accepted
change.set('foo', undefined!);
database.fireDidChangeItemsExternal({ items: change });
const deleted = new Set<string>(['foo']);
database.fireDidChangeItemsExternal({ deleted });
ok(changes.has('foo'));
equal(storage.get('foo', null!), null);
equal(storage.get('foo', undefined), undefined);
changes.clear();
// Nothing happens if changing to same value
change.set('foo', undefined!);
database.fireDidChangeItemsExternal({ items: change });
database.fireDidChangeItemsExternal({ deleted });
equal(changes.size, 0);
await storage.close();

View File

@@ -21,6 +21,21 @@ suite('LinkedText', () => {
{ label: 'link text', href: 'http://link.href', title: 'and a title' },
'.'
]);
assert.deepEqual(parseLinkedText('Some message with [link text](http://link.href \'and a title\').').nodes, [
'Some message with ',
{ label: 'link text', href: 'http://link.href', title: 'and a title' },
'.'
]);
assert.deepEqual(parseLinkedText('Some message with [link text](http://link.href "and a \'title\'").').nodes, [
'Some message with ',
{ label: 'link text', href: 'http://link.href', title: 'and a \'title\'' },
'.'
]);
assert.deepEqual(parseLinkedText('Some message with [link text](http://link.href \'and a "title"\').').nodes, [
'Some message with ',
{ label: 'link text', href: 'http://link.href', title: 'and a "title"' },
'.'
]);
assert.deepEqual(parseLinkedText('Some message with [link text](random stuff).').nodes, [
'Some message with [link text](random stuff).'
]);

View File

@@ -75,7 +75,11 @@ function getProcessItem(processes: FormattedProcessItem[], item: ProcessItem, in
// Recurse into children if any
if (Array.isArray(item.children)) {
item.children.forEach(child => getProcessItem(processes, child, indent + 1, isLocal));
item.children.forEach(child => {
if (child) {
getProcessItem(processes, child, indent + 1, isLocal);
}
});
}
}

View File

@@ -32,6 +32,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogs';
import { mnemonicButtonLabel } from 'vs/base/common/labels';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
const RUN_TEXTMATE_IN_WORKER = false;
@@ -100,7 +101,8 @@ export class CodeWindow extends Disposable implements ICodeWindow {
@IWorkspacesMainService private readonly workspacesMainService: IWorkspacesMainService,
@IBackupMainService private readonly backupMainService: IBackupMainService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IDialogMainService private readonly dialogMainService: IDialogMainService
@IDialogMainService private readonly dialogMainService: IDialogMainService,
@ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService
) {
super();
@@ -244,6 +246,8 @@ export class CodeWindow extends Disposable implements ICodeWindow {
get isExtensionTestHost(): boolean { return !!(this.config && this.config.extensionTestsPath); }
get isExtensionDevelopmentTestFromCli(): boolean { return this.isExtensionDevelopmentHost && this.isExtensionTestHost && !this.config?.debugId; }
setRepresentedFilename(filename: string): void {
if (isMacintosh) {
this.win.setRepresentedFilename(filename);
@@ -447,6 +451,15 @@ export class CodeWindow extends Disposable implements ICodeWindow {
private onWindowError(error: WindowError): void {
this.logService.error(error === WindowError.CRASHED ? '[VS Code]: render process crashed!' : '[VS Code]: detected unresponsive');
// If we run extension tests from CLI, showing a dialog is not
// very helpful in this case. Rather, we bring down the test run
// to signal back a failing run.
if (this.isExtensionDevelopmentTestFromCli) {
this.lifecycleMainService.kill(1);
return;
}
// Telemetry
type WindowErrorClassification = {
type: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true };
};

View File

@@ -16,6 +16,7 @@ export interface IBulkEditOptions {
progress?: IProgress<IProgressStep>;
showPreview?: boolean;
label?: string;
quotableLabel?: string;
}
export interface IBulkEditResult {

View File

@@ -46,7 +46,7 @@ class MinimapOptions {
public readonly renderMinimap: RenderMinimap;
public readonly mode: 'actual' | 'cover' | 'contain';
public readonly size: 'proportional' | 'fill' | 'fit';
public readonly minimapHeightIsEditorHeight: boolean;
@@ -108,7 +108,7 @@ class MinimapOptions {
const minimapOpts = options.get(EditorOption.minimap);
this.renderMinimap = layoutInfo.renderMinimap | 0;
this.mode = minimapOpts.mode;
this.size = minimapOpts.size;
this.minimapHeightIsEditorHeight = layoutInfo.minimapHeightIsEditorHeight;
this.scrollBeyondLastLine = options.get(EditorOption.scrollBeyondLastLine);
this.showSlider = minimapOpts.showSlider;
@@ -144,7 +144,7 @@ class MinimapOptions {
public equals(other: MinimapOptions): boolean {
return (this.renderMinimap === other.renderMinimap
&& this.mode === other.mode
&& this.size === other.size
&& this.minimapHeightIsEditorHeight === other.minimapHeightIsEditorHeight
&& this.scrollBeyondLastLine === other.scrollBeyondLastLine
&& this.showSlider === other.showSlider
@@ -1111,7 +1111,7 @@ class InnerMinimap extends Disposable {
if (!this._lastRenderData) {
return;
}
if (this._model.options.minimapHeightIsEditorHeight) {
if (this._model.options.size !== 'proportional') {
if (e.leftButton && this._lastRenderData) {
// pretend the click occured in the center of the slider
const position = dom.getDomNodePagePosition(this._slider.domNode);

View File

@@ -617,10 +617,11 @@ export class DiffReview extends Disposable {
header.setAttribute('role', 'listitem');
container.appendChild(header);
const lineHeight = modifiedOptions.get(EditorOption.lineHeight);
let modLine = minModifiedLine;
for (let i = 0, len = diffs.length; i < len; i++) {
const diffEntry = diffs[i];
DiffReview._renderSection(container, diffEntry, modLine, this._width, originalOptions, originalModel, originalModelOpts, modifiedOptions, modifiedModel, modifiedModelOpts);
DiffReview._renderSection(container, diffEntry, modLine, lineHeight, this._width, originalOptions, originalModel, originalModelOpts, modifiedOptions, modifiedModel, modifiedModelOpts);
if (diffEntry.modifiedLineStart !== 0) {
modLine = diffEntry.modifiedLineEnd;
}
@@ -632,7 +633,7 @@ export class DiffReview extends Disposable {
}
private static _renderSection(
dest: HTMLElement, diffEntry: DiffEntry, modLine: number, width: number,
dest: HTMLElement, diffEntry: DiffEntry, modLine: number, lineHeight: number, width: number,
originalOptions: IComputedEditorOptions, originalModel: ITextModel, originalModelOpts: TextModelResolvedOptions,
modifiedOptions: IComputedEditorOptions, modifiedModel: ITextModel, modifiedModelOpts: TextModelResolvedOptions
): void {
@@ -641,17 +642,18 @@ export class DiffReview extends Disposable {
let rowClassName: string = 'diff-review-row';
let lineNumbersExtraClassName: string = '';
let spacerClassName: string = 'diff-review-spacer';
const spacerClassName: string = 'diff-review-spacer';
let spacerCodiconName: string | null = null;
switch (type) {
case DiffEntryType.Insert:
rowClassName = 'diff-review-row line-insert';
lineNumbersExtraClassName = ' char-insert';
spacerClassName = 'diff-review-spacer insert-sign';
spacerCodiconName = 'codicon codicon-add';
break;
case DiffEntryType.Delete:
rowClassName = 'diff-review-row line-delete';
lineNumbersExtraClassName = ' char-delete';
spacerClassName = 'diff-review-spacer delete-sign';
spacerCodiconName = 'codicon codicon-remove';
break;
}
@@ -686,6 +688,7 @@ export class DiffReview extends Disposable {
let cell = document.createElement('div');
cell.className = 'diff-review-cell';
cell.style.height = `${lineHeight}px`;
row.appendChild(cell);
const originalLineNumber = document.createElement('span');
@@ -713,7 +716,15 @@ export class DiffReview extends Disposable {
const spacer = document.createElement('span');
spacer.className = spacerClassName;
spacer.innerHTML = '&#160;&#160;';
if (spacerCodiconName) {
const spacerCodicon = document.createElement('span');
spacerCodicon.className = spacerCodiconName;
spacerCodicon.innerHTML = '&#160;&#160;';
spacer.appendChild(spacerCodicon);
} else {
spacer.innerHTML = '&#160;&#160;';
}
cell.appendChild(spacer);
let lineContent: string;

View File

@@ -37,13 +37,14 @@
width: 100%;
}
.monaco-diff-editor .diff-review-cell {
display: table-cell;
}
.monaco-diff-editor .diff-review-spacer {
display: inline-block;
width: 10px;
vertical-align: middle;
}
.monaco-diff-editor .diff-review-spacer > .codicon {
font-size: 9px !important;
}
.monaco-diff-editor .diff-review-actions {

View File

@@ -510,7 +510,7 @@ export interface IEditorOptions {
* Controls whether clicking on the empty content after a folded line will unfold the line.
* Defaults to false.
*/
unfoldOnClickInEmptyContent?: boolean;
unfoldOnClickAfterEndOfLine?: boolean;
/**
* Enable highlighting of matching brackets.
* Defaults to 'always'.
@@ -1808,7 +1808,7 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
const minimapRenderCharacters = minimap.renderCharacters;
let minimapScale = (pixelRatio >= 2 ? Math.round(minimap.scale * 2) : minimap.scale);
const minimapMaxColumn = minimap.maxColumn | 0;
const minimapMode = minimap.mode;
const minimapSize = minimap.size;
const scrollbar = options.get(EditorOption.scrollbar);
const verticalScrollbarWidth = scrollbar.verticalScrollbarSize | 0;
@@ -1872,7 +1872,7 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
let minimapCharWidth = minimapScale / pixelRatio;
let minimapWidthMultiplier: number = 1;
if (minimapMode === 'cover' || minimapMode === 'contain') {
if (minimapSize === 'fill' || minimapSize === 'fit') {
const viewLineCount = env.viewLineCount;
const { typicalViewportLineCount, extraLinesBeyondLastLine, desiredRatio, minimapLineCount } = EditorLayoutInfoComputer.computeContainedMinimapLineCount({
viewLineCount: viewLineCount,
@@ -1893,7 +1893,7 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
minimapCharWidth = minimapScale / pixelRatio;
} else {
const effectiveMinimapHeight = Math.ceil((viewLineCount + extraLinesBeyondLastLine) * minimapLineHeight);
if (minimapMode === 'cover' || effectiveMinimapHeight > minimapCanvasInnerHeight) {
if (minimapSize === 'fill' || effectiveMinimapHeight > minimapCanvasInnerHeight) {
minimapHeightIsEditorHeight = true;
const configuredFontScale = minimapScale;
minimapLineHeight = Math.min(lineHeight * pixelRatio, Math.max(1, Math.floor(1 / desiredRatio)));
@@ -2080,7 +2080,7 @@ export interface IEditorMinimapOptions {
* Control the minimap rendering mode.
* Defaults to 'actual'.
*/
mode?: 'actual' | 'cover' | 'contain';
size?: 'proportional' | 'fill' | 'fit';
/**
* Control the rendering of the minimap slider.
* Defaults to 'mouseover'.
@@ -2109,7 +2109,7 @@ class EditorMinimap extends BaseEditorOption<EditorOption.minimap, EditorMinimap
constructor() {
const defaults: EditorMinimapOptions = {
enabled: false, // {{SQL CARBON EDIT}} disable minimap by default
mode: 'actual',
size: 'proportional',
side: 'right',
showSlider: 'mouseover',
renderCharacters: true,
@@ -2124,16 +2124,16 @@ class EditorMinimap extends BaseEditorOption<EditorOption.minimap, EditorMinimap
default: defaults.enabled,
description: nls.localize('minimap.enabled', "Controls whether the minimap is shown.")
},
'editor.minimap.mode': {
'editor.minimap.size': {
type: 'string',
enum: ['actual', 'cover', 'contain'],
enum: ['proportional', 'fill', 'fit'],
enumDescriptions: [
nls.localize('minimap.mode.actual', "The minimap will be displayed in its original size, so it might be higher than the editor."),
nls.localize('minimap.mode.cover', "The minimap will always have the height of the editor and will stretch or shrink as necessary."),
nls.localize('minimap.mode.contain', "The minimap will shrink as necessary to never be higher than the editor."),
nls.localize('minimap.size.proportional', "The minimap has the same size as the editor contents (and might scroll)."),
nls.localize('minimap.size.fill', "The minimap will stretch or shrink as necessary to fill the height of the editor (no scrolling)."),
nls.localize('minimap.size.fit', "The minimap will shrink as necessary to never be larger than the editor (no scrolling)."),
],
default: defaults.mode,
description: nls.localize('minimap.mode', "Controls the rendering mode of the minimap.")
default: defaults.size,
description: nls.localize('minimap.size', "Controls the size of the minimap.")
},
'editor.minimap.side': {
type: 'string',
@@ -2152,7 +2152,8 @@ class EditorMinimap extends BaseEditorOption<EditorOption.minimap, EditorMinimap
default: defaults.scale,
minimum: 1,
maximum: 3,
description: nls.localize('minimap.scale', "Scale of content drawn in the minimap.")
enum: [1, 2, 3],
description: nls.localize('minimap.scale', "Scale of content drawn in the minimap: 1, 2 or 3.")
},
'editor.minimap.renderCharacters': {
type: 'boolean',
@@ -2175,7 +2176,7 @@ class EditorMinimap extends BaseEditorOption<EditorOption.minimap, EditorMinimap
const input = _input as IEditorMinimapOptions;
return {
enabled: EditorBooleanOption.boolean(input.enabled, this.defaultValue.enabled),
mode: EditorStringEnumOption.stringSet<'actual' | 'cover' | 'contain'>(input.mode, this.defaultValue.mode, ['actual', 'cover', 'contain']),
size: EditorStringEnumOption.stringSet<'proportional' | 'fill' | 'fit'>(input.size, this.defaultValue.size, ['proportional', 'fill', 'fit']),
side: EditorStringEnumOption.stringSet<'right' | 'left'>(input.side, this.defaultValue.side, ['right', 'left']),
showSlider: EditorStringEnumOption.stringSet<'always' | 'mouseover'>(input.showSlider, this.defaultValue.showSlider, ['always', 'mouseover']),
renderCharacters: EditorBooleanOption.boolean(input.renderCharacters, this.defaultValue.renderCharacters),
@@ -2840,9 +2841,14 @@ export interface ISuggestOptions {
*/
showSnippets?: boolean;
/**
* Controls the visibility of the status bar at the bottom of the suggest widget.
* Status bar related settings.
*/
hideStatusBar?: boolean;
statusBar?: {
/**
* Controls the visibility of the status bar at the bottom of the suggest widget.
*/
visible?: boolean;
}
}
export type InternalSuggestOptions = Readonly<Required<ISuggestOptions>>;
@@ -2884,7 +2890,9 @@ class EditorSuggest extends BaseEditorOption<EditorOption.suggest, InternalSugge
showFolders: true,
showTypeParameters: true,
showSnippets: true,
hideStatusBar: true
statusBar: {
visible: false
}
};
super(
EditorOption.suggest, 'suggest', defaults,
@@ -3070,10 +3078,10 @@ class EditorSuggest extends BaseEditorOption<EditorOption.suggest, InternalSugge
default: true,
markdownDescription: nls.localize('editor.suggest.showSnippets', "When enabled IntelliSense shows `snippet`-suggestions.")
},
'editor.suggest.hideStatusBar': {
'editor.suggest.statusBar.visible': {
type: 'boolean',
default: true,
markdownDescription: nls.localize('editor.suggest.hideStatusBar', "Controls the visibility of the status bar at the bottom of the suggest widget.")
default: false,
markdownDescription: nls.localize('editor.suggest.statusBar.visible', "Controls the visibility of the status bar at the bottom of the suggest widget.")
}
}
);
@@ -3118,7 +3126,9 @@ class EditorSuggest extends BaseEditorOption<EditorOption.suggest, InternalSugge
showFolders: EditorBooleanOption.boolean(input.showFolders, this.defaultValue.showFolders),
showTypeParameters: EditorBooleanOption.boolean(input.showTypeParameters, this.defaultValue.showTypeParameters),
showSnippets: EditorBooleanOption.boolean(input.showSnippets, this.defaultValue.showSnippets),
hideStatusBar: EditorBooleanOption.boolean(input.hideStatusBar, this.defaultValue.hideStatusBar),
statusBar: {
visible: EditorBooleanOption.boolean(input.statusBar?.visible, !!this.defaultValue.statusBar.visible)
}
};
}
}
@@ -3333,7 +3343,7 @@ export const enum EditorOption {
folding,
foldingStrategy,
foldingHighlight,
unfoldOnClickInEmptyContent,
unfoldOnClickAfterEndOfLine,
fontFamily,
fontInfo,
fontLigatures,
@@ -3633,9 +3643,9 @@ export const EditorOptions = {
EditorOption.foldingHighlight, 'foldingHighlight', true,
{ description: nls.localize('foldingHighlight', "Controls whether the editor should highlight folded ranges.") }
)),
unfoldOnClickInEmptyContent: register(new EditorBooleanOption(
EditorOption.unfoldOnClickInEmptyContent, 'unfoldOnClickInEmptyContent', false,
{ description: nls.localize('unfoldOnClickInEmptyContent', "Controls whether clicking on the empty content after a folded line will unfold the line.") }
unfoldOnClickAfterEndOfLine: register(new EditorBooleanOption(
EditorOption.unfoldOnClickAfterEndOfLine, 'unfoldOnClickAfterEndOfLine', false,
{ description: nls.localize('unfoldOnClickAfterEndOfLine', "Controls whether clicking on the empty content after a folded line will unfold the line.") }
)),
fontFamily: register(new EditorStringOption(
EditorOption.fontFamily, 'fontFamily', EDITOR_FONT_DEFAULTS.fontFamily,
@@ -3777,7 +3787,7 @@ export const EditorOptions = {
)),
definitionLinkOpensInPeek: register(new EditorBooleanOption(
EditorOption.definitionLinkOpensInPeek, 'definitionLinkOpensInPeek', false,
{ description: nls.localize('definitionLinkOpensInPeek', "Controls whether the definition link opens element in the peek widget.") }
{ description: nls.localize('definitionLinkOpensInPeek', "Controls whether the Go to Definition mouse gesture always opens the peek widget.") }
)),
quickSuggestions: register(new EditorQuickSuggestions()),
quickSuggestionsDelay: register(new EditorIntOption(

View File

@@ -430,15 +430,14 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
return this._columnSelectData;
}
const primaryCursor = this._cursors.getPrimaryCursor();
const primaryPos = primaryCursor.viewState.selectionStart.getStartPosition();
const viewLineNumber = primaryPos.lineNumber;
const viewVisualColumn = CursorColumns.visibleColumnFromColumn2(this.context.config, this.context.viewModel, primaryPos);
const viewSelectionStart = primaryCursor.viewState.selectionStart.getStartPosition();
const viewPosition = primaryCursor.viewState.position;
return {
isReal: false,
fromViewLineNumber: viewLineNumber,
fromViewVisualColumn: viewVisualColumn,
toViewLineNumber: viewLineNumber,
toViewVisualColumn: viewVisualColumn,
fromViewLineNumber: viewSelectionStart.lineNumber,
fromViewVisualColumn: CursorColumns.visibleColumnFromColumn2(this.context.config, this.context.viewModel, viewSelectionStart),
toViewLineNumber: viewPosition.lineNumber,
toViewVisualColumn: CursorColumns.visibleColumnFromColumn2(this.context.config, this.context.viewModel, viewPosition),
};
}

View File

@@ -494,7 +494,7 @@ export interface CompletionItem {
preselect?: boolean;
/**
* A string or snippet that should be inserted in a document when selecting
* this completion. When `falsy` the [label](#CompletionItem.label)
* this completion.
* is used.
*/
insertText: string;
@@ -1373,7 +1373,7 @@ export interface RenameProvider {
*/
export interface AuthenticationSession {
id: string;
accessToken(): Promise<string>;
getAccessToken(): Thenable<string>;
accountName: string;
}

View File

@@ -199,7 +199,7 @@ export enum EditorOption {
folding = 31,
foldingStrategy = 32,
foldingHighlight = 33,
unfoldOnClickInEmptyContent = 34,
unfoldOnClickAfterEndOfLine = 34,
fontFamily = 35,
fontInfo = 36,
fontLigatures = 37,

View File

@@ -161,6 +161,7 @@ class ExecCommandPasteAction extends ExecCommandAction {
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.CtrlCmd | KeyCode.KEY_V,
win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_V, secondary: [KeyMod.Shift | KeyCode.Insert] },
linux: { primary: KeyMod.CtrlCmd | KeyCode.KEY_V, secondary: [KeyMod.Shift | KeyCode.Insert] },
weight: KeybindingWeight.EditorContrib
};
// Do not bind paste keybindings in the browser,

View File

@@ -164,7 +164,7 @@ export async function applyCodeAction(
});
if (action.edit) {
await bulkEditService.apply(action.edit, { editor });
await bulkEditService.apply(action.edit, { editor, label: action.title });
}
if (action.command) {

View File

@@ -62,7 +62,7 @@ export class FoldingController extends Disposable implements IEditorContribution
private readonly editor: ICodeEditor;
private _isEnabled: boolean;
private _useFoldingProviders: boolean;
private _unfoldOnClickInEmptyContent: boolean;
private _unfoldOnClickAfterEndOfLine: boolean;
private readonly foldingDecorationProvider: FoldingDecorationProvider;
@@ -92,7 +92,7 @@ export class FoldingController extends Disposable implements IEditorContribution
const options = this.editor.getOptions();
this._isEnabled = options.get(EditorOption.folding);
this._useFoldingProviders = options.get(EditorOption.foldingStrategy) !== 'indentation';
this._unfoldOnClickInEmptyContent = options.get(EditorOption.unfoldOnClickInEmptyContent);
this._unfoldOnClickAfterEndOfLine = options.get(EditorOption.unfoldOnClickAfterEndOfLine);
this.foldingModel = null;
this.hiddenRangeModel = null;
@@ -114,8 +114,7 @@ export class FoldingController extends Disposable implements IEditorContribution
this._register(this.editor.onDidChangeConfiguration((e: ConfigurationChangedEvent) => {
if (e.hasChanged(EditorOption.folding)) {
const options = this.editor.getOptions();
this._isEnabled = options.get(EditorOption.folding);
this._isEnabled = this.editor.getOptions().get(EditorOption.folding);
this.foldingEnabled.set(this._isEnabled);
this.onModelChanged();
}
@@ -126,12 +125,11 @@ export class FoldingController extends Disposable implements IEditorContribution
this.onModelContentChanged();
}
if (e.hasChanged(EditorOption.foldingStrategy)) {
const options = this.editor.getOptions();
this._useFoldingProviders = options.get(EditorOption.foldingStrategy) !== 'indentation';
this._useFoldingProviders = this.editor.getOptions().get(EditorOption.foldingStrategy) !== 'indentation';
this.onFoldingStrategyChanged();
}
if (e.hasChanged(EditorOption.unfoldOnClickInEmptyContent)) {
this._unfoldOnClickInEmptyContent = options.get(EditorOption.unfoldOnClickInEmptyContent);
if (e.hasChanged(EditorOption.unfoldOnClickAfterEndOfLine)) {
this._unfoldOnClickAfterEndOfLine = this.editor.getOptions().get(EditorOption.unfoldOnClickAfterEndOfLine);
}
}));
this.onModelChanged();
@@ -370,7 +368,7 @@ export class FoldingController extends Disposable implements IEditorContribution
iconClicked = true;
break;
case MouseTargetType.CONTENT_EMPTY: {
if (this._unfoldOnClickInEmptyContent && this.hiddenRangeModel.hasRanges()) {
if (this._unfoldOnClickAfterEndOfLine && this.hiddenRangeModel.hasRanges()) {
const data = e.target.detail as IEmptyContentData;
if (!data.isAfterLines) {
break;

View File

@@ -338,9 +338,8 @@ export class GotoDefinitionAtPositionEditorContribution implements IEditorContri
private gotoDefinition(position: Position, openToSide: boolean): Promise<any> {
this.editor.setPosition(position);
const definitionLinkOpensInPeek = this.editor.getOption(EditorOption.definitionLinkOpensInPeek);
return this.editor.invokeWithinContext((accessor) => {
const canPeek = definitionLinkOpensInPeek && !this.isInPeekEditor(accessor);
const canPeek = !openToSide && this.editor.getOption(EditorOption.definitionLinkOpensInPeek) && !this.isInPeekEditor(accessor);
const action = new DefinitionAction({ openToSide, openInPeek: canPeek, muteMessage: true }, { alias: '', label: '', id: '', precondition: undefined });
return action.run(accessor, this.editor);
});

View File

@@ -77,8 +77,8 @@ export class LinksList extends Disposable {
const newLinks = list.links.map(link => new Link(link, provider));
links = LinksList._union(links, newLinks);
// register disposables
if (isDisposable(provider)) {
this._register(provider);
if (isDisposable(list)) {
this._register(list);
}
}
this.links = links;

View File

@@ -206,7 +206,8 @@ class RenameController implements IEditorContribution {
this._bulkEditService.apply(renameResult, {
editor: this.editor,
showPreview: inputFieldResult.wantsPreview,
label: nls.localize('label', "Renaming '{0}'", loc?.text)
label: nls.localize('label', "Renaming '{0}'", loc?.text),
quotableLabel: nls.localize('quotableLabel', "Renaming {0}", loc?.text),
}).then(result => {
if (result.ariaSummary) {
alert(nls.localize('aria', "Successfully renamed '{0}' to '{1}'. Summary: {2}", loc!.text, inputFieldResult.newName, result.ariaSummary));

View File

@@ -234,7 +234,7 @@
flex-shrink: 0;
}
.monaco-editor .suggest-widget .monaco-list .monaco-list-row:not(.string-label) > .contents > .main > .left > .monaco-icon-label {
max-width: 80%;
max-width: 100%;
}
.monaco-editor .suggest-widget .monaco-list .monaco-list-row.string-label > .contents > .main > .left > .monaco-icon-label {
flex-shrink: 1;
@@ -392,6 +392,10 @@
word-wrap: break-word;
}
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .docs.markdown-docs .codicon {
vertical-align: sub;
}
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > p:empty {
display: none;
}

View File

@@ -552,7 +552,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
this.messageElement = append(this.element, $('.message'));
this.listElement = append(this.element, $('.tree'));
const applyStatusBarStyle = () => toggleClass(this.element, 'with-status-bar', !this.editor.getOption(EditorOption.suggest).hideStatusBar);
const applyStatusBarStyle = () => toggleClass(this.element, 'with-status-bar', this.editor.getOption(EditorOption.suggest).statusBar.visible);
applyStatusBarStyle();
this.statusBarElement = append(this.element, $('.suggest-status-bar'));

View File

@@ -33,7 +33,7 @@ interface IEditorLayoutProviderOpts {
readonly minimapSide: 'left' | 'right';
readonly minimapRenderCharacters: boolean;
readonly minimapMaxColumn: number;
minimapMode?: 'actual' | 'cover' | 'contain';
minimapSize?: 'proportional' | 'fill' | 'fit';
readonly pixelRatio: number;
}
@@ -47,7 +47,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
options._write(EditorOption.folding, false);
const minimapOptions: EditorMinimapOptions = {
enabled: input.minimap,
mode: input.minimapMode || 'actual',
size: input.minimapSize || 'proportional',
side: input.minimapSide,
renderCharacters: input.minimapRenderCharacters,
maxColumn: input.minimapMaxColumn,
@@ -978,7 +978,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
minimapSide: 'right',
minimapRenderCharacters: true,
minimapMaxColumn: 150,
minimapMode: 'cover',
minimapSize: 'fill',
pixelRatio: 2,
}, {
width: 1000,
@@ -1042,7 +1042,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
minimapSide: 'right',
minimapRenderCharacters: true,
minimapMaxColumn: 150,
minimapMode: 'cover',
minimapSize: 'fill',
pixelRatio: 2,
}, {
width: 1000,
@@ -1106,7 +1106,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
minimapSide: 'right',
minimapRenderCharacters: true,
minimapMaxColumn: 150,
minimapMode: 'contain',
minimapSize: 'fit',
pixelRatio: 2,
}, {
width: 1000,
@@ -1170,7 +1170,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
minimapSide: 'right',
minimapRenderCharacters: true,
minimapMaxColumn: 150,
minimapMode: 'contain',
minimapSize: 'fit',
pixelRatio: 2,
}, {
width: 1000,

19
src/vs/monaco.d.ts vendored
View File

@@ -3035,7 +3035,7 @@ declare namespace monaco.editor {
* Controls whether clicking on the empty content after a folded line will unfold the line.
* Defaults to false.
*/
unfoldOnClickInEmptyContent?: boolean;
unfoldOnClickAfterEndOfLine?: boolean;
/**
* Enable highlighting of matching brackets.
* Defaults to 'always'.
@@ -3448,7 +3448,7 @@ declare namespace monaco.editor {
* Control the minimap rendering mode.
* Defaults to 'actual'.
*/
mode?: 'actual' | 'cover' | 'contain';
size?: 'proportional' | 'fill' | 'fit';
/**
* Control the rendering of the minimap slider.
* Defaults to 'mouseover'.
@@ -3758,9 +3758,14 @@ declare namespace monaco.editor {
*/
showSnippets?: boolean;
/**
* Controls the visibility of the status bar at the bottom of the suggest widget.
* Status bar related settings.
*/
hideStatusBar?: boolean;
statusBar?: {
/**
* Controls the visibility of the status bar at the bottom of the suggest widget.
*/
visible?: boolean;
};
}
export type InternalSuggestOptions = Readonly<Required<ISuggestOptions>>;
@@ -3829,7 +3834,7 @@ declare namespace monaco.editor {
folding = 31,
foldingStrategy = 32,
foldingHighlight = 33,
unfoldOnClickInEmptyContent = 34,
unfoldOnClickAfterEndOfLine = 34,
fontFamily = 35,
fontInfo = 36,
fontLigatures = 37,
@@ -3945,7 +3950,7 @@ declare namespace monaco.editor {
folding: IEditorOption<EditorOption.folding, boolean>;
foldingStrategy: IEditorOption<EditorOption.foldingStrategy, 'auto' | 'indentation'>;
foldingHighlight: IEditorOption<EditorOption.foldingHighlight, boolean>;
unfoldOnClickInEmptyContent: IEditorOption<EditorOption.unfoldOnClickInEmptyContent, boolean>;
unfoldOnClickAfterEndOfLine: IEditorOption<EditorOption.unfoldOnClickAfterEndOfLine, boolean>;
fontFamily: IEditorOption<EditorOption.fontFamily, string>;
fontInfo: IEditorOption<EditorOption.fontInfo, FontInfo>;
fontLigatures2: IEditorOption<EditorOption.fontLigatures, string>;
@@ -5451,7 +5456,7 @@ declare namespace monaco.languages {
preselect?: boolean;
/**
* A string or snippet that should be inserted in a document when selecting
* this completion. When `falsy` the [label](#CompletionItem.label)
* this completion.
* is used.
*/
insertText: string;

View File

@@ -112,7 +112,7 @@ export class EnvironmentService implements IEnvironmentService {
get settingsResource(): URI { return resources.joinPath(this.userRoamingDataHome, 'settings.json'); }
@memoize
get userDataSyncHome(): URI { return resources.joinPath(this.userRoamingDataHome, '.sync'); }
get userDataSyncHome(): URI { return resources.joinPath(this.userRoamingDataHome, 'sync'); }
@memoize
get settingsSyncPreviewResource(): URI { return resources.joinPath(this.userDataSyncHome, 'settings.json'); }

View File

@@ -101,6 +101,12 @@ export interface INotification extends INotificationProperties {
* this usecase and much easier to use!
*/
actions?: INotificationActions;
/**
* The initial set of progress properties for the notification. To update progress
* later on, access the `INotificationHandle.progress` property.
*/
progress?: INotificationProgressProperties;
}
export interface INotificationActions {
@@ -119,6 +125,24 @@ export interface INotificationActions {
secondary?: ReadonlyArray<IAction>;
}
export interface INotificationProgressProperties {
/**
* Causes the progress bar to spin infinitley.
*/
infinite?: boolean;
/**
* Indicate the total amount of work.
*/
total?: number;
/**
* Indicate that a specific chunk of work is done.
*/
worked?: number;
}
export interface INotificationProgress {
/**
@@ -149,6 +173,13 @@ export interface INotificationHandle {
*/
readonly onDidClose: Event<void>;
/**
* Will be fired whenever the visibility of the notification changes.
* A notification can either be visible as toast or inside the notification
* center if it is visible.
*/
readonly onDidChangeVisibility: Event<boolean>;
/**
* Allows to indicate progress on the notification even after the
* notification is already visible.

View File

@@ -240,9 +240,36 @@ export class FileStorageDatabase extends Disposable implements IStorageDatabase
private async onDidStorageChangeExternal(): Promise<void> {
const items = await this.doGetItemsFromFile();
// pervious cache, diff for changes
let changed = new Map<string, string>();
let deleted = new Set<string>();
if (this.cache) {
items.forEach((value, key) => {
const existingValue = this.cache?.get(key);
if (existingValue !== value) {
changed.set(key, value);
}
});
this.cache.forEach((_, key) => {
if (!items.has(key)) {
deleted.add(key);
}
});
}
// no previous cache, consider all as changed
else {
changed = items;
}
// Update cache
this.cache = items;
this._onDidChangeItemsExternal.fire({ items });
// Emit as event as needed
if (changed.size > 0 || deleted.size > 0) {
this._onDidChangeItemsExternal.fire({ changed, deleted });
}
}
async getItems(): Promise<Map<string, string>> {

View File

@@ -24,15 +24,16 @@ interface ISerializableUpdateRequest {
}
interface ISerializableItemsChangeEvent {
items: Item[];
changed?: Item[];
deleted?: Key[];
}
export class GlobalStorageDatabaseChannel extends Disposable implements IServerChannel {
private static readonly STORAGE_CHANGE_DEBOUNCE_TIME = 100;
private readonly _onDidChangeItems: Emitter<ISerializableItemsChangeEvent> = this._register(new Emitter<ISerializableItemsChangeEvent>());
readonly onDidChangeItems: Event<ISerializableItemsChangeEvent> = this._onDidChangeItems.event;
private readonly _onDidChangeItems = this._register(new Emitter<ISerializableItemsChangeEvent>());
readonly onDidChangeItems = this._onDidChangeItems.event;
private whenReady: Promise<void>;
@@ -99,15 +100,18 @@ export class GlobalStorageDatabaseChannel extends Disposable implements IServerC
}
private serializeEvents(events: IStorageChangeEvent[]): ISerializableItemsChangeEvent {
const items = new Map<Key, Value>();
const changed = new Map<Key, Value>();
const deleted = new Set<Key>();
events.forEach(event => {
const existing = this.storageMainService.get(event.key);
if (typeof existing === 'string') {
items.set(event.key, existing);
changed.set(event.key, existing);
} else {
deleted.add(event.key);
}
});
return { items: mapToSerializable(items) };
return { changed: mapToSerializable(changed), deleted: values(deleted) };
}
listen(_: unknown, event: string): Event<any> {
@@ -170,8 +174,11 @@ export class GlobalStorageDatabaseChannelClient extends Disposable implements IS
}
private onDidChangeItemsOnMain(e: ISerializableItemsChangeEvent): void {
if (Array.isArray(e.items)) {
this._onDidChangeItemsExternal.fire({ items: serializableToMap(e.items) });
if (Array.isArray(e.changed) || Array.isArray(e.deleted)) {
this._onDidChangeItemsExternal.fire({
changed: e.changed ? serializableToMap(e.changed) : undefined,
deleted: e.deleted ? new Set<string>(e.deleted) : undefined
});
}
}

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Event, Emitter } from 'vs/base/common/event';
import { Emitter } from 'vs/base/common/event';
import { ILogService, LogLevel } from 'vs/platform/log/common/log';
import { IWorkspaceStorageChangeEvent, IStorageService, StorageScope, IWillSaveStateEvent, WillSaveStateReason, logStorage } from 'vs/platform/storage/common/storage';
import { SQLiteStorageDatabase, ISQLiteStorageDatabaseLoggingOptions } from 'vs/base/parts/storage/node/storage';
@@ -25,11 +25,11 @@ export class NativeStorageService extends Disposable implements IStorageService
private static readonly WORKSPACE_STORAGE_NAME = 'state.vscdb';
private static readonly WORKSPACE_META_NAME = 'workspace.json';
private readonly _onDidChangeStorage: Emitter<IWorkspaceStorageChangeEvent> = this._register(new Emitter<IWorkspaceStorageChangeEvent>());
readonly onDidChangeStorage: Event<IWorkspaceStorageChangeEvent> = this._onDidChangeStorage.event;
private readonly _onDidChangeStorage = this._register(new Emitter<IWorkspaceStorageChangeEvent>());
readonly onDidChangeStorage = this._onDidChangeStorage.event;
private readonly _onWillSaveState: Emitter<IWillSaveStateEvent> = this._register(new Emitter<IWillSaveStateEvent>());
readonly onWillSaveState: Event<IWillSaveStateEvent> = this._onWillSaveState.event;
private readonly _onWillSaveState = this._register(new Emitter<IWillSaveStateEvent>());
readonly onWillSaveState = this._onWillSaveState.event;
private globalStorage: IStorage;

View File

@@ -260,7 +260,7 @@ export class UndoRedoService implements IUndoRedoService {
this._splitPastWorkspaceElement(element, element.removedResources.set);
const message = nls.localize('cannotWorkspaceUndo', "Could not undo '{0}' across all files. {1}", element.label, element.removedResources.createMessage());
this._notificationService.info(message);
return;
return this.undo(resource);
}
// this must be the last past element in all the impacted resources!
@@ -281,18 +281,25 @@ export class UndoRedoService implements IUndoRedoService {
const paths = cannotUndoDueToResources.map(r => r.scheme === Schemas.file ? r.fsPath : r.path);
const message = nls.localize('cannotWorkspaceUndoDueToChanges', "Could not undo '{0}' across all files because changes were made to {1}", element.label, paths.join(', '));
this._notificationService.info(message);
return;
return this.undo(resource);
}
return this._dialogService.show(
Severity.Info,
nls.localize('confirmWorkspace', "Would you like to undo '{0}' across all files?", element.label),
[
nls.localize('ok', "Yes, change {0} files.", affectedEditStacks.length),
nls.localize('nok', "No, change only this file.")
]
nls.localize('ok', "Undo In {0} Files", affectedEditStacks.length),
nls.localize('nok', "Undo This File"),
nls.localize('cancel', "Cancel"),
],
{
cancelId: 2
}
).then((result) => {
if (result.choice === 0) {
if (result.choice === 2) {
// cancel
return;
} else if (result.choice === 0) {
for (const editStack of affectedEditStacks) {
editStack.past.pop();
editStack.future.push(element);
@@ -344,7 +351,7 @@ export class UndoRedoService implements IUndoRedoService {
this._splitFutureWorkspaceElement(element, element.removedResources.set);
const message = nls.localize('cannotWorkspaceRedo', "Could not redo '{0}' across all files. {1}", element.label, element.removedResources.createMessage());
this._notificationService.info(message);
return;
return this.redo(resource);
}
// this must be the last future element in all the impacted resources!
@@ -365,7 +372,7 @@ export class UndoRedoService implements IUndoRedoService {
const paths = cannotRedoDueToResources.map(r => r.scheme === Schemas.file ? r.fsPath : r.path);
const message = nls.localize('cannotWorkspaceRedoDueToChanges', "Could not redo '{0}' across all files because changes were made to {1}", element.label, paths.join(', '));
this._notificationService.info(message);
return;
return this.redo(resource);
}
for (const editStack of affectedEditStacks) {

View File

@@ -68,7 +68,7 @@ export abstract class AbstractSynchroniser extends Disposable {
) {
super();
this.syncFolder = joinPath(environmentService.userDataSyncHome, source);
this.lastSyncResource = joinPath(this.syncFolder, `.lastSync${source}.json`);
this.lastSyncResource = joinPath(this.syncFolder, `lastSync${source}.json`);
this.cleanUpDelayer = new ThrottledDelayer(50);
this.cleanUpBackup();
}
@@ -202,7 +202,7 @@ export abstract class AbstractSynchroniser extends Disposable {
}
protected async backupLocal(content: VSBuffer): Promise<void> {
const resource = joinPath(this.syncFolder, toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, ''));
const resource = joinPath(this.syncFolder, `${toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')}.json`);
try {
await this.fileService.writeFile(resource, content);
} catch (e) {
@@ -213,9 +213,13 @@ export abstract class AbstractSynchroniser extends Disposable {
private async cleanUpBackup(): Promise<void> {
try {
if (!(await this.fileService.exists(this.syncFolder))) {
return;
}
const stat = await this.fileService.resolve(this.syncFolder);
if (stat.children) {
const all = stat.children.filter(stat => stat.isFile && /^\d{8}T\d{6}$/.test(stat.name)).sort();
const all = stat.children.filter(stat => stat.isFile && /^\d{8}T\d{6}(\.json)?$/.test(stat.name)).sort();
console.log(all.map(a => a.name));
const backUpMaxAge = 1000 * 60 * 60 * 24 * (this.configurationService.getValue<number>('sync.localBackupDuration') || 30 /* Default 30 days */);
let toDelete = all.filter(stat => {
const ctime = stat.ctime || new Date(

View File

@@ -194,7 +194,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
if (added.length || removed.length || updated.length) {
// back up all disabled or market place extensions
const backUpExtensions = localExtensions.filter(e => e.disabled || !!e.identifier.uuid);
await this.backupLocal(VSBuffer.fromString(JSON.stringify(backUpExtensions)));
await this.backupLocal(VSBuffer.fromString(JSON.stringify(backUpExtensions, null, '\t')));
skippedExtensions = await this.updateLocalExtensions(added, removed, updated, skippedExtensions);
}

View File

@@ -165,7 +165,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
if (local) {
// update local
this.logService.trace('UI State: Updating local ui state...');
await this.backupLocal(VSBuffer.fromString(JSON.stringify(localUserData)));
await this.backupLocal(VSBuffer.fromString(JSON.stringify(localUserData, null, '\t')));
await this.writeLocalGlobalState(local);
this.logService.info('UI State: Updated local ui state');
}

View File

@@ -8,6 +8,11 @@ import { Event, Emitter } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { IUserDataSyncLogService, IUserDataSyncService, SyncStatus, IUserDataAutoSyncService, UserDataSyncError, UserDataSyncErrorCode, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync';
import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
type AutoSyncTriggerClassification = {
source: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
};
export class UserDataAutoSyncService extends Disposable implements IUserDataAutoSyncService {
@@ -25,6 +30,7 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto
@IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService,
@IUserDataSyncLogService private readonly logService: IUserDataSyncLogService,
@IAuthenticationTokenService private readonly authTokenService: IAuthenticationTokenService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
) {
super();
this.updateEnablement(false, true);
@@ -32,7 +38,7 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto
this._register(Event.any<any>(authTokenService.onDidChangeToken)(() => this.updateEnablement(true, true)));
this._register(Event.any<any>(userDataSyncService.onDidChangeStatus)(() => this.updateEnablement(true, true)));
this._register(this.userDataSyncEnablementService.onDidChangeEnablement(() => this.updateEnablement(true, false)));
this._register(this.userDataSyncEnablementService.onDidChangeResourceEnablement(() => this.triggerAutoSync()));
this._register(this.userDataSyncEnablementService.onDidChangeResourceEnablement(() => this.triggerAutoSync(['resourceEnablement'])));
}
private async updateEnablement(stopIfDisabled: boolean, auto: boolean): Promise<void> {
@@ -99,7 +105,8 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto
this.successiveFailures = 0;
}
async triggerAutoSync(): Promise<void> {
async triggerAutoSync(sources: string[]): Promise<void> {
sources.forEach(source => this.telemetryService.publicLog2<{ source: string }, AutoSyncTriggerClassification>('sync/triggerAutoSync', { source }));
if (this.enabled) {
return this.syncDelayer.trigger(() => {
this.logService.info('Auto Sync: Triggered.');

View File

@@ -278,7 +278,7 @@ export interface IUserDataSyncService {
readonly conflictsSources: SyncSource[];
readonly onDidChangeConflicts: Event<SyncSource[]>;
readonly onDidChangeLocal: Event<void>;
readonly onDidChangeLocal: Event<SyncSource>;
readonly onSyncErrors: Event<[SyncSource, UserDataSyncError][]>;
readonly lastSyncTime: number | undefined;
@@ -299,7 +299,7 @@ export const IUserDataAutoSyncService = createDecorator<IUserDataAutoSyncService
export interface IUserDataAutoSyncService {
_serviceBrand: any;
readonly onError: Event<UserDataSyncError>;
triggerAutoSync(): Promise<void>;
triggerAutoSync(sources: string[]): Promise<void>;
}
export const IUserDataSyncUtilService = createDecorator<IUserDataSyncUtilService>('IUserDataSyncUtilService');

View File

@@ -86,7 +86,7 @@ export class UserDataAutoSyncChannel implements IServerChannel {
call(context: any, command: string, args?: any): Promise<any> {
switch (command) {
case 'triggerAutoSync': return this.service.triggerAutoSync();
case 'triggerAutoSync': return this.service.triggerAutoSync(args[0]);
}
throw new Error('Invalid call');
}

View File

@@ -34,7 +34,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
private _onDidChangeStatus: Emitter<SyncStatus> = this._register(new Emitter<SyncStatus>());
readonly onDidChangeStatus: Event<SyncStatus> = this._onDidChangeStatus.event;
readonly onDidChangeLocal: Event<void>;
readonly onDidChangeLocal: Event<SyncSource>;
private _conflictsSources: SyncSource[] = [];
get conflictsSources(): SyncSource[] { return this._conflictsSources; }
@@ -74,7 +74,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
}
this._lastSyncTime = this.storageService.getNumber(LAST_SYNC_TIME_KEY, StorageScope.GLOBAL, undefined);
this.onDidChangeLocal = Event.any(...this.synchronisers.map(s => s.onDidChangeLocal));
this.onDidChangeLocal = Event.any(...this.synchronisers.map(s => Event.map(s.onDidChangeLocal, () => s.source)));
}
async pull(): Promise<void> {

View File

@@ -8,6 +8,7 @@ import { Event } from 'vs/base/common/event';
import { IElectronService } from 'vs/platform/electron/node/electron';
import { UserDataAutoSyncService as BaseUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataAutoSyncService';
import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
export class UserDataAutoSyncService extends BaseUserDataAutoSyncService {
@@ -17,15 +18,15 @@ export class UserDataAutoSyncService extends BaseUserDataAutoSyncService {
@IElectronService electronService: IElectronService,
@IUserDataSyncLogService logService: IUserDataSyncLogService,
@IAuthenticationTokenService authTokenService: IAuthenticationTokenService,
@ITelemetryService telemetryService: ITelemetryService,
) {
super(userDataSyncEnablementService, userDataSyncService, logService, authTokenService);
super(userDataSyncEnablementService, userDataSyncService, logService, authTokenService, telemetryService);
// Sync immediately if there is a local change.
this._register(Event.debounce(Event.any<any>(
electronService.onWindowFocus,
electronService.onWindowOpen,
this._register(Event.debounce<string, string[]>(Event.any<string>(
Event.map(electronService.onWindowFocus, () => 'windowFocus'),
Event.map(electronService.onWindowOpen, () => 'windowOpen'),
userDataSyncService.onDidChangeLocal,
), () => undefined, 500)(() => this.triggerAutoSync()));
), (last, source) => last ? [...last, source] : [source], 1000)(sources => this.triggerAutoSync(sources)));
}
}

29
src/vs/vscode.d.ts vendored
View File

@@ -1500,7 +1500,7 @@ declare module 'vscode' {
/**
* A file system watcher notifies about changes to files and folders
* on disk.
* on disk or from other [FileSystemProviders](#FileSystemProvider).
*
* To get an instance of a `FileSystemWatcher` use
* [createFileSystemWatcher](#workspace.createFileSystemWatcher).
@@ -1806,7 +1806,7 @@ declare module 'vscode' {
placeHolder?: string;
/**
* Set to `true` to show a password prompt that will not show the typed value.
* Controls if a password input is shown. Password input hides the typed text.
*/
password?: boolean;
@@ -2020,7 +2020,7 @@ declare module 'vscode' {
* Base kind for source actions: `source`
*
* Source code actions apply to the entire file. They must be explicitly requested and will not show in the
* normal [light bulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action) menu. Source actions
* normal [lightbulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action) menu. Source actions
* can be run on save using `editor.codeActionsOnSave` and are also shown in the `source` context menu.
*/
static readonly Source: CodeActionKind;
@@ -2086,7 +2086,7 @@ declare module 'vscode' {
/**
* Requested kind of actions to return.
*
* Actions not of this kind are filtered out before being shown by the lightbulb.
* Actions not of this kind are filtered out before being shown by the [lightbulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action).
*/
readonly only?: CodeActionKind;
}
@@ -2138,7 +2138,15 @@ declare module 'vscode' {
/**
* Marks that the code action cannot currently be applied.
*
* Disabled code actions will be surfaced in the refactor UI but cannot be applied.
* - Disabled code actions are not shown in automatic [lightbulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action)
* code action menu.
*
* - Disabled actions are shown as faded out in the code action menu when the user request a more specific type
* of code action, such as refactorings.
*
* - If the user has a [keybinding](https://code.visualstudio.com/docs/editor/refactoring#_keybindings-for-code-actions)
* that auto applies a code action and only a disabled code actions are returned, VS Code will show the user a
* message with `reason` in the editor.
*/
disabled?: {
/**
@@ -2163,7 +2171,7 @@ declare module 'vscode' {
/**
* The code action interface defines the contract between extensions and
* the [light bulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action) feature.
* the [lightbulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action) feature.
*
* A code action can be any command that is [known](#commands.getCommands) to the system.
*/
@@ -2495,16 +2503,18 @@ declare module 'vscode' {
/**
* The evaluatable expression provider interface defines the contract between extensions and
* the debug hover.
* the debug hover. In this contract the provider returns an evaluatable expression for a given position
* in a document and VS Code evaluates this expression in the active debug session and shows the result in a debug hover.
*/
export interface EvaluatableExpressionProvider {
/**
* Provide an evaluatable expression for the given document and position.
* VS Code will evaluate this expression in the active debug session and will show the result in the debug hover.
* The expression can be implicitly specified by the range in the underlying document or by explicitly returning an expression.
*
* @param document The document in which the debug hover is opened.
* @param position The position in the document where the debug hover is opened.
* @param document The document for which the debug hover is about to appear.
* @param position The line and character position in the document where the debug hover is about to appear.
* @param token A cancellation token.
* @return An EvaluatableExpression or a thenable that resolves to such. The lack of a result can be
* signaled by returning `undefined` or `null`.
@@ -9174,6 +9184,7 @@ declare module 'vscode' {
/**
* Register a provider that locates evaluatable expressions in text documents.
* VS Code will evaluate the expression in the active debug session and will show the result in the debug hover.
*
* If multiple providers are registered for a language an arbitrary provider will be used.
*

View File

@@ -20,7 +20,7 @@ declare module 'vscode' {
export interface AuthenticationSession {
id: string;
accessToken(): Promise<string>;
getAccessToken(): Thenable<string>;
accountName: string;
scopes: string[]
}
@@ -58,13 +58,13 @@ declare module 'vscode' {
/**
* Returns an array of current sessions.
*/
getSessions(): Promise<ReadonlyArray<AuthenticationSession>>;
getSessions(): Thenable<ReadonlyArray<AuthenticationSession>>;
/**
* Prompts a user to login.
*/
login(scopes: string[]): Promise<AuthenticationSession>;
logout(sessionId: string): Promise<void>;
login(scopes: string[]): Thenable<AuthenticationSession>;
logout(sessionId: string): Thenable<void>;
}
export namespace authentication {
@@ -1273,14 +1273,14 @@ declare module 'vscode' {
* in an operation that takes time to complete, your extension may decide to finish the ongoing backup rather
* than cancelling it to ensure that VS Code has some valid backup.
*/
backup(cancellation: CancellationToken): Thenable<boolean>;
backup(cancellation: CancellationToken): Thenable<void>;
}
/**
* Represents a custom document for a custom webview editor.
*
* Custom documents are only used within a given `WebviewCustomEditorProvider`. The lifecycle of a
* `WebviewEditorCustomDocument` is managed by VS Code. When more more references remain to a given `WebviewEditorCustomDocument`
* Custom documents are only used within a given `CustomEditorProvider`. The lifecycle of a
* `CustomDocument` is managed by VS Code. When more more references remain to a given `CustomDocument`
* then it is disposed of.
*
* @param UserDataType Type of custom object that extensions can store on the document.
@@ -1297,7 +1297,7 @@ declare module 'vscode' {
readonly uri: Uri;
/**
* Event fired when there are no more references to the `WebviewEditorCustomDocument`.
* Event fired when there are no more references to the `CustomDocument`.
*/
readonly onDidDispose: Event<void>;
@@ -1313,7 +1313,7 @@ declare module 'vscode' {
/**
* Provider for webview editors that use a custom data model.
*
* Custom webview editors use [`WebviewEditorCustomDocument`](#WebviewEditorCustomDocument) as their data model.
* Custom webview editors use [`CustomDocument`](#CustomDocument) as their data model.
* This gives extensions full control over actions such as edit, save, and backup.
*
* You should use custom text based editors when dealing with binary files or more complex scenarios. For simple text
@@ -1321,24 +1321,26 @@ declare module 'vscode' {
*/
export interface CustomEditorProvider {
/**
* Create the model for a given
* Resolve the model for a given resource.
*
* @param document Resource being resolved.
* @param document Document to resolve.
*
* @return The capabilities of the resolved document.
*/
resolveCustomDocument(document: CustomDocument): Thenable<CustomEditorCapabilities>;
/**
* Resolve a webview editor for a given resource.
*
* To resolve a webview editor, a provider must fill in its initial html content and hook up all
* To resolve a webview editor, the provider must fill in its initial html content and hook up all
* the event listeners it is interested it. The provider should also take ownership of the passed in `WebviewPanel`.
*
* @param document Document for resource being resolved.
* @param webview Webview being resolved. The provider should take ownership of this webview.
* @param document Document for the resource being resolved.
* @param webviewPanel Webview to resolve. The provider should take ownership of this webview.
*
* @return Thenable indicating that the webview editor has been resolved.
*/
resolveCustomEditor(document: CustomDocument, webview: WebviewPanel): Thenable<void>;
resolveCustomEditor(document: CustomDocument, webviewPanel: WebviewPanel): Thenable<void>;
}
/**
@@ -1349,7 +1351,7 @@ declare module 'vscode' {
* undo and backup. The provider is responsible for synchronizing text changes between the webview and the `TextDocument`.
*
* You should use text based webview editors when dealing with text based file formats, such as `xml` or `json`.
* For binary files or more specialized use cases, see [WebviewCustomEditorProvider](#WebviewCustomEditorProvider).
* For binary files or more specialized use cases, see [CustomEditorProvider](#CustomEditorProvider).
*/
export interface CustomTextEditorProvider {
/**
@@ -1359,11 +1361,11 @@ declare module 'vscode' {
* the event listeners it is interested it. The provider should also take ownership of the passed in `WebviewPanel`.
*
* @param document Resource being resolved.
* @param webview Webview being resolved. The provider should take ownership of this webview.
* @param webviewPanel Webview to resolve. The provider should take ownership of this webview.
*
* @return Thenable indicating that the webview editor has been resolved.
*/
resolveCustomTextEditor(document: TextDocument, webview: WebviewPanel): Thenable<void>;
resolveCustomTextEditor(document: TextDocument, webviewPanel: WebviewPanel): Thenable<void>;
}
namespace window {
@@ -1680,9 +1682,10 @@ declare module 'vscode' {
*
* The documentation is shown in the code actions menu if either:
*
* - Code actions of `kind` are requested by VS Code. Note that in this case, we always pick the most specific
* documentation. For example, if documentation for both `Refactor` and `RefactorExtract` is provided, and we
* request code actions for `RefactorExtract`, we prefer the more specific documentation for `RefactorExtract`.
* - Code actions of `kind` are requested by VS Code. In this case, VS Code will show the documentation that
* most closely matches the requested code action kind. For example, if a provider has documentation for
* both `Refactor` and `RefactorExtract`, when the user requests code actions for `RefactorExtract`,
* VS Code will use the documentation for `RefactorExtract` intead of the documentation for `Refactor`.
*
* - Any code actions of `kind` are returned by the provider.
*/
@@ -1703,7 +1706,10 @@ declare module 'vscode' {
*/
export interface OpenDialogOptions {
/**
* Dialog title
* Dialog title.
*
* Depending on the underlying operating system this parameter might be ignored, since some
* systems do not present title on open dialogs.
*/
title?: string;
}
@@ -1713,7 +1719,10 @@ declare module 'vscode' {
*/
export interface SaveDialogOptions {
/**
* Dialog title
* Dialog title.
*
* Depending on the underlying operating system this parameter might be ignored, since some
* systems do not present title on save dialogs.
*/
title?: string;
}

View File

@@ -25,7 +25,7 @@ export class MainThreadAuthenticationProvider {
return {
id: session.id,
accountName: session.accountName,
accessToken: () => this._proxy.$getSessionAccessToken(this.id, session.id)
getAccessToken: () => this._proxy.$getSessionAccessToken(this.id, session.id)
};
});
}
@@ -35,7 +35,7 @@ export class MainThreadAuthenticationProvider {
return {
id: session.id,
accountName: session.accountName,
accessToken: () => this._proxy.$getSessionAccessToken(this.id, session.id)
getAccessToken: () => this._proxy.$getSessionAccessToken(this.id, session.id)
};
});
}
@@ -75,48 +75,52 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
async $getSessionsPrompt(providerId: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean> {
const alwaysAllow = this.storageService.get(`${extensionId}-${providerId}`, StorageScope.GLOBAL);
if (alwaysAllow) {
return true;
return alwaysAllow === 'true';
}
const { choice } = await this.dialogService.show(
const { choice, checkboxChecked } = await this.dialogService.show(
Severity.Info,
nls.localize('confirmAuthenticationAccess', "The extension '{0}' is trying to access authentication information from {1}.", extensionName, providerName),
[nls.localize('cancel', "Cancel"), nls.localize('allow', "Allow"), nls.localize('alwaysAllow', "Always Allow"),],
{ cancelId: 0 }
[nls.localize('cancel', "Cancel"), nls.localize('allow', "Allow")],
{
cancelId: 0,
checkbox: {
label: nls.localize('neverAgain', "Don't Show Again")
}
}
);
switch (choice) {
case 1/** Allow */:
return true;
case 2 /** Always Allow */:
this.storageService.store(`${extensionId}-${providerId}`, 'true', StorageScope.GLOBAL);
return true;
default:
return false;
const allow = choice === 1;
if (checkboxChecked) {
this.storageService.store(`${extensionId}-${providerId}`, allow ? 'true' : 'false', StorageScope.GLOBAL);
}
return allow;
}
async $loginPrompt(providerId: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean> {
const alwaysAllow = this.storageService.get(`${extensionId}-${providerId}`, StorageScope.GLOBAL);
if (alwaysAllow) {
return true;
return alwaysAllow === 'true';
}
const { choice } = await this.dialogService.show(
const { choice, checkboxChecked } = await this.dialogService.show(
Severity.Info,
nls.localize('confirmLogin', "The extension '{0}' wants to sign in using {1}.", extensionName, providerName),
[nls.localize('cancel', "Cancel"), nls.localize('continue', "Continue"), nls.localize('neverAgain', "Don't Show Again")],
{ cancelId: 0 }
[nls.localize('cancel', "Cancel"), nls.localize('continue', "Continue")],
{
cancelId: 0,
checkbox: {
label: nls.localize('neverAgain', "Don't Show Again")
}
}
);
switch (choice) {
case 1/** Allow */:
return true;
case 2 /** Always Allow */:
this.storageService.store(`${extensionId}-${providerId}`, 'true', StorageScope.GLOBAL);
return true;
default:
return false;
const allow = choice === 1;
if (checkboxChecked) {
this.storageService.store(`${extensionId}-${providerId}`, allow ? 'true' : 'false', StorageScope.GLOBAL);
}
return allow;
}
}

View File

@@ -122,7 +122,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
this._register(_webviewWorkbenchService.registerResolver({
canResolve: (webview: WebviewInput) => {
if (webview instanceof CustomEditorInput) {
extensionService.activateByEvent(`onWebviewEditor:${webview.viewType}`);
extensionService.activateByEvent(`onCustomEditor:${webview.viewType}`);
return false;
}

View File

@@ -624,7 +624,7 @@ export interface ExtHostWebviewsShape {
$onSave(resource: UriComponents, viewType: string): Promise<void>;
$onSaveAs(resource: UriComponents, viewType: string, targetResource: UriComponents): Promise<void>;
$backup(resource: UriComponents, viewType: string, cancellation: CancellationToken): Promise<boolean>;
$backup(resource: UriComponents, viewType: string, cancellation: CancellationToken): Promise<void>;
}
export interface MainThreadUrlsShape extends IDisposable {

View File

@@ -34,7 +34,7 @@ export class AuthenticationProviderWrapper implements vscode.AuthenticationProvi
id: session.id,
accountName: session.accountName,
scopes: session.scopes,
accessToken: async () => {
getAccessToken: async () => {
const isAllowed = await this._proxy.$getSessionsPrompt(
this._provider.id,
this.displayName,
@@ -45,7 +45,7 @@ export class AuthenticationProviderWrapper implements vscode.AuthenticationProvi
throw new Error('User did not consent to token access.');
}
return session.accessToken();
return session.getAccessToken();
}
};
});
@@ -60,7 +60,7 @@ export class AuthenticationProviderWrapper implements vscode.AuthenticationProvi
return this._provider.login(scopes);
}
logout(sessionId: string): Promise<void> {
logout(sessionId: string): Thenable<void> {
return this._provider.logout(sessionId);
}
}
@@ -137,7 +137,7 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
const sessions = await authProvider.getSessions();
const session = sessions.find(session => session.id === sessionId);
if (session) {
return session.accessToken();
return session.getAccessToken();
}
throw new Error(`Unable to find session with id: ${sessionId}`);

View File

@@ -71,11 +71,17 @@ export class ExtHostTimeline implements IExtHostTimeline {
scheme: scheme,
onDidChange: undefined,
async provideTimeline(uri: URI, options: TimelineOptions, token: CancellationToken, internalOptions?: { cacheResults?: boolean }) {
timelineDisposables.clear();
// For now, only allow the caching of a single Uri
if (internalOptions?.cacheResults && !itemsBySourceByUriMap.has(getUriKey(uri))) {
itemsBySourceByUriMap.clear();
if (internalOptions?.cacheResults) {
if (options.cursor === undefined) {
timelineDisposables.clear();
}
if (!itemsBySourceByUriMap.has(getUriKey(uri))) {
itemsBySourceByUriMap.clear();
}
} else {
timelineDisposables.clear();
}
const result = await provider.provideTimeline(uri, options, token);

View File

@@ -247,143 +247,160 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa
type EditType = unknown;
class WebviewEditorCustomDocument extends Disposable implements vscode.CustomDocument {
private _currentEditIndex: number = -1;
private _savePoint: number = -1;
private readonly _edits: Array<EditType> = [];
class CustomDocument extends Disposable implements vscode.CustomDocument {
public userData: unknown;
public _capabilities?: vscode.CustomEditorCapabilities = undefined;
constructor(
private readonly _proxy: MainThreadWebviewsShape,
public readonly viewType: string,
public readonly uri: vscode.Uri,
) {
super();
public static create(proxy: MainThreadWebviewsShape, viewType: string, uri: vscode.Uri) {
return Object.seal(new CustomDocument(proxy, viewType, uri));
}
_setCapabilities(capabilities: vscode.CustomEditorCapabilities) {
if (this._capabilities) {
throw new Error('Capabilities already provided');
}
// Explicitly initialize all properties as we seal the object after creation!
this._capabilities = capabilities;
capabilities.editing?.onDidEdit(edit => {
this.pushEdit(edit, this);
});
#currentEditIndex: number = -1;
#savePoint: number = -1;
readonly #edits: Array<EditType> = [];
readonly #proxy: MainThreadWebviewsShape;
readonly #viewType: string;
readonly #uri: vscode.Uri;
#capabilities: vscode.CustomEditorCapabilities | undefined = undefined;
private constructor(proxy: MainThreadWebviewsShape, viewType: string, uri: vscode.Uri) {
super();
this.#proxy = proxy;
this.#viewType = viewType;
this.#uri = uri;
}
dispose() {
this.#onDidDispose.fire();
super.dispose();
}
//#region Public API
#_onDidDispose = this._register(new Emitter<void>());
public readonly onDidDispose = this.#_onDidDispose.event;
public get viewType(): string { return this.#viewType; }
public get uri(): vscode.Uri { return this.#uri; }
#onDidDispose = this._register(new Emitter<void>());
public readonly onDidDispose = this.#onDidDispose.event;
public userData: unknown = undefined;
//#endregion
dispose() {
this.#_onDidDispose.fire();
super.dispose();
//#region Internal
/** @internal*/ _setCapabilities(capabilities: vscode.CustomEditorCapabilities) {
if (this.#capabilities) {
throw new Error('Capabilities already provided');
}
this.#capabilities = capabilities;
capabilities.editing?.onDidEdit(edit => {
this.pushEdit(edit);
});
}
private pushEdit(edit: EditType, trigger: any) {
this.spliceEdits(edit);
this._currentEditIndex = this._edits.length - 1;
this.updateState();
// this._onApplyEdit.fire({ edits: [edit], trigger });
}
private updateState() {
const dirty = this._edits.length > 0 && this._savePoint !== this._currentEditIndex;
this._proxy.$onDidChangeCustomDocumentState(this.uri, this.viewType, { dirty });
}
private spliceEdits(editToInsert?: EditType) {
const start = this._currentEditIndex + 1;
const toRemove = this._edits.length - this._currentEditIndex;
editToInsert
? this._edits.splice(start, toRemove, editToInsert)
: this._edits.splice(start, toRemove);
}
revert() {
/** @internal*/ _revert() {
const editing = this.getEditingCapability();
if (this._currentEditIndex === this._savePoint) {
if (this.#currentEditIndex === this.#savePoint) {
return true;
}
if (this._currentEditIndex >= this._savePoint) {
const editsToUndo = this._edits.slice(this._savePoint, this._currentEditIndex);
if (this.#currentEditIndex >= this.#savePoint) {
const editsToUndo = this.#edits.slice(this.#savePoint, this.#currentEditIndex);
editing.undoEdits(editsToUndo.reverse());
} else if (this._currentEditIndex < this._savePoint) {
const editsToRedo = this._edits.slice(this._currentEditIndex, this._savePoint);
} else if (this.#currentEditIndex < this.#savePoint) {
const editsToRedo = this.#edits.slice(this.#currentEditIndex, this.#savePoint);
editing.applyEdits(editsToRedo);
}
this._currentEditIndex = this._savePoint;
this.#currentEditIndex = this.#savePoint;
this.spliceEdits();
this.updateState();
return true;
}
undo() {
/** @internal*/ _undo() {
const editing = this.getEditingCapability();
if (this._currentEditIndex < 0) {
if (this.#currentEditIndex < 0) {
// nothing to undo
return;
}
const undoneEdit = this._edits[this._currentEditIndex];
--this._currentEditIndex;
const undoneEdit = this.#edits[this.#currentEditIndex];
--this.#currentEditIndex;
editing.undoEdits([undoneEdit]);
this.updateState();
}
redo() {
/** @internal*/ _redo() {
const editing = this.getEditingCapability();
if (this._currentEditIndex >= this._edits.length - 1) {
if (this.#currentEditIndex >= this.#edits.length - 1) {
// nothing to redo
return;
}
++this._currentEditIndex;
const redoneEdit = this._edits[this._currentEditIndex];
++this.#currentEditIndex;
const redoneEdit = this.#edits[this.#currentEditIndex];
editing.applyEdits([redoneEdit]);
this.updateState();
}
save() {
/** @internal*/ _save() {
return this.getEditingCapability().save();
}
saveAs(target: vscode.Uri) {
/** @internal*/ _saveAs(target: vscode.Uri) {
return this.getEditingCapability().saveAs(target);
}
backup(cancellation: CancellationToken) {
/** @internal*/ _backup(cancellation: CancellationToken) {
return this.getEditingCapability().backup(cancellation);
}
//#endregion
private pushEdit(edit: EditType) {
this.spliceEdits(edit);
this.#currentEditIndex = this.#edits.length - 1;
this.updateState();
}
private updateState() {
const dirty = this.#edits.length > 0 && this.#savePoint !== this.#currentEditIndex;
this.#proxy.$onDidChangeCustomDocumentState(this.uri, this.viewType, { dirty });
}
private spliceEdits(editToInsert?: EditType) {
const start = this.#currentEditIndex + 1;
const toRemove = this.#edits.length - this.#currentEditIndex;
editToInsert
? this.#edits.splice(start, toRemove, editToInsert)
: this.#edits.splice(start, toRemove);
}
private getEditingCapability(): vscode.CustomEditorEditingCapability {
if (!this._capabilities?.editing) {
if (!this.#capabilities?.editing) {
throw new Error('Document is not editable');
}
return this._capabilities.editing;
return this.#capabilities.editing;
}
}
class WebviewDocumentStore {
private readonly _documents = new Map<string, WebviewEditorCustomDocument>();
private readonly _documents = new Map<string, CustomDocument>();
public get(viewType: string, resource: vscode.Uri): WebviewEditorCustomDocument | undefined {
public get(viewType: string, resource: vscode.Uri): CustomDocument | undefined {
return this._documents.get(this.key(viewType, resource));
}
public add(document: WebviewEditorCustomDocument) {
public add(document: CustomDocument) {
const key = this.key(document.viewType, document.uri);
if (this._documents.has(key)) {
throw new Error(`Document already exists for viewType:${document.viewType} resource:${document.uri}`);
@@ -391,7 +408,7 @@ class WebviewDocumentStore {
this._documents.set(key, document);
}
public delete(document: WebviewEditorCustomDocument) {
public delete(document: CustomDocument) {
const key = this.key(document.viewType, document.uri);
this._documents.delete(key);
}
@@ -622,7 +639,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
}
const revivedResource = URI.revive(resource);
const document = Object.seal(new WebviewEditorCustomDocument(this._proxy, viewType, revivedResource));
const document = CustomDocument.create(this._proxy, viewType, revivedResource);
const capabilities = await entry.provider.resolveCustomDocument(document);
document._setCapabilities(capabilities);
this._documents.add(document);
@@ -687,39 +704,39 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
async $undo(resourceComponents: UriComponents, viewType: string): Promise<void> {
const document = this.getDocument(viewType, resourceComponents);
document.undo();
document._undo();
}
async $redo(resourceComponents: UriComponents, viewType: string): Promise<void> {
const document = this.getDocument(viewType, resourceComponents);
document.redo();
document._redo();
}
async $revert(resourceComponents: UriComponents, viewType: string): Promise<void> {
const document = this.getDocument(viewType, resourceComponents);
document.revert();
document._revert();
}
async $onSave(resourceComponents: UriComponents, viewType: string): Promise<void> {
const document = this.getDocument(viewType, resourceComponents);
document.save();
document._save();
}
async $onSaveAs(resourceComponents: UriComponents, viewType: string, targetResource: UriComponents): Promise<void> {
const document = this.getDocument(viewType, resourceComponents);
return document.saveAs(URI.revive(targetResource));
return document._saveAs(URI.revive(targetResource));
}
async $backup(resourceComponents: UriComponents, viewType: string, cancellation: CancellationToken): Promise<boolean> {
async $backup(resourceComponents: UriComponents, viewType: string, cancellation: CancellationToken): Promise<void> {
const document = this.getDocument(viewType, resourceComponents);
return document.backup(cancellation);
return document._backup(cancellation);
}
private getWebviewPanel(handle: WebviewPanelHandle): ExtHostWebviewEditor | undefined {
return this._webviewPanels.get(handle);
}
private getDocument(viewType: string, resource: UriComponents): WebviewEditorCustomDocument {
private getDocument(viewType: string, resource: UriComponents): CustomDocument {
const document = this._documents.get(viewType, URI.revive(resource));
if (!document) {
throw new Error('No webview editor custom document found');

View File

@@ -25,9 +25,10 @@ import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/commo
import { SideBarVisibleContext } from 'vs/workbench/common/viewlet';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IViewDescriptorService, IViewContainersRegistry, Extensions as ViewContainerExtensions, IViewsService, FocusedViewContext, ViewContainerLocation } from 'vs/workbench/common/views';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { IQuickInputService, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { IActivityBarService } from 'vs/workbench/services/activityBar/browser/activityBarService';
const registry = Registry.as<IWorkbenchActionRegistry>(WorkbenchExtensions.WorkbenchActions);
const viewCategory = nls.localize('view', "View");
@@ -534,6 +535,7 @@ export class MoveFocusedViewAction extends Action {
@IQuickInputService private quickInputService: IQuickInputService,
@IContextKeyService private contextKeyService: IContextKeyService,
@INotificationService private notificationService: INotificationService,
@IActivityBarService private activityBarService: IActivityBarService,
@IViewletService private viewletService: IViewletService
) {
super(id, label);
@@ -542,58 +544,64 @@ export class MoveFocusedViewAction extends Action {
run(): Promise<void> {
const viewContainerRegistry = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry);
const focusedView = FocusedViewContext.getValue(this.contextKeyService);
const focusedViewId = FocusedViewContext.getValue(this.contextKeyService);
if (focusedView === undefined || focusedView.trim() === '') {
if (focusedViewId === undefined || focusedViewId.trim() === '') {
this.notificationService.error(nls.localize('moveFocusedView.error.noFocusedView', "There is no view currently focused."));
return Promise.resolve();
}
const viewDescriptor = this.viewDescriptorService.getViewDescriptor(focusedView);
const viewDescriptor = this.viewDescriptorService.getViewDescriptor(focusedViewId);
if (!viewDescriptor || !viewDescriptor.canMoveView) {
this.notificationService.error(nls.localize('moveFocusedView.error.nonMovableView', "The currently focused view is not movable {0}.", focusedView));
this.notificationService.error(nls.localize('moveFocusedView.error.nonMovableView', "The currently focused view is not movable."));
return Promise.resolve();
}
const quickPick = this.quickInputService.createQuickPick();
quickPick.placeholder = nls.localize('moveFocusedView.selectDestination', "Select a destination area for the view...");
quickPick.autoFocusOnList = true;
quickPick.placeholder = nls.localize('moveFocusedView.selectDestination', "Select a Destination for the View");
quickPick.items = [
{
id: 'sidebar',
label: nls.localize('sidebar', "Sidebar")
},
{
id: 'panel',
const pinnedViewlets = this.activityBarService.getPinnedViewletIds();
const items: Array<IQuickPickItem | IQuickPickSeparator> = this.viewletService.getViewlets()
.filter(viewlet => {
if (viewlet.id === this.viewDescriptorService.getViewContainer(focusedViewId)!.id) {
return false;
}
return !viewContainerRegistry.get(viewlet.id)!.rejectAddedViews && pinnedViewlets.indexOf(viewlet.id) !== -1;
})
.map(viewlet => {
return {
id: viewlet.id,
label: viewlet.name,
};
});
if (this.viewDescriptorService.getViewLocation(focusedViewId) !== ViewContainerLocation.Panel) {
items.unshift({
type: 'separator',
label: nls.localize('sidebar', "Side Bar")
});
items.push({
type: 'separator',
label: nls.localize('panel', "Panel")
}
];
});
items.push({
id: '_.panel.newcontainer',
label: nls.localize('moveFocusedView.newContainerInPanel', "New Container in Panel"),
});
}
quickPick.items = items;
quickPick.onDidAccept(() => {
const destination = quickPick.selectedItems[0];
if (destination.id === 'panel') {
quickPick.hide();
if (destination.id === '_.panel.newcontainer') {
this.viewDescriptorService.moveViewToLocation(viewDescriptor!, ViewContainerLocation.Panel);
this.viewsService.openView(focusedView, true);
return;
} else if (destination.id === 'sidebar') {
quickPick.placeholder = nls.localize('moveFocusedView.selectDestinationContainer', "Select a destination view group...");
quickPick.items = this.viewletService.getViewlets().map(viewlet => {
return {
id: viewlet.id,
label: viewlet.name
};
});
return;
this.viewsService.openView(focusedViewId, true);
} else if (destination.id) {
quickPick.hide();
this.viewDescriptorService.moveViewsToContainer([viewDescriptor], viewContainerRegistry.get(destination.id)!);
this.viewsService.openView(focusedView, true);
return;
this.viewsService.openView(focusedViewId, true);
}
quickPick.hide();
@@ -605,7 +613,7 @@ export class MoveFocusedViewAction extends Action {
}
}
registry.registerWorkbenchAction(SyncActionDescriptor.create(MoveFocusedViewAction, MoveFocusedViewAction.ID, MoveFocusedViewAction.LABEL), 'View: Move Focused View', viewCategory);
registry.registerWorkbenchAction(SyncActionDescriptor.create(MoveFocusedViewAction, MoveFocusedViewAction.ID, MoveFocusedViewAction.LABEL), 'View: Move Focused View', viewCategory, FocusedViewContext.notEqualsTo(''));
// --- Resize View

View File

@@ -27,6 +27,10 @@
margin-bottom: auto;
}
.monaco-workbench .activitybar > .content > .composite-bar-excess {
height: 100%;
}
.monaco-workbench .activitybar .menubar {
width: 100%;
height: 35px;

View File

@@ -17,13 +17,14 @@ import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { Widget } from 'vs/base/browser/ui/widget';
import { isUndefinedOrNull } from 'vs/base/common/types';
import { LocalSelectionTransfer } from 'vs/workbench/browser/dnd';
import { ITheme } from 'vs/platform/theme/common/themeService';
import { LocalSelectionTransfer, DragAndDropObserver } from 'vs/workbench/browser/dnd';
import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService';
import { Emitter } from 'vs/base/common/event';
import { DraggedViewIdentifier } from 'vs/workbench/browser/parts/views/viewPaneContainer';
import { Registry } from 'vs/platform/registry/common/platform';
import { IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainerLocation, IViewDescriptorService } from 'vs/workbench/common/views';
import { ICompositeDragAndDrop, CompositeDragAndDropData } from 'vs/base/parts/composite/browser/compositeDnd';
import { IPaneComposite } from 'vs/workbench/common/panecomposite';
export interface ICompositeBarItem {
id: string;
@@ -38,7 +39,7 @@ export class CompositeDragAndDrop implements ICompositeDragAndDrop {
constructor(
private viewDescriptorService: IViewDescriptorService,
private targetContainerLocation: ViewContainerLocation,
private openComposite: (id: string, focus?: boolean) => void,
private openComposite: (id: string, focus?: boolean) => Promise<IPaneComposite | undefined>,
private moveComposite: (from: string, to: string) => void,
private getVisibleCompositeIds: () => string[]
) { }
@@ -52,9 +53,14 @@ export class CompositeDragAndDrop implements ICompositeDragAndDrop {
if (targetCompositeId) {
if (currentLocation !== this.targetContainerLocation && this.targetContainerLocation !== ViewContainerLocation.Panel) {
const destinationContainer = viewContainerRegistry.get(targetCompositeId);
if (destinationContainer) {
this.viewDescriptorService.moveViewsToContainer(this.viewDescriptorService.getViewDescriptors(currentContainer)!.allViewDescriptors.filter(vd => vd.canMoveView), destinationContainer);
this.openComposite(targetCompositeId, true);
if (destinationContainer && !destinationContainer.rejectAddedViews) {
const viewsToMove = this.viewDescriptorService.getViewDescriptors(currentContainer)!.allViewDescriptors.filter(vd => vd.canMoveView);
this.viewDescriptorService.moveViewsToContainer(viewsToMove, destinationContainer);
this.openComposite(targetCompositeId, true).then(composite => {
if (composite && viewsToMove.length === 1) {
composite.openView(viewsToMove[0].id, true);
}
});
}
} else {
this.moveComposite(dragData.id, targetCompositeId);
@@ -73,10 +79,14 @@ export class CompositeDragAndDrop implements ICompositeDragAndDrop {
if (viewDescriptor && viewDescriptor.canMoveView) {
if (targetCompositeId) {
const destinationContainer = viewContainerRegistry.get(targetCompositeId);
if (destinationContainer) {
if (destinationContainer && !destinationContainer.rejectAddedViews) {
if (this.targetContainerLocation === ViewContainerLocation.Sidebar) {
this.viewDescriptorService.moveViewsToContainer([viewDescriptor], destinationContainer);
this.openComposite(targetCompositeId, true);
this.openComposite(targetCompositeId, true).then(composite => {
if (composite) {
composite.openView(viewDescriptor.id, true);
}
});
} else {
this.viewDescriptorService.moveViewToLocation(viewDescriptor, this.targetContainerLocation);
this.moveComposite(this.viewDescriptorService.getViewContainer(viewDescriptor.id)!.id, targetCompositeId);
@@ -91,13 +101,25 @@ export class CompositeDragAndDrop implements ICompositeDragAndDrop {
this.moveComposite(newCompositeId, targetId);
}
this.openComposite(newCompositeId, true);
this.openComposite(newCompositeId, true).then(composite => {
if (composite) {
composite.openView(viewDescriptor.id, true);
}
});
}
}
}
}
onDragEnter(data: CompositeDragAndDropData, targetCompositeId: string | undefined, originalEvent: DragEvent): boolean {
return this.canDrop(data, targetCompositeId);
}
onDragOver(data: CompositeDragAndDropData, targetCompositeId: string | undefined, originalEvent: DragEvent): boolean {
return this.canDrop(data, targetCompositeId);
}
private canDrop(data: CompositeDragAndDropData, targetCompositeId: string | undefined): boolean {
const dragData = data.getData();
const viewContainerRegistry = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry);
@@ -134,6 +156,7 @@ export class CompositeDragAndDrop implements ICompositeDragAndDrop {
if (this.targetContainerLocation === ViewContainerLocation.Sidebar) {
const destinationContainer = viewContainerRegistry.get(targetCompositeId);
return !!destinationContainer &&
!destinationContainer.rejectAddedViews &&
this.viewDescriptorService.getViewDescriptors(currentContainer)!.allViewDescriptors.some(vd => vd.canMoveView);
}
// ... from sidebar to the panel
@@ -155,10 +178,9 @@ export class CompositeDragAndDrop implements ICompositeDragAndDrop {
}
// ... into a destination
return true;
const destinationContainer = viewContainerRegistry.get(targetCompositeId);
return !!destinationContainer && !destinationContainer.rejectAddedViews;
}
return false;
}
}
@@ -200,6 +222,7 @@ export class CompositeBar extends Widget implements ICompositeBar {
constructor(
items: ICompositeBarItem[],
private options: ICompositeBarOptions,
@IThemeService private readonly themeService: IThemeService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IContextMenuService private readonly contextMenuService: IContextMenuService
) {
@@ -228,6 +251,7 @@ export class CompositeBar extends Widget implements ICompositeBar {
create(parent: HTMLElement): HTMLElement {
const actionBarDiv = parent.appendChild($('.composite-bar'));
const excessDiv = parent.appendChild($('.composite-bar-excess'));
this.compositeSwitcherBar = this._register(new ActionBar(actionBarDiv, {
actionViewItemProvider: (action: IAction) => {
@@ -254,58 +278,99 @@ export class CompositeBar extends Widget implements ICompositeBar {
this._register(addDisposableListener(parent, EventType.CONTEXT_MENU, e => this.showContextMenu(e)));
// Allow to drop at the end to move composites to the end
this._register(addDisposableListener(parent, EventType.DROP, (e: DragEvent) => {
if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype)) {
EventHelper.stop(e, true);
this._register(new DragAndDropObserver(excessDiv, {
onDragOver: (e: DragEvent) => {
if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype)) {
EventHelper.stop(e, true);
const data = this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype);
if (Array.isArray(data)) {
const draggedCompositeId = data[0].id;
this.compositeTransfer.clearData(DraggedCompositeIdentifier.prototype);
const data = this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype);
if (Array.isArray(data)) {
const draggedCompositeId = data[0].id;
this.options.dndHandler.drop(new CompositeDragAndDropData('composite', draggedCompositeId), undefined, e);
}
}
if (this.compositeTransfer.hasData(DraggedViewIdentifier.prototype)) {
const data = this.compositeTransfer.getData(DraggedViewIdentifier.prototype);
if (Array.isArray(data)) {
const draggedViewId = data[0].id;
this.compositeTransfer.clearData(DraggedViewIdentifier.prototype);
this.options.dndHandler.drop(new CompositeDragAndDropData('view', draggedViewId), undefined, e);
}
}
}));
this._register(addDisposableListener(parent, EventType.DRAG_OVER, (e: DragEvent) => {
if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype)) {
EventHelper.stop(e, true);
const data = this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype);
if (Array.isArray(data)) {
const draggedCompositeId = data[0].id;
// Check if drop is allowed
if (e.dataTransfer && !this.options.dndHandler.onDragOver(new CompositeDragAndDropData('composite', draggedCompositeId), undefined, e)) {
e.dataTransfer.dropEffect = 'none';
// Check if drop is allowed
if (e.dataTransfer && !this.options.dndHandler.onDragOver(new CompositeDragAndDropData('composite', draggedCompositeId), undefined, e)) {
e.dataTransfer.dropEffect = 'none';
}
}
}
}
if (this.compositeTransfer.hasData(DraggedViewIdentifier.prototype)) {
EventHelper.stop(e, true);
if (this.compositeTransfer.hasData(DraggedViewIdentifier.prototype)) {
EventHelper.stop(e, true);
const data = this.compositeTransfer.getData(DraggedViewIdentifier.prototype);
if (Array.isArray(data)) {
const draggedViewId = data[0].id;
const data = this.compositeTransfer.getData(DraggedViewIdentifier.prototype);
if (Array.isArray(data)) {
const draggedViewId = data[0].id;
// Check if drop is allowed
if (e.dataTransfer && !this.options.dndHandler.onDragOver(new CompositeDragAndDropData('view', draggedViewId), undefined, e)) {
e.dataTransfer.dropEffect = 'none';
// Check if drop is allowed
if (e.dataTransfer && !this.options.dndHandler.onDragOver(new CompositeDragAndDropData('view', draggedViewId), undefined, e)) {
e.dataTransfer.dropEffect = 'none';
}
}
}
}
},
onDragEnter: (e: DragEvent) => {
if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype)) {
EventHelper.stop(e, true);
const data = this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype);
if (Array.isArray(data)) {
const draggedCompositeId = data[0].id;
// Check if drop is allowed
const validDropTarget = this.options.dndHandler.onDragEnter(new CompositeDragAndDropData('composite', draggedCompositeId), undefined, e);
this.updateFromDragging(excessDiv, validDropTarget);
}
}
if (this.compositeTransfer.hasData(DraggedViewIdentifier.prototype)) {
EventHelper.stop(e, true);
const data = this.compositeTransfer.getData(DraggedViewIdentifier.prototype);
if (Array.isArray(data)) {
const draggedViewId = data[0].id;
// Check if drop is allowed
const validDropTarget = this.options.dndHandler.onDragEnter(new CompositeDragAndDropData('view', draggedViewId), undefined, e);
this.updateFromDragging(excessDiv, validDropTarget);
}
}
},
onDragLeave: (e: DragEvent) => {
if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype) ||
this.compositeTransfer.hasData(DraggedViewIdentifier.prototype)) {
this.updateFromDragging(excessDiv, false);
}
},
onDragEnd: (e: DragEvent) => {
// no-op, will not be called
},
onDrop: (e: DragEvent) => {
if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype)) {
EventHelper.stop(e, true);
const data = this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype);
if (Array.isArray(data)) {
const draggedCompositeId = data[0].id;
this.compositeTransfer.clearData(DraggedCompositeIdentifier.prototype);
this.options.dndHandler.drop(new CompositeDragAndDropData('composite', draggedCompositeId), undefined, e);
this.updateFromDragging(excessDiv, false);
}
}
if (this.compositeTransfer.hasData(DraggedViewIdentifier.prototype)) {
const data = this.compositeTransfer.getData(DraggedViewIdentifier.prototype);
if (Array.isArray(data)) {
const draggedViewId = data[0].id;
this.compositeTransfer.clearData(DraggedViewIdentifier.prototype);
this.options.dndHandler.drop(new CompositeDragAndDropData('view', draggedViewId), undefined, e);
this.updateFromDragging(excessDiv, false);
}
}
},
}));
return actionBarDiv;
@@ -410,6 +475,13 @@ export class CompositeBar extends Widget implements ICompositeBar {
}
}
private updateFromDragging(element: HTMLElement, isDragging: boolean): void {
const theme = this.themeService.getTheme();
const dragBackground = this.options.colors(theme).dragAndDropBackground;
element.style.backgroundColor = isDragging && dragBackground ? dragBackground.toString() : '';
}
private resetActiveComposite(compositeId: string) {
const defaultCompositeId = this.options.getDefaultCompositeId();

View File

@@ -544,14 +544,16 @@ export class CompositeActionViewItem extends ActivityActionViewItem {
if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype)) {
const data = this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype);
if (Array.isArray(data) && data[0].id !== this.activity.id) {
this.updateFromDragging(container, true);
const validDropTarget = this.dndHandler.onDragEnter(new CompositeDragAndDropData('composite', data[0].id), this.activity.id, e);
this.updateFromDragging(container, validDropTarget);
}
}
if (this.compositeTransfer.hasData(DraggedViewIdentifier.prototype)) {
const data = this.compositeTransfer.getData(DraggedViewIdentifier.prototype);
if (Array.isArray(data) && data[0].id !== this.activity.id) {
this.updateFromDragging(container, true);
const validDropTarget = this.dndHandler.onDragEnter(new CompositeDragAndDropData('view', data[0].id), this.activity.id, e);
this.updateFromDragging(container, validDropTarget);
}
}
},
@@ -616,6 +618,8 @@ export class CompositeActionViewItem extends ActivityActionViewItem {
const data = this.compositeTransfer.getData(DraggedViewIdentifier.prototype);
if (Array.isArray(data)) {
const draggedViewId = data[0].id;
this.updateFromDragging(container, false);
this.compositeTransfer.clearData(DraggedViewIdentifier.prototype);
this.dndHandler.drop(new CompositeDragAndDropData('view', draggedViewId), this.activity.id, e);
}

View File

@@ -147,6 +147,7 @@ class StateChange {
encoding: boolean = false;
EOL: boolean = false;
tabFocusMode: boolean = false;
columnSelectionMode: boolean = false;
screenReaderMode: boolean = false;
metadata: boolean = false;
@@ -157,6 +158,7 @@ class StateChange {
this.encoding = this.encoding || other.encoding;
this.EOL = this.EOL || other.EOL;
this.tabFocusMode = this.tabFocusMode || other.tabFocusMode;
this.columnSelectionMode = this.columnSelectionMode || other.columnSelectionMode;
this.screenReaderMode = this.screenReaderMode || other.screenReaderMode;
this.metadata = this.metadata || other.metadata;
}
@@ -168,21 +170,23 @@ class StateChange {
|| this.encoding
|| this.EOL
|| this.tabFocusMode
|| this.columnSelectionMode
|| this.screenReaderMode
|| this.metadata;
}
}
interface StateDelta {
selectionStatus?: string;
mode?: string;
encoding?: string;
EOL?: string;
indentation?: string;
tabFocusMode?: boolean;
screenReaderMode?: boolean;
metadata?: string | undefined;
}
type StateDelta = (
{ type: 'selectionStatus'; selectionStatus: string | undefined; }
| { type: 'mode'; mode: string | undefined; }
| { type: 'encoding'; encoding: string | undefined; }
| { type: 'EOL'; EOL: string | undefined; }
| { type: 'indentation'; indentation: string | undefined; }
| { type: 'tabFocusMode'; tabFocusMode: boolean; }
| { type: 'columnSelectionMode'; columnSelectionMode: boolean; }
| { type: 'screenReaderMode'; screenReaderMode: boolean; }
| { type: 'metadata'; metadata: string | undefined; }
);
class State {
@@ -204,6 +208,9 @@ class State {
private _tabFocusMode: boolean | undefined;
get tabFocusMode(): boolean | undefined { return this._tabFocusMode; }
private _columnSelectionMode: boolean | undefined;
get columnSelectionMode(): boolean | undefined { return this._columnSelectionMode; }
private _screenReaderMode: boolean | undefined;
get screenReaderMode(): boolean | undefined { return this._screenReaderMode; }
@@ -213,56 +220,63 @@ class State {
update(update: StateDelta): StateChange {
const change = new StateChange();
if ('selectionStatus' in update) {
if (update.type === 'selectionStatus') {
if (this._selectionStatus !== update.selectionStatus) {
this._selectionStatus = update.selectionStatus;
change.selectionStatus = true;
}
}
if ('indentation' in update) {
if (update.type === 'indentation') {
if (this._indentation !== update.indentation) {
this._indentation = update.indentation;
change.indentation = true;
}
}
if ('mode' in update) {
if (update.type === 'mode') {
if (this._mode !== update.mode) {
this._mode = update.mode;
change.mode = true;
}
}
if ('encoding' in update) {
if (update.type === 'encoding') {
if (this._encoding !== update.encoding) {
this._encoding = update.encoding;
change.encoding = true;
}
}
if ('EOL' in update) {
if (update.type === 'EOL') {
if (this._EOL !== update.EOL) {
this._EOL = update.EOL;
change.EOL = true;
}
}
if ('tabFocusMode' in update) {
if (update.type === 'tabFocusMode') {
if (this._tabFocusMode !== update.tabFocusMode) {
this._tabFocusMode = update.tabFocusMode;
change.tabFocusMode = true;
}
}
if ('screenReaderMode' in update) {
if (update.type === 'columnSelectionMode') {
if (this._columnSelectionMode !== update.columnSelectionMode) {
this._columnSelectionMode = update.columnSelectionMode;
change.columnSelectionMode = true;
}
}
if (update.type === 'screenReaderMode') {
if (this._screenReaderMode !== update.screenReaderMode) {
this._screenReaderMode = update.screenReaderMode;
change.screenReaderMode = true;
}
}
if ('metadata' in update) {
if (update.type === 'metadata') {
if (this._metadata !== update.metadata) {
this._metadata = update.metadata;
change.metadata = true;
@@ -282,6 +296,7 @@ const nlsEOLCRLF = nls.localize('endOfLineCarriageReturnLineFeed', "CRLF");
export class EditorStatus extends Disposable implements IWorkbenchContribution {
private readonly tabFocusModeElement = this._register(new MutableDisposable<IStatusbarEntryAccessor>());
private readonly columnSelectionModeElement = this._register(new MutableDisposable<IStatusbarEntryAccessor>());
private readonly screenRedearModeElement = this._register(new MutableDisposable<IStatusbarEntryAccessor>());
private readonly indentationElement = this._register(new MutableDisposable<IStatusbarEntryAccessor>());
private readonly selectionElement = this._register(new MutableDisposable<IStatusbarEntryAccessor>());
@@ -402,6 +417,22 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
}
}
private updateColumnSelectionModeElement(visible: boolean): void {
if (visible) {
if (!this.columnSelectionModeElement.value) {
this.columnSelectionModeElement.value = this.statusbarService.addEntry({
text: nls.localize('columnSelectionModeEnabled', "Column Selection"),
tooltip: nls.localize('disableColumnSelectionMode', "Disable Column Selection Mode"),
command: 'editor.action.toggleColumnSelection',
backgroundColor: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_BACKGROUND),
color: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_FOREGROUND)
}, 'status.editor.columnSelectionMode', nls.localize('status.editor.columnSelectionMode', "Column Selection Mode"), StatusbarAlignment.RIGHT, 100.8);
}
} else {
this.columnSelectionModeElement.clear();
}
}
private updateScreenReaderModeElement(visible: boolean): void {
if (visible) {
if (!this.screenRedearModeElement.value) {
@@ -544,6 +575,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
private doRenderNow(changed: StateChange): void {
this.updateTabFocusModeElement(!!this.state.tabFocusMode);
this.updateColumnSelectionModeElement(!!this.state.columnSelectionMode);
this.updateScreenReaderModeElement(!!this.state.screenReaderMode);
this.updateIndentationElement(this.state.indentation);
this.updateSelectionElement(this.state.selectionStatus && !this.state.screenReaderMode ? this.state.selectionStatus : undefined);
@@ -583,6 +615,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
const activeCodeEditor = activeControl ? withNullAsUndefined(getCodeEditor(activeControl.getControl())) : undefined;
// Update all states
this.onColumnSelectionModeChange(activeCodeEditor);
this.onScreenReaderModeChange(activeCodeEditor);
this.onSelectionChange(activeCodeEditor);
this.onModeChange(activeCodeEditor, activeInput);
@@ -600,6 +633,9 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
// Hook Listener for Configuration changes
this.activeEditorListeners.add(activeCodeEditor.onDidChangeConfiguration((event: ConfigurationChangedEvent) => {
if (event.hasChanged(EditorOption.columnSelection)) {
this.onColumnSelectionModeChange(activeCodeEditor);
}
if (event.hasChanged(EditorOption.accessibilitySupport)) {
this.onScreenReaderModeChange(activeCodeEditor);
}
@@ -668,14 +704,14 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
}
private onModeChange(editorWidget: ICodeEditor | undefined, editorInput: IEditorInput | undefined): void {
let info: StateDelta = { mode: undefined };
let info: StateDelta = { type: 'mode', mode: undefined };
// We only support text based editors
if (editorWidget && editorInput && toEditorWithModeSupport(editorInput)) {
const textModel = editorWidget.getModel();
if (textModel) {
const modeId = textModel.getLanguageIdentifier().language;
info = { mode: withNullAsUndefined(this.modeService.getLanguageName(modeId)) };
info.mode = withNullAsUndefined(this.modeService.getLanguageName(modeId));
}
}
@@ -683,7 +719,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
}
private onIndentationChange(editorWidget: ICodeEditor | undefined): void {
const update: StateDelta = { indentation: undefined };
const update: StateDelta = { type: 'indentation', indentation: undefined };
if (editorWidget) {
const model = editorWidget.getModel();
@@ -701,7 +737,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
}
private onMetadataChange(editor: IBaseEditor | undefined): void {
const update: StateDelta = { metadata: undefined };
const update: StateDelta = { type: 'metadata', metadata: undefined };
if (editor instanceof BaseBinaryResourceEditor || editor instanceof BinaryResourceDiffEditor) {
update.metadata = editor.getMetadata();
@@ -710,6 +746,16 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
this.updateState(update);
}
private onColumnSelectionModeChange(editorWidget: ICodeEditor | undefined): void {
const info: StateDelta = { type: 'columnSelectionMode', columnSelectionMode: false };
if (editorWidget && editorWidget.getOption(EditorOption.columnSelection)) {
info.columnSelectionMode = true;
}
this.updateState(info);
}
private onScreenReaderModeChange(editorWidget: ICodeEditor | undefined): void {
let screenReaderMode = false;
@@ -733,7 +779,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
this.screenReaderNotification.close();
}
this.updateState({ screenReaderMode: screenReaderMode });
this.updateState({ type: 'screenReaderMode', screenReaderMode: screenReaderMode });
}
private onSelectionChange(editorWidget: ICodeEditor | undefined): void {
@@ -773,11 +819,11 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
}
}
this.updateState({ selectionStatus: this.getSelectionLabel(info) });
this.updateState({ type: 'selectionStatus', selectionStatus: this.getSelectionLabel(info) });
}
private onEOLChange(editorWidget: ICodeEditor | undefined): void {
const info: StateDelta = { EOL: undefined };
const info: StateDelta = { type: 'EOL', EOL: undefined };
if (editorWidget && !editorWidget.getOption(EditorOption.readOnly)) {
const codeEditorModel = editorWidget.getModel();
@@ -794,7 +840,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
return;
}
const info: StateDelta = { encoding: undefined };
const info: StateDelta = { type: 'encoding', encoding: undefined };
// We only support text based editors that have a model associated
// This ensures we do not show the encoding picker while an editor
@@ -828,7 +874,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
}
private onTabFocusModeChange(): void {
const info: StateDelta = { tabFocusMode: TabFocus.getTabFocusMode() };
const info: StateDelta = { type: 'tabFocusMode', tabFocusMode: TabFocus.getTabFocusMode() };
this.updateState(info);
}

View File

@@ -26,10 +26,8 @@ export class ClearNotificationAction extends Action {
super(id, label, 'codicon-close');
}
run(notification: INotificationViewItem): Promise<any> {
async run(notification: INotificationViewItem): Promise<any> {
this.commandService.executeCommand(CLEAR_NOTIFICATION, notification);
return Promise.resolve();
}
}
@@ -46,10 +44,8 @@ export class ClearAllNotificationsAction extends Action {
super(id, label, 'codicon-clear-all');
}
run(notification: INotificationViewItem): Promise<any> {
async run(notification: INotificationViewItem): Promise<any> {
this.commandService.executeCommand(CLEAR_ALL_NOTIFICATIONS);
return Promise.resolve();
}
}
@@ -63,13 +59,11 @@ export class HideNotificationsCenterAction extends Action {
label: string,
@ICommandService private readonly commandService: ICommandService
) {
super(id, label, 'codicon-close');
super(id, label, 'codicon-chevron-down');
}
run(notification: INotificationViewItem): Promise<any> {
async run(notification: INotificationViewItem): Promise<any> {
this.commandService.executeCommand(HIDE_NOTIFICATIONS_CENTER);
return Promise.resolve();
}
}
@@ -86,10 +80,8 @@ export class ExpandNotificationAction extends Action {
super(id, label, 'codicon-chevron-up');
}
run(notification: INotificationViewItem): Promise<any> {
async run(notification: INotificationViewItem): Promise<any> {
this.commandService.executeCommand(EXPAND_NOTIFICATION, notification);
return Promise.resolve();
}
}
@@ -106,10 +98,8 @@ export class CollapseNotificationAction extends Action {
super(id, label, 'codicon-chevron-down');
}
run(notification: INotificationViewItem): Promise<any> {
async run(notification: INotificationViewItem): Promise<any> {
this.commandService.executeCommand(COLLAPSE_NOTIFICATION, notification);
return Promise.resolve();
}
}

View File

@@ -100,6 +100,9 @@ export class NotificationsCenter extends Themable implements INotificationsCente
// Theming
this.updateStyles();
// Mark as visible
this.model.notifications.forEach(notification => notification.updateVisibility(true));
// Context Key
this.notificationsCenterVisibleContextKey.set(true);
@@ -115,7 +118,7 @@ export class NotificationsCenter extends Themable implements INotificationsCente
clearAllAction.enabled = false;
} else {
notificationsCenterTitle.textContent = localize('notifications', "Notifications");
clearAllAction.enabled = true;
clearAllAction.enabled = this.model.notifications.some(notification => !notification.hasProgress);
}
}
@@ -172,20 +175,22 @@ export class NotificationsCenter extends Themable implements INotificationsCente
return; // only if visible
}
let focusGroup = false;
let focusEditor = false;
// Update notifications list based on event
const [notificationsList, notificationsCenterContainer] = assertAllDefined(this.notificationsList, this.notificationsCenterContainer);
switch (e.kind) {
case NotificationChangeType.ADD:
notificationsList.updateNotificationsList(e.index, 0, [e.item]);
e.item.updateVisibility(true);
break;
case NotificationChangeType.CHANGE:
notificationsList.updateNotificationsList(e.index, 1, [e.item]);
break;
case NotificationChangeType.REMOVE:
focusGroup = isAncestor(document.activeElement, notificationsCenterContainer);
focusEditor = isAncestor(document.activeElement, notificationsCenterContainer);
notificationsList.updateNotificationsList(e.index, 1);
e.item.updateVisibility(false);
break;
}
@@ -197,7 +202,7 @@ export class NotificationsCenter extends Themable implements INotificationsCente
this.hide();
// Restore focus to editor group if we had focus
if (focusGroup) {
if (focusEditor) {
this.editorGroupService.activeGroup.focus();
}
}
@@ -208,13 +213,16 @@ export class NotificationsCenter extends Themable implements INotificationsCente
return; // already hidden
}
const focusGroup = isAncestor(document.activeElement, this.notificationsCenterContainer);
const focusEditor = isAncestor(document.activeElement, this.notificationsCenterContainer);
// Hide
this._isVisible = false;
removeClass(this.notificationsCenterContainer, 'visible');
this.notificationsList.hide();
// Mark as hidden
this.model.notifications.forEach(notification => notification.updateVisibility(false));
// Context Key
this.notificationsCenterVisibleContextKey.set(false);
@@ -222,7 +230,7 @@ export class NotificationsCenter extends Themable implements INotificationsCente
this._onDidChangeVisibility.fire();
// Restore focus to editor group if we had focus
if (focusGroup) {
if (focusEditor) {
this.editorGroupService.activeGroup.focus();
}
}

View File

@@ -75,6 +75,7 @@ export function registerNotificationCommands(center: INotificationsCenterControl
// Show Notifications Cneter
CommandsRegistry.registerCommand(SHOW_NOTIFICATIONS_CENTER, () => {
toasts.hide();
center.show();
});
@@ -92,6 +93,7 @@ export function registerNotificationCommands(center: INotificationsCenterControl
if (center.isVisible) {
center.hide();
} else {
toasts.hide();
center.show();
}
});

View File

@@ -46,7 +46,7 @@ export class NotificationsStatus extends Disposable {
if (!this.isNotificationsCenterVisible) {
if (e.kind === NotificationChangeType.ADD) {
this.newNotificationsCount++;
} else if (e.kind === NotificationChangeType.REMOVE) {
} else if (e.kind === NotificationChangeType.REMOVE && this.newNotificationsCount > 0) {
this.newNotificationsCount--;
}
}
@@ -69,8 +69,9 @@ export class NotificationsStatus extends Disposable {
}
}
// Show the bell with a dot if there are unread or in-progress notifications
const statusProperties: IStatusbarEntry = {
text: `${this.newNotificationsCount === 0 ? '$(bell)' : '$(bell-dot)'}${notificationsInProgress > 0 ? ' $(sync~spin)' : ''}`,
text: `${notificationsInProgress > 0 || this.newNotificationsCount > 0 ? '$(bell-dot)' : '$(bell)'}`,
command: this.isNotificationsCenterVisible ? HIDE_NOTIFICATIONS_CENTER : SHOW_NOTIFICATIONS_CENTER,
tooltip: this.getTooltip(notificationsInProgress),
showBeak: this.isNotificationsCenterVisible

View File

@@ -179,11 +179,8 @@ export class NotificationsToasts extends Themable implements INotificationsToast
const toast: INotificationToast = { item, list: notificationList, container: notificationToastContainer, toast: notificationToast, toDispose: itemDisposables };
this.mapNotificationToToast.set(item, toast);
itemDisposables.add(toDisposable(() => {
if (this.isToastVisible(toast) && notificationsToastsContainer) {
notificationsToastsContainer.removeChild(toast.container);
}
}));
// When disposed, remove as visible
itemDisposables.add(toDisposable(() => this.updateToastVisibility(toast, false)));
// Make visible
notificationList.show();
@@ -236,6 +233,9 @@ export class NotificationsToasts extends Themable implements INotificationsToast
addClass(notificationToast, 'notification-fade-in-done');
}));
// Mark as visible
item.updateVisibility(true);
// Events
if (!this._isVisible) {
this._isVisible = true;
@@ -292,12 +292,13 @@ export class NotificationsToasts extends Themable implements INotificationsToast
}
private removeToast(item: INotificationViewItem): void {
let focusEditor = false;
const notificationToast = this.mapNotificationToToast.get(item);
let focusGroup = false;
if (notificationToast) {
const toastHasDOMFocus = isAncestor(document.activeElement, notificationToast.container);
if (toastHasDOMFocus) {
focusGroup = !(this.focusNext() || this.focusPrevious()); // focus next if any, otherwise focus editor
focusEditor = !(this.focusNext() || this.focusPrevious()); // focus next if any, otherwise focus editor
}
// Listeners
@@ -317,7 +318,7 @@ export class NotificationsToasts extends Themable implements INotificationsToast
this.doHide();
// Move focus back to editor group as needed
if (focusGroup) {
if (focusEditor) {
this.editorGroupService.activeGroup.focus();
}
}
@@ -346,11 +347,11 @@ export class NotificationsToasts extends Themable implements INotificationsToast
}
hide(): void {
const focusGroup = this.notificationsToastsContainer ? isAncestor(document.activeElement, this.notificationsToastsContainer) : false;
const focusEditor = this.notificationsToastsContainer ? isAncestor(document.activeElement, this.notificationsToastsContainer) : false;
this.removeToasts();
if (focusGroup) {
if (focusEditor) {
this.editorGroupService.activeGroup.focus();
}
}
@@ -459,12 +460,12 @@ export class NotificationsToasts extends Themable implements INotificationsToast
notificationToasts.push(toast);
break;
case ToastVisibility.HIDDEN:
if (!this.isToastVisible(toast)) {
if (!this.isToastInDOM(toast)) {
notificationToasts.push(toast);
}
break;
case ToastVisibility.VISIBLE:
if (this.isToastVisible(toast)) {
if (this.isToastInDOM(toast)) {
notificationToasts.push(toast);
}
break;
@@ -530,7 +531,7 @@ export class NotificationsToasts extends Themable implements INotificationsToast
// In order to measure the client height, the element cannot have display: none
toast.container.style.opacity = '0';
this.setVisibility(toast, true);
this.updateToastVisibility(toast, true);
heightToGive -= toast.container.offsetHeight;
@@ -542,7 +543,7 @@ export class NotificationsToasts extends Themable implements INotificationsToast
}
// Hide or show toast based on context
this.setVisibility(toast, makeVisible);
this.updateToastVisibility(toast, makeVisible);
toast.container.style.opacity = '';
if (makeVisible) {
@@ -551,20 +552,24 @@ export class NotificationsToasts extends Themable implements INotificationsToast
});
}
private setVisibility(toast: INotificationToast, visible: boolean): void {
if (this.isToastVisible(toast) === visible) {
private updateToastVisibility(toast: INotificationToast, visible: boolean): void {
if (this.isToastInDOM(toast) === visible) {
return;
}
// Update visibility in DOM
const notificationsToastsContainer = assertIsDefined(this.notificationsToastsContainer);
if (visible) {
notificationsToastsContainer.appendChild(toast.container);
} else {
notificationsToastsContainer.removeChild(toast.container);
}
// Update visibility in model
toast.item.updateVisibility(visible);
}
private isToastVisible(toast: INotificationToast): boolean {
private isToastInDOM(toast: INotificationToast): boolean {
return !!toast.container.parentElement;
}
}

View File

@@ -62,6 +62,10 @@
}
.monaco-workbench .part.panel > .composite.title > .composite-bar-excess {
width: 100%;
}
.monaco-workbench .part.panel > .title > .panel-switcher-container > .monaco-action-bar {
line-height: 27px; /* matches panel titles in settings */
height: 35px;

View File

@@ -36,6 +36,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten
import { ViewContainer, IViewContainersRegistry, Extensions as ViewContainerExtensions, IViewDescriptorService, IViewDescriptorCollection, ViewContainerLocation } from 'vs/workbench/common/views';
import { MenuId } from 'vs/platform/actions/common/actions';
import { ViewMenuActions } from 'vs/workbench/browser/parts/views/viewMenuActions';
import { IPaneComposite } from 'vs/workbench/common/panecomposite';
interface ICachedPanel {
id: string;
@@ -143,7 +144,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
getDefaultCompositeId: () => this.panelRegistry.getDefaultPanelId(),
hidePart: () => this.layoutService.setPanelHidden(true),
dndHandler: new CompositeDragAndDrop(this.viewDescriptorService, ViewContainerLocation.Panel,
(id: string, focus?: boolean) => this.openPanel(id, focus),
(id: string, focus?: boolean) => (<unknown>this.openPanel(id, focus)) as Promise<IPaneComposite | undefined>, // {{SQL CARBON EDIT}} strict-null-check
(from: string, to: string) => this.compositeBar.move(from, to),
() => this.getPinnedPanels().map(p => p.id)
),

View File

@@ -255,7 +255,10 @@ export abstract class ViewPane extends Pane implements IView {
this._onDidFocus.fire();
}));
this._register(focusTracker.onDidBlur(() => {
this.focusedViewContextKey.reset();
if (this.focusedViewContextKey.get() === this.id) {
this.focusedViewContextKey.reset();
}
this._onDidBlur.fire();
}));
}
@@ -1006,7 +1009,8 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
}
if (!this.areExtensionsReady) {
if (this.visibleViewsCountFromCache === undefined) {
return true;
// TODO @sbatten fix hack for #91367
return this.viewDescriptorService.getViewContainerLocation(this.viewContainer) === ViewContainerLocation.Panel;
}
// Check in cache so that view do not jump. See #29609
return this.visibleViewsCountFromCache === 1;

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { INotification, INotificationHandle, INotificationActions, INotificationProgress, NoOpNotification, Severity, NotificationMessage, IPromptChoice, IStatusMessageOptions, NotificationsFilter } from 'vs/platform/notification/common/notification';
import { INotification, INotificationHandle, INotificationActions, INotificationProgress, NoOpNotification, Severity, NotificationMessage, IPromptChoice, IStatusMessageOptions, NotificationsFilter, INotificationProgressProperties } from 'vs/platform/notification/common/notification';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import { Event, Emitter } from 'vs/base/common/event';
import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
@@ -92,6 +92,9 @@ export class NotificationHandle extends Disposable implements INotificationHandl
private readonly _onDidClose = this._register(new Emitter<void>());
readonly onDidClose = this._onDidClose.event;
private readonly _onDidChangeVisibility = this._register(new Emitter<boolean>());
readonly onDidChangeVisibility = this._onDidChangeVisibility.event;
constructor(private readonly item: INotificationViewItem, private readonly onClose: (item: INotificationViewItem) => void) {
super();
@@ -99,6 +102,11 @@ export class NotificationHandle extends Disposable implements INotificationHandl
}
private registerListeners(): void {
// Visibility
this._register(this.item.onDidChangeVisibility(visible => this._onDidChangeVisibility.fire(visible)));
// Closing
Event.once(this.item.onDidClose)(() => {
this._onDidClose.fire();
@@ -265,6 +273,7 @@ export interface INotificationViewItem {
readonly onDidChangeExpansion: Event<void>;
readonly onDidClose: Event<void>;
readonly onDidChangeVisibility: Event<boolean>;
readonly onDidChangeLabel: Event<INotificationViewItemLabelChangeEvent>;
expand(): void;
@@ -275,6 +284,8 @@ export interface INotificationViewItem {
updateMessage(message: NotificationMessage): void;
updateActions(actions?: INotificationActions): void;
updateVisibility(visible: boolean): void;
close(): void;
equals(item: INotificationViewItem): boolean;
@@ -398,6 +409,7 @@ export class NotificationViewItem extends Disposable implements INotificationVie
private static readonly MAX_MESSAGE_LENGTH = 1000;
private _expanded: boolean | undefined;
private _visible: boolean = false;
private _actions: INotificationActions | undefined;
private _progress: NotificationViewItemProgress | undefined;
@@ -411,6 +423,9 @@ export class NotificationViewItem extends Disposable implements INotificationVie
private readonly _onDidChangeLabel = this._register(new Emitter<INotificationViewItemLabelChangeEvent>());
readonly onDidChangeLabel = this._onDidChangeLabel.event;
private readonly _onDidChangeVisibility = this._register(new Emitter<boolean>());
readonly onDidChangeVisibility = this._onDidChangeVisibility.event;
static create(notification: INotification, filter: NotificationsFilter = NotificationsFilter.OFF): INotificationViewItem | undefined {
if (!notification || !notification.message || isPromiseCanceledError(notification.message)) {
return undefined; // we need a message to show
@@ -435,7 +450,7 @@ export class NotificationViewItem extends Disposable implements INotificationVie
actions = { primary: notification.message.actions };
}
return new NotificationViewItem(severity, notification.sticky, notification.silent || filter === NotificationsFilter.SILENT || (filter === NotificationsFilter.ERROR && notification.severity !== Severity.Error), message, notification.source, actions);
return new NotificationViewItem(severity, notification.sticky, notification.silent || filter === NotificationsFilter.SILENT || (filter === NotificationsFilter.ERROR && notification.severity !== Severity.Error), message, notification.source, notification.progress, actions);
}
private static parseNotificationMessage(input: NotificationMessage): INotificationMessage | undefined {
@@ -472,13 +487,30 @@ export class NotificationViewItem extends Disposable implements INotificationVie
private _silent: boolean | undefined,
private _message: INotificationMessage,
private _source: string | undefined,
progress: INotificationProgressProperties | undefined,
actions?: INotificationActions
) {
super();
if (progress) {
this.setProgress(progress);
}
this.setActions(actions);
}
private setProgress(progress: INotificationProgressProperties): void {
if (progress.infinite) {
this.progress.infinite();
} else if (progress.total) {
this.progress.total(progress.total);
if (progress.worked) {
this.progress.worked(progress.worked);
}
}
}
private setActions(actions: INotificationActions = { primary: [], secondary: [] }): void {
if (!Array.isArray(actions.primary)) {
actions.primary = [];
@@ -583,6 +615,14 @@ export class NotificationViewItem extends Disposable implements INotificationVie
this._onDidChangeLabel.fire({ kind: NotificationViewItemLabelKind.ACTIONS });
}
updateVisibility(visible: boolean): void {
if (this._visible !== visible) {
this._visible = visible;
this._onDidChangeVisibility.fire(visible);
}
}
expand(): void {
if (this._expanded || !this.canCollapse) {
return;

View File

@@ -17,7 +17,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
import { IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { IAction, IActionViewItem } from 'vs/base/common/actions';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { flatten } from 'vs/base/common/arrays';
import { flatten, mergeSort } from 'vs/base/common/arrays';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { SetMap } from 'vs/base/common/collections';
@@ -53,6 +53,7 @@ export interface IViewContainerDescriptor {
readonly extensionId?: ExtensionIdentifier;
readonly rejectAddedViews?: boolean;
}
export interface IViewContainersRegistry {
@@ -211,9 +212,16 @@ export interface IViewDescriptorCollection extends IDisposable {
readonly allViewDescriptors: IViewDescriptor[];
}
export enum ViewContentPriority {
Normal = 0,
Low = 1,
Lowest = 2
}
export interface IViewContentDescriptor {
readonly content: string;
readonly when?: ContextKeyExpr | 'default';
readonly priority?: ViewContentPriority;
/**
* ordered preconditions for each button in the content
@@ -247,6 +255,13 @@ export interface IViewsRegistry {
}
function compareViewContentDescriptors(a: IViewContentDescriptor, b: IViewContentDescriptor): number {
const aPriority = a.priority ?? ViewContentPriority.Normal;
const bPriority = b.priority ?? ViewContentPriority.Normal;
if (aPriority !== bPriority) {
return aPriority - bPriority;
}
return a.content < b.content ? -1 : 1;
}
@@ -328,8 +343,8 @@ class ViewsRegistry extends Disposable implements IViewsRegistry {
getViewWelcomeContent(id: string): IViewContentDescriptor[] {
const result: IViewContentDescriptor[] = [];
result.sort(compareViewContentDescriptors);
this._viewWelcomeContents.forEach(id, descriptor => result.push(descriptor));
mergeSort(result, compareViewContentDescriptors);
return result;
}

View File

@@ -111,6 +111,7 @@ class BulkEditPreviewContribution {
private async _previewEdit(edit: WorkspaceEdit) {
this._ctxEnabled.set(true);
const uxState = this._activeSession?.uxState ?? new UXState(this._panelService, this._editorGroupsService);
const view = await getBulkEditPane(this._viewsService);
if (!view) {
this._ctxEnabled.set(false);
@@ -136,9 +137,9 @@ class BulkEditPreviewContribution {
let session: PreviewSession;
if (this._activeSession) {
this._activeSession.cts.dispose(true);
session = new PreviewSession(this._activeSession.uxState);
session = new PreviewSession(uxState);
} else {
session = new PreviewSession(new UXState(this._panelService, this._editorGroupsService));
session = new PreviewSession(uxState);
}
this._activeSession = session;

View File

@@ -10,6 +10,13 @@ import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configur
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { Registry } from 'vs/platform/registry/common/platform';
import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { CoreNavigationCommands } from 'vs/editor/browser/controller/coreCommands';
import { Position } from 'vs/editor/common/core/position';
import { Selection } from 'vs/editor/common/core/selection';
import { CursorColumns } from 'vs/editor/common/controller/cursorCommon';
export class ToggleColumnSelectionAction extends Action {
public static readonly ID = 'editor.action.toggleColumnSelection';
@@ -18,26 +25,68 @@ export class ToggleColumnSelectionAction extends Action {
constructor(
id: string,
label: string,
@IConfigurationService private readonly _configurationService: IConfigurationService
@IConfigurationService private readonly _configurationService: IConfigurationService,
@ICodeEditorService private readonly _codeEditorService: ICodeEditorService
) {
super(id, label);
}
public run(): Promise<any> {
const newValue = !this._configurationService.getValue<boolean>('editor.columnSelection');
return this._configurationService.updateValue('editor.columnSelection', newValue, ConfigurationTarget.USER);
private _getCodeEditor(): ICodeEditor | null {
const codeEditor = this._codeEditorService.getFocusedCodeEditor();
if (codeEditor) {
return codeEditor;
}
return this._codeEditorService.getActiveCodeEditor();
}
public async run(): Promise<any> {
const oldValue = this._configurationService.getValue<boolean>('editor.columnSelection');
const codeEditor = this._getCodeEditor();
await this._configurationService.updateValue('editor.columnSelection', !oldValue, ConfigurationTarget.USER);
const newValue = this._configurationService.getValue<boolean>('editor.columnSelection');
if (!codeEditor || codeEditor !== this._getCodeEditor() || oldValue === newValue || !codeEditor.hasModel()) {
return;
}
const cursors = codeEditor._getCursors();
if (codeEditor.getOption(EditorOption.columnSelection)) {
const selection = codeEditor.getSelection();
const modelSelectionStart = new Position(selection.selectionStartLineNumber, selection.selectionStartColumn);
const viewSelectionStart = cursors.context.convertModelPositionToViewPosition(modelSelectionStart);
const modelPosition = new Position(selection.positionLineNumber, selection.positionColumn);
const viewPosition = cursors.context.convertModelPositionToViewPosition(modelPosition);
CoreNavigationCommands.MoveTo.runCoreEditorCommand(cursors, {
position: modelSelectionStart,
viewPosition: viewSelectionStart
});
const visibleColumn = CursorColumns.visibleColumnFromColumn2(cursors.context.config, cursors.context.viewModel, viewPosition);
CoreNavigationCommands.ColumnSelect.runCoreEditorCommand(cursors, {
position: modelPosition,
viewPosition: viewPosition,
doColumnSelect: true,
mouseColumn: visibleColumn + 1
});
} else {
const columnSelectData = cursors.getColumnSelectData();
const fromViewColumn = CursorColumns.columnFromVisibleColumn2(cursors.context.config, cursors.context.viewModel, columnSelectData.fromViewLineNumber, columnSelectData.fromViewVisualColumn);
const fromPosition = cursors.context.convertViewPositionToModelPosition(columnSelectData.fromViewLineNumber, fromViewColumn);
const toViewColumn = CursorColumns.columnFromVisibleColumn2(cursors.context.config, cursors.context.viewModel, columnSelectData.toViewLineNumber, columnSelectData.toViewVisualColumn);
const toPosition = cursors.context.convertViewPositionToModelPosition(columnSelectData.toViewLineNumber, toViewColumn);
codeEditor.setSelection(new Selection(fromPosition.lineNumber, fromPosition.column, toPosition.lineNumber, toPosition.column));
}
}
}
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleColumnSelectionAction, ToggleColumnSelectionAction.ID, ToggleColumnSelectionAction.LABEL), 'View: Toggle Column Selection Mode', nls.localize('view', "View"));
registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleColumnSelectionAction, ToggleColumnSelectionAction.ID, ToggleColumnSelectionAction.LABEL), 'Toggle Column Selection Mode');
MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, {
group: '3_multi',
group: '4_config',
command: {
id: ToggleColumnSelectionAction.ID,
title: nls.localize({ key: 'miColumnSelection', comment: ['&& denotes a mnemonic'] }, "Column &&Selection Mode"),
toggled: ContextKeyExpr.equals('config.editor.columnSelection', true)
},
order: 1.5
order: 2
});

View File

@@ -70,7 +70,7 @@ Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).regi
const registry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActions);
registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleMultiCursorModifierAction, ToggleMultiCursorModifierAction.ID, ToggleMultiCursorModifierAction.LABEL), 'Toggle Multi-Cursor Modifier');
MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, {
group: '3_multi',
group: '4_config',
command: {
id: ToggleMultiCursorModifierAction.ID,
title: nls.localize('miMultiCursorAlt', "Switch to Alt+Click for Multi-Cursor")
@@ -79,7 +79,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, {
order: 1
});
MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, {
group: '3_multi',
group: '4_config',
command: {
id: ToggleMultiCursorModifierAction.ID,
title: (

View File

@@ -20,10 +20,7 @@ import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { Registry } from 'vs/platform/registry/common/platform';
import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
export class SelectionClipboard extends Disposable implements IEditorContribution {
private static readonly SELECTION_LENGTH_LIMIT = 65536;
@@ -119,15 +116,7 @@ class PasteSelectionClipboardAction extends EditorAction {
id: 'editor.action.selectionClipboardPaste',
label: nls.localize('actions.pasteSelectionClipboard', "Paste Selection Clipboard"),
alias: 'Paste Selection Clipboard',
precondition: EditorContextKeys.writable,
kbOpts: {
kbExpr: ContextKeyExpr.and(
EditorContextKeys.editorTextFocus,
ContextKeyExpr.has('config.editor.selectionClipboard')
),
primary: KeyMod.Shift | KeyCode.Insert,
weight: KeybindingWeight.EditorContrib
}
precondition: EditorContextKeys.writable
});
}

View File

@@ -333,11 +333,7 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ
}
const editorInfo = this._editorInfoStore.get(editor.viewType);
if (!editorInfo) {
continue;
}
if (!editorInfo.matches(newResource)) {
if (!editorInfo?.matches(newResource)) {
continue;
}

View File

@@ -74,7 +74,7 @@ export interface ICustomEditorModel extends IWorkingCopy {
readonly onWillSave: Event<CustomEditorSaveEvent>;
readonly onWillSaveAs: Event<CustomEditorSaveAsEvent>;
onBackup(f: () => CancelablePromise<boolean>): void;
onBackup(f: () => CancelablePromise<void>): void;
setDirty(dirty: boolean): void;
undo(): void;

View File

@@ -29,7 +29,7 @@ namespace HotExitState {
readonly type = Type.Pending;
constructor(
public readonly operation: CancelablePromise<boolean>,
public readonly operation: CancelablePromise<void>,
) { }
}
@@ -90,9 +90,9 @@ export class CustomEditorModel extends Disposable implements ICustomEditorModel
private readonly _onWillSaveAs = this._register(new Emitter<CustomEditorSaveAsEvent>());
public readonly onWillSaveAs = this._onWillSaveAs.event;
private _onBackup: undefined | (() => CancelablePromise<boolean>);
private _onBackup: undefined | (() => CancelablePromise<void>);
public onBackup(f: () => CancelablePromise<boolean>) {
public onBackup(f: () => CancelablePromise<void>) {
if (this._onBackup) {
throw new Error('Backup already implemented');
}
@@ -182,7 +182,11 @@ export class CustomEditorModel extends Disposable implements ICustomEditorModel
this._hotExitState = pendingState;
try {
this._hotExitState = await pendingState.operation ? HotExitState.Allowed : HotExitState.NotAllowed;
await pendingState.operation;
// Make sure state has not changed in the meantime
if (this._hotExitState === pendingState) {
this._hotExitState = HotExitState.Allowed;
}
} catch (e) {
// Make sure state has not changed in the meantime
if (this._hotExitState === pendingState) {

View File

@@ -267,7 +267,7 @@ configurationRegistry.registerConfiguration({
default: true
},
'debug.onTaskErrors': {
enum: ['debugAnyway', 'showErrors', 'prompt', 'cancel'],
enum: ['debugAnyway', 'showErrors', 'prompt', 'abort'],
enumDescriptions: [nls.localize('debugAnyway', "Ignore task errors and start debugging."), nls.localize('showErrors', "Show the Problems view and do not start debugging."), nls.localize('prompt', "Prompt user."), nls.localize('cancel', "Cancel debugging.")],
description: nls.localize('debug.onTaskErrors', "Controls what to do when errors are encountered after running a preLaunchTask."),
default: 'prompt'

View File

@@ -519,18 +519,17 @@ abstract class AbstractLaunch {
if (!config || (!Array.isArray(config.configurations) && !Array.isArray(config.compounds))) {
return [];
} else {
const names: string[] = [];
const configurations: (IConfig | ICompound)[] = [];
if (config.configurations) {
names.push(...config.configurations.filter(cfg => cfg && typeof cfg.name === 'string').map(cfg => cfg.name));
configurations.push(...config.configurations.filter(cfg => cfg && typeof cfg.name === 'string'));
}
if (includeCompounds && config.compounds) {
if (config.compounds) {
names.push(...config.compounds.filter(compound => typeof compound.name === 'string' && compound.configurations && compound.configurations.length)
.map(compound => compound.name));
configurations.push(...config.compounds.filter(compound => typeof compound.name === 'string' && compound.configurations && compound.configurations.length));
}
}
return names;
return getVisibleAndSorted(configurations).map(c => c.name);
}
}

View File

@@ -209,7 +209,7 @@ export class DebugHoverWidget implements IContentWidget {
if (!matchingExpression) {
const lineContent = model.getLineContent(pos.lineNumber);
matchingExpression = lineContent.substring(rng.startColumn - 1, rng.endColumn);
matchingExpression = lineContent.substring(rng.startColumn - 1, rng.endColumn - 1);
}
}

View File

@@ -19,7 +19,7 @@ import { RawDebugSession } from 'vs/workbench/contrib/debug/browser/rawDebugSess
import { IProductService } from 'vs/platform/product/common/productService';
import { IWorkspaceFolder, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { RunOnceScheduler } from 'vs/base/common/async';
import { RunOnceScheduler, Queue } from 'vs/base/common/async';
import { generateUuid } from 'vs/base/common/uuid';
import { IHostService } from 'vs/workbench/services/host/browser/host';
import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug';
@@ -807,62 +807,58 @@ export class DebugSession implements IDebugSession {
this._onDidChangeState.fire();
}));
let outpuPromises: Promise<void>[] = [];
const outputQueue = new Queue<void>();
this.rawListeners.push(this.raw.onDidOutput(async event => {
if (!event.body || !this.raw) {
return;
}
const outputSeverity = event.body.category === 'stderr' ? severity.Error : event.body.category === 'console' ? severity.Warning : severity.Info;
if (event.body.category === 'telemetry') {
// only log telemetry events from debug adapter if the debug extension provided the telemetry key
// and the user opted in telemetry
if (this.raw.customTelemetryService && this.telemetryService.isOptedIn) {
// __GDPR__TODO__ We're sending events in the name of the debug extension and we can not ensure that those are declared correctly.
this.raw.customTelemetryService.publicLog(event.body.output, event.body.data);
}
return;
}
// Make sure to append output in the correct order by properly waiting on preivous promises #33822
const waitFor = outpuPromises.slice();
const source = event.body.source && event.body.line ? {
lineNumber: event.body.line,
column: event.body.column ? event.body.column : 1,
source: this.getSource(event.body.source)
} : undefined;
if (event.body.group === 'start' || event.body.group === 'startCollapsed') {
const expanded = event.body.group === 'start';
this.repl.startGroup(event.body.output || '', expanded, source);
return;
}
if (event.body.group === 'end') {
this.repl.endGroup();
if (!event.body.output) {
// Only return if the end event does not have additional output in it
outputQueue.queue(async () => {
if (!event.body || !this.raw) {
return;
}
}
if (event.body.variablesReference) {
const container = new ExpressionContainer(this, undefined, event.body.variablesReference, generateUuid());
outpuPromises.push(container.getChildren().then(async children => {
await Promise.all(waitFor);
children.forEach(child => {
// Since we can not display multiple trees in a row, we are displaying these variables one after the other (ignoring their names)
(<any>child).name = null;
this.appendToRepl(child, outputSeverity, source);
const outputSeverity = event.body.category === 'stderr' ? severity.Error : event.body.category === 'console' ? severity.Warning : severity.Info;
if (event.body.category === 'telemetry') {
// only log telemetry events from debug adapter if the debug extension provided the telemetry key
// and the user opted in telemetry
if (this.raw.customTelemetryService && this.telemetryService.isOptedIn) {
// __GDPR__TODO__ We're sending events in the name of the debug extension and we can not ensure that those are declared correctly.
this.raw.customTelemetryService.publicLog(event.body.output, event.body.data);
}
return;
}
// Make sure to append output in the correct order by properly waiting on preivous promises #33822
const source = event.body.source && event.body.line ? {
lineNumber: event.body.line,
column: event.body.column ? event.body.column : 1,
source: this.getSource(event.body.source)
} : undefined;
if (event.body.group === 'start' || event.body.group === 'startCollapsed') {
const expanded = event.body.group === 'start';
this.repl.startGroup(event.body.output || '', expanded, source);
return;
}
if (event.body.group === 'end') {
this.repl.endGroup();
if (!event.body.output) {
// Only return if the end event does not have additional output in it
return;
}
}
if (event.body.variablesReference) {
const container = new ExpressionContainer(this, undefined, event.body.variablesReference, generateUuid());
await container.getChildren().then(children => {
children.forEach(child => {
// Since we can not display multiple trees in a row, we are displaying these variables one after the other (ignoring their names)
(<any>child).name = null;
this.appendToRepl(child, outputSeverity, source);
});
});
}));
} else if (typeof event.body.output === 'string') {
await Promise.all(waitFor);
this.appendToRepl(event.body.output, outputSeverity, source);
}
await Promise.all(outpuPromises);
outpuPromises = [];
} else if (typeof event.body.output === 'string') {
this.appendToRepl(event.body.output, outputSeverity, source);
}
});
}));
this.rawListeners.push(this.raw.onDidBreakpoint(event => {

View File

@@ -75,35 +75,35 @@ export class StartView extends ViewPane {
};
this._register(editorService.onDidActiveEditorChange(setContextKey));
this._register(this.debugService.getConfigurationManager().onDidRegisterDebugger(setContextKey));
this.registerViews();
setContextKey();
const debugKeybinding = this.keybindingService.lookupKeybinding(StartAction.ID);
debugKeybindingLabel = debugKeybinding ? ` (${debugKeybinding.getLabel()})` : '';
}
shouldShowWelcome(): boolean {
return true;
}
private registerViews(): void {
const viewsRegistry = Registry.as<IViewsRegistry>(Extensions.ViewsRegistry);
viewsRegistry.registerViewWelcomeContent(StartView.ID, {
content: localize('openAFileWhichCanBeDebugged', "[Open a file](command:{0}) which can be debugged or run.", isMacintosh ? OpenFileFolderAction.ID : OpenFileAction.ID),
when: CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR.toNegated()
});
const debugKeybinding = this.keybindingService.lookupKeybinding(StartAction.ID);
const debugKeybindingLabel = debugKeybinding ? ` (${debugKeybinding.getLabel()})` : '';
viewsRegistry.registerViewWelcomeContent(StartView.ID, {
content: localize('runAndDebugAction', "[Run and Debug{0}](command:{1})", debugKeybindingLabel, StartAction.ID),
preconditions: [CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR]
});
viewsRegistry.registerViewWelcomeContent(StartView.ID, {
content: localize('customizeRunAndDebug', "To customize Run and Debug [create a launch.json file](command:{0}).", ConfigureAction.ID),
when: WorkbenchStateContext.notEqualsTo('empty')
});
viewsRegistry.registerViewWelcomeContent(StartView.ID, {
content: localize('customizeRunAndDebugOpenFolder', "To customize Run and Debug, [open a folder](command:{0}) and create a launch.json file.", isMacintosh ? OpenFileFolderAction.ID : OpenFolderAction.ID),
when: WorkbenchStateContext.isEqualTo('empty')
});
}
}
const viewsRegistry = Registry.as<IViewsRegistry>(Extensions.ViewsRegistry);
viewsRegistry.registerViewWelcomeContent(StartView.ID, {
content: localize('openAFileWhichCanBeDebugged', "[Open a file](command:{0}) which can be debugged or run.", isMacintosh ? OpenFileFolderAction.ID : OpenFileAction.ID),
when: CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR.toNegated()
});
let debugKeybindingLabel = '';
viewsRegistry.registerViewWelcomeContent(StartView.ID, {
content: localize('runAndDebugAction', "[Run and Debug{0}](command:{1})", debugKeybindingLabel, StartAction.ID),
preconditions: [CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR]
});
viewsRegistry.registerViewWelcomeContent(StartView.ID, {
content: localize('customizeRunAndDebug', "To customize Run and Debug [create a launch.json file](command:{0}).", ConfigureAction.ID),
when: WorkbenchStateContext.notEqualsTo('empty')
});
viewsRegistry.registerViewWelcomeContent(StartView.ID, {
content: localize('customizeRunAndDebugOpenFolder', "To customize Run and Debug, [open a folder](command:{0}) and create a launch.json file.", isMacintosh ? OpenFileFolderAction.ID : OpenFolderAction.ID),
when: WorkbenchStateContext.isEqualTo('empty')
});

View File

@@ -87,7 +87,8 @@ Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegis
name: localize('extensions', "Extensions"),
ctorDescriptor: new SyncDescriptor(ExtensionsViewPaneContainer),
icon: 'codicon-extensions',
order: 14 // {{SQL CARBON EDIT}}
order: 14, // {{SQL CARBON EDIT}}
rejectAddedViews: true,
}, ViewContainerLocation.Sidebar);

View File

@@ -2690,7 +2690,7 @@ export class SystemDisabledWarningAction extends ExtensionAction {
if (server) {
this.tooltip = localize('Install in other server to enable', "Install the extension on '{0}' to enable.", server.label);
} else {
this.tooltip = localize('disabled because of extension kind', "This extension cannot be enabled in the remote server.");
this.tooltip = localize('disabled because of extension kind', "This extension has defined that it cannot run on the remote server");
}
return;
}

View File

@@ -65,21 +65,21 @@ export class ExplorerViewletViewsContribution extends Disposable implements IWor
private registerViews(): void {
const viewsRegistry = Registry.as<IViewsRegistry>(Extensions.ViewsRegistry);
viewsRegistry.registerViewWelcomeContent(EmptyView.ID, {
this._register(viewsRegistry.registerViewWelcomeContent(EmptyView.ID, {
content: localize('noWorkspaceHelp', "You have not yet added a folder to the workspace.\n[Add Folder](command:{0})", AddRootFolderAction.ID),
when: WorkbenchStateContext.isEqualTo('workspace')
});
}));
const commandId = isMacintosh ? OpenFileFolderAction.ID : OpenFolderAction.ID;
viewsRegistry.registerViewWelcomeContent(EmptyView.ID, {
this._register(viewsRegistry.registerViewWelcomeContent(EmptyView.ID, {
content: localize('remoteNoFolderHelp', "Connected to remote.\n[Open Folder](command:{0})", commandId),
when: ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('workspace'), RemoteNameContext.notEqualsTo(''), IsWebContext.toNegated())
});
}));
viewsRegistry.registerViewWelcomeContent(EmptyView.ID, {
this._register(viewsRegistry.registerViewWelcomeContent(EmptyView.ID, {
content: localize('noFolderHelp', "You have not yet opened a folder.\n[Open Folder](command:{0})", commandId),
when: ContextKeyExpr.or(ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('workspace'), RemoteNameContext.isEqualTo('')), ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('workspace'), IsWebContext))
});
}));
const viewDescriptors = viewsRegistry.getViews(VIEW_CONTAINER);

View File

@@ -55,11 +55,11 @@
align-items: center;
}
.explorer-viewlet .pane-header .monaco-count-badge.hidden {
.pane-header .dirty-count.monaco-count-badge.hidden {
display: none;
}
.explorer-viewlet .monaco-count-badge {
.dirty-count.monaco-count-badge {
padding: 1px 6px 2px;
margin-left: 6px;
min-height: auto;

Some files were not shown because too many files have changed in this diff Show More