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/mssql/notebooks/**',
'!extensions/integration-tests/testData/**', '!extensions/integration-tests/testData/**',
'!extensions/big-data-cluster/src/bigDataCluster/controller/apiGenerated.ts', '!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 = [ const copyrightFilter = [

View File

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

View File

@@ -150,11 +150,11 @@
"colors.ignored": "Color for ignored resources.", "colors.ignored": "Color for ignored resources.",
"colors.conflict": "Color for resources with conflicts.", "colors.conflict": "Color for resources with conflicts.",
"colors.submodule": "Color for submodule resources.", "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.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.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.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.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.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.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.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; opts.signoff = true;
} }
const smartCommitChanges = config.get<'all' | 'tracked'>('smartCommitChanges');
if ( if (
( (
// no changes // no changes
(noStagedChanges && noUnstagedChanges) (noStagedChanges && noUnstagedChanges)
// or no staged changes and not `all` // or no staged changes and not `all`
|| (!opts.all && noStagedChanges) || (!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 && !opts.empty
) { ) {
@@ -1416,7 +1420,7 @@ export class CommandCenter {
return false; return false;
} }
if (opts.all && config.get<'all' | 'tracked'>('smartCommitChanges') === 'tracked') { if (opts.all && smartCommitChanges === 'tracked') {
opts.all = 'tracked'; opts.all = 'tracked';
} }
@@ -2353,7 +2357,7 @@ export class CommandCenter {
else if (item.previousRef === 'HEAD' && item.ref === '~') { else if (item.previousRef === 'HEAD' && item.ref === '~') {
title = localize('git.title.index', '{0} (Index)', basename); title = localize('git.title.index', '{0} (Index)', basename);
} else { } 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); 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(); await loginService.initialize();
vscode.authentication.registerAuthenticationProvider({ vscode.authentication.registerAuthenticationProvider({
id: 'GitHub', id: 'github',
displayName: 'GitHub', displayName: 'GitHub',
onDidChangeSessions: onDidChangeSessions.event, onDidChangeSessions: onDidChangeSessions.event,
getSessions: () => Promise.resolve(loginService.sessions), getSessions: () => Promise.resolve(loginService.sessions),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -184,7 +184,7 @@ export class AzureActiveDirectoryService {
private convertToSession(token: IToken): vscode.AuthenticationSession { private convertToSession(token: IToken): vscode.AuthenticationSession {
return { return {
id: token.sessionId, id: token.sessionId,
accessToken: () => this.resolveAccessToken(token), getAccessToken: () => this.resolveAccessToken(token),
accountName: token.accountName, accountName: token.accountName,
scopes: token.scope.split(' ') scopes: token.scope.split(' ')
}; };
@@ -192,7 +192,9 @@ export class AzureActiveDirectoryService {
private async resolveAccessToken(token: IToken): Promise<string> { private async resolveAccessToken(token: IToken): Promise<string> {
if (token.accessToken && (!token.expiresAt || token.expiresAt > Date.now())) { 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); return Promise.resolve(token.accessToken);
} }

View File

@@ -5,6 +5,9 @@
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import { AzureActiveDirectoryService, onDidChangeSessions } from './AADHelper'; 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'; 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(); await loginService.initialize();
context.subscriptions.push(vscode.authentication.registerAuthenticationProvider({ context.subscriptions.push(vscode.authentication.registerAuthenticationProvider({
id: 'MSA', id: 'microsoft',
displayName: 'Microsoft', displayName: 'Microsoft',
onDidChangeSessions: onDidChangeSessions.event, onDidChangeSessions: onDidChangeSessions.event,
getSessions: () => Promise.resolve(loginService.sessions), getSessions: () => Promise.resolve(loginService.sessions),
@@ -45,6 +48,7 @@ export async function activate(context: vscode.ExtensionContext) {
if (sessions.length === 1) { if (sessions.length === 1) {
await loginService.logout(loginService.sessions[0].id); await loginService.logout(loginService.sessions[0].id);
onDidChangeSessions.fire(); onDidChangeSessions.fire();
vscode.window.showInformationMessage(localize('signedOut', "Successfully signed out."));
return; return;
} }
@@ -58,6 +62,7 @@ export async function activate(context: vscode.ExtensionContext) {
if (selectedSession) { if (selectedSession) {
await loginService.logout(selectedSession.id); await loginService.logout(selectedSession.id);
onDidChangeSessions.fire(); onDidChangeSessions.fire();
vscode.window.showInformationMessage(localize('signedOut', "Successfully signed out."));
return; 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" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= 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: which-pm-runs@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb" resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb"

View File

@@ -1,7 +1,7 @@
{ {
"name": "azuredatastudio", "name": "azuredatastudio",
"version": "1.16.0", "version": "1.16.0",
"distro": "4672f37b373e16688fc9cda03d25a138669718e9", "distro": "4e85ca0eb0c1b17aceab11996f7c2cf460868e8a",
"author": { "author": {
"name": "Microsoft Corporation" "name": "Microsoft Corporation"
}, },
@@ -184,7 +184,7 @@
"vinyl": "^2.0.0", "vinyl": "^2.0.0",
"vinyl-fs": "^3.0.0", "vinyl-fs": "^3.0.0",
"vsce": "1.48.0", "vsce": "1.48.0",
"vscode-debugprotocol": "1.39.0-pre.0", "vscode-debugprotocol": "1.39.0",
"vscode-nls-dev": "^3.3.1", "vscode-nls-dev": "^3.3.1",
"webpack": "^4.16.5", "webpack": "^4.16.5",
"webpack-cli": "^3.3.8", "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. # 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 # 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 if [ "$SNAP_ARCH" == "amd64" ]; then
ARCH="x86_64-linux-gnu" ARCH="x86_64-linux-gnu"
@@ -14,21 +14,21 @@ else
ARCH="$SNAP_ARCH-linux-gnu" ARCH="$SNAP_ARCH-linux-gnu"
fi fi
export XDG_CACHE_HOME=$SNAP_USER_COMMON/.cache GDK_CACHE_DIR="$SNAP_USER_COMMON/.cache"
if [[ -d $SNAP_USER_DATA/.cache && ! -e $XDG_CACHE_HOME ]]; then if [[ -d "$SNAP_USER_DATA/.cache" && ! -e "$GDK_CACHE_DIR" ]]; then
# the .cache directory used to be stored under $SNAP_USER_DATA, migrate it # 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 fi
mkdir -p $XDG_CACHE_HOME [ ! -d "$GDK_CACHE_DIR" ] && mkdir -p "$GDK_CACHE_DIR"
# Gdk-pixbuf loaders # Gdk-pixbuf loaders
export GDK_PIXBUF_MODULE_FILE=$XDG_CACHE_HOME/gdk-pixbuf-loaders.cache 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 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 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 "$SNAP/usr/lib/$ARCH/gdk-pixbuf-2.0/gdk-pixbuf-query-loaders" > "$GDK_PIXBUF_MODULE_FILE"
fi fi
# Create $XDG_RUNTIME_DIR if not exists (to be removed when https://pad.lv/1656340 is fixed) # 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 "$@" exec "$@"

View File

@@ -5,7 +5,7 @@
@font-face { @font-face {
font-family: "codicon"; font-family: "codicon";
src: url("./codicon.ttf?279add2ec8b3d516ca20a123230cbf9f") format("truetype"); src: url("./codicon.ttf?b5dd8f5aa953889dc1f4c9fa9b44d3dd") format("truetype");
} }
.codicon[class*='codicon-'] { .codicon[class*='codicon-'] {
@@ -415,5 +415,6 @@
.codicon-group-by-ref-type:before { content: "\eb97" } .codicon-group-by-ref-type:before { content: "\eb97" }
.codicon-ungroup-by-ref-type:before { content: "\eb98" } .codicon-ungroup-by-ref-type:before { content: "\eb98" }
.codicon-bell-dot:before { content: "\f101" } .codicon-bell-dot:before { content: "\f101" }
.codicon-debug-alt-2:before { content: "\f102" } .codicon-bell-progress:before { content: "\f102" }
.codicon-debug-alt:before { content: "\f103" } .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) { if (!item.row) {
item.row = this.cache.alloc(item.templateId); 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); item.row!.domNode!.setAttribute('role', role);
const checked = this.ariaProvider.isChecked ? this.ariaProvider.isChecked(item.element) : undefined; const checked = this.ariaProvider.isChecked ? this.ariaProvider.isChecked(item.element) : undefined;
if (typeof checked !== 'undefined') { if (typeof checked !== 'undefined') {

View File

@@ -196,7 +196,7 @@ function asListOptions<T, TFilterData, TRef>(modelProvider: () => ITreeModel<T,
} : undefined, } : undefined,
getRole: options.ariaProvider && options.ariaProvider.getRole ? (node) => { getRole: options.ariaProvider && options.ariaProvider.getRole ? (node) => {
return options.ariaProvider!.getRole!(node.element); 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) => { getRole: options.ariaProvider!.getRole ? (el) => {
return options.ariaProvider!.getRole!(el.element as T); return options.ariaProvider!.getRole!(el.element as T);
} : undefined, } : () => 'treeitem',
isChecked: options.ariaProvider!.isChecked ? (e) => { isChecked: options.ariaProvider!.isChecked ? (e) => {
return options.ariaProvider?.isChecked!(e.element as T); return options.ariaProvider?.isChecked!(e.element as T);
} : undefined } : 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 { export function parseLinkedText(text: string): LinkedText {
const result: LinkedTextNode[] = []; const result: LinkedTextNode[] = [];
@@ -36,7 +36,7 @@ export function parseLinkedText(text: string): LinkedText {
result.push(text.substring(index, match.index)); result.push(text.substring(index, match.index));
} }
const [, label, href, title] = match; const [, label, href, , title] = match;
if (title) { if (title) {
result.push({ label, href, title }); result.push({ label, href, title });

View File

@@ -21,4 +21,5 @@ export class CompositeDragAndDropData implements IDragAndDropData {
export interface ICompositeDragAndDrop { export interface ICompositeDragAndDrop {
drop(data: IDragAndDropData, target: string | undefined, originalEvent: DragEvent): void; drop(data: IDragAndDropData, target: string | undefined, originalEvent: DragEvent): void;
onDragOver(data: IDragAndDropData, target: string | undefined, originalEvent: DragEvent): boolean; 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 _valueSelection: Readonly<[number, number]> | undefined;
private valueSelectionUpdated = true; private valueSelectionUpdated = true;
private _validationMessage: string | undefined; private _validationMessage: string | undefined;
private _ok = false; private _ok: boolean | 'default' = 'default';
private _customButton = false; private _customButton = false;
private _customButtonLabel: string | undefined; private _customButtonLabel: string | undefined;
private _customButtonHover: string | undefined; private _customButtonHover: string | undefined;
@@ -566,7 +566,7 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
return this._ok; return this._ok;
} }
set ok(showOkButton: boolean) { set ok(showOkButton: boolean | 'default') {
this._ok = showOkButton; this._ok = showOkButton;
this.update(); this.update();
} }
@@ -575,6 +575,10 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
return this.visible ? this.ui.inputBox.hasFocus() : false; return this.visible ? this.ui.inputBox.hasFocus() : false;
} }
public focusOnInput() {
this.ui.inputBox.setFocus();
}
onDidChangeSelection = this.onDidChangeSelectionEmitter.event; onDidChangeSelection = this.onDidChangeSelectionEmitter.event;
onDidTriggerItemButton = this.onDidTriggerItemButtonEmitter.event; onDidTriggerItemButton = this.onDidTriggerItemButtonEmitter.event;
@@ -753,7 +757,8 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
if (!this.visible) { if (!this.visible) {
return; 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(); super.update();
if (this.ui.inputBox.value !== this.value) { if (this.ui.inputBox.value !== this.value) {
this.ui.inputBox.value = this.value; this.ui.inputBox.value = this.value;

View File

@@ -113,7 +113,7 @@ export interface IInputOptions {
placeHolder?: string; 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; password?: boolean;
@@ -162,7 +162,7 @@ export interface IQuickPick<T extends IQuickPickItem> extends IQuickInput {
readonly onDidAccept: Event<void>; readonly onDidAccept: Event<void>;
ok: boolean; ok: boolean | 'default';
readonly onDidCustom: Event<void>; readonly onDidCustom: Event<void>;
@@ -209,6 +209,8 @@ export interface IQuickPick<T extends IQuickPickItem> extends IQuickInput {
validationMessage: string | undefined; validationMessage: string | undefined;
inputHasFocus(): boolean; inputHasFocus(): boolean;
focusOnInput(): void;
} }
export interface IInputBox extends IQuickInput { export interface IInputBox extends IQuickInput {

View File

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

View File

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

View File

@@ -21,6 +21,21 @@ suite('LinkedText', () => {
{ label: 'link text', href: 'http://link.href', title: 'and a title' }, { 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, [ assert.deepEqual(parseLinkedText('Some message with [link text](random stuff).').nodes, [
'Some message with [link text](random stuff).' '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 // Recurse into children if any
if (Array.isArray(item.children)) { 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 { IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogs';
import { mnemonicButtonLabel } from 'vs/base/common/labels'; import { mnemonicButtonLabel } from 'vs/base/common/labels';
import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { ThemeIcon } from 'vs/platform/theme/common/themeService';
import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
const RUN_TEXTMATE_IN_WORKER = false; const RUN_TEXTMATE_IN_WORKER = false;
@@ -100,7 +101,8 @@ export class CodeWindow extends Disposable implements ICodeWindow {
@IWorkspacesMainService private readonly workspacesMainService: IWorkspacesMainService, @IWorkspacesMainService private readonly workspacesMainService: IWorkspacesMainService,
@IBackupMainService private readonly backupMainService: IBackupMainService, @IBackupMainService private readonly backupMainService: IBackupMainService,
@ITelemetryService private readonly telemetryService: ITelemetryService, @ITelemetryService private readonly telemetryService: ITelemetryService,
@IDialogMainService private readonly dialogMainService: IDialogMainService @IDialogMainService private readonly dialogMainService: IDialogMainService,
@ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService
) { ) {
super(); super();
@@ -244,6 +246,8 @@ export class CodeWindow extends Disposable implements ICodeWindow {
get isExtensionTestHost(): boolean { return !!(this.config && this.config.extensionTestsPath); } get isExtensionTestHost(): boolean { return !!(this.config && this.config.extensionTestsPath); }
get isExtensionDevelopmentTestFromCli(): boolean { return this.isExtensionDevelopmentHost && this.isExtensionTestHost && !this.config?.debugId; }
setRepresentedFilename(filename: string): void { setRepresentedFilename(filename: string): void {
if (isMacintosh) { if (isMacintosh) {
this.win.setRepresentedFilename(filename); this.win.setRepresentedFilename(filename);
@@ -447,6 +451,15 @@ export class CodeWindow extends Disposable implements ICodeWindow {
private onWindowError(error: WindowError): void { private onWindowError(error: WindowError): void {
this.logService.error(error === WindowError.CRASHED ? '[VS Code]: render process crashed!' : '[VS Code]: detected unresponsive'); 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 WindowErrorClassification = {
type: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; type: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true };
}; };

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -494,7 +494,7 @@ export interface CompletionItem {
preselect?: boolean; preselect?: boolean;
/** /**
* A string or snippet that should be inserted in a document when selecting * 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. * is used.
*/ */
insertText: string; insertText: string;
@@ -1373,7 +1373,7 @@ export interface RenameProvider {
*/ */
export interface AuthenticationSession { export interface AuthenticationSession {
id: string; id: string;
accessToken(): Promise<string>; getAccessToken(): Thenable<string>;
accountName: string; accountName: string;
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -338,9 +338,8 @@ export class GotoDefinitionAtPositionEditorContribution implements IEditorContri
private gotoDefinition(position: Position, openToSide: boolean): Promise<any> { private gotoDefinition(position: Position, openToSide: boolean): Promise<any> {
this.editor.setPosition(position); this.editor.setPosition(position);
const definitionLinkOpensInPeek = this.editor.getOption(EditorOption.definitionLinkOpensInPeek);
return this.editor.invokeWithinContext((accessor) => { 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 }); const action = new DefinitionAction({ openToSide, openInPeek: canPeek, muteMessage: true }, { alias: '', label: '', id: '', precondition: undefined });
return action.run(accessor, this.editor); 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)); const newLinks = list.links.map(link => new Link(link, provider));
links = LinksList._union(links, newLinks); links = LinksList._union(links, newLinks);
// register disposables // register disposables
if (isDisposable(provider)) { if (isDisposable(list)) {
this._register(provider); this._register(list);
} }
} }
this.links = links; this.links = links;

View File

@@ -206,7 +206,8 @@ class RenameController implements IEditorContribution {
this._bulkEditService.apply(renameResult, { this._bulkEditService.apply(renameResult, {
editor: this.editor, editor: this.editor,
showPreview: inputFieldResult.wantsPreview, 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 => { }).then(result => {
if (result.ariaSummary) { if (result.ariaSummary) {
alert(nls.localize('aria', "Successfully renamed '{0}' to '{1}'. Summary: {2}", loc!.text, inputFieldResult.newName, 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; flex-shrink: 0;
} }
.monaco-editor .suggest-widget .monaco-list .monaco-list-row:not(.string-label) > .contents > .main > .left > .monaco-icon-label { .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 { .monaco-editor .suggest-widget .monaco-list .monaco-list-row.string-label > .contents > .main > .left > .monaco-icon-label {
flex-shrink: 1; flex-shrink: 1;
@@ -392,6 +392,10 @@
word-wrap: break-word; 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 { .monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > p:empty {
display: none; display: none;
} }

View File

@@ -552,7 +552,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
this.messageElement = append(this.element, $('.message')); this.messageElement = append(this.element, $('.message'));
this.listElement = append(this.element, $('.tree')); 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(); applyStatusBarStyle();
this.statusBarElement = append(this.element, $('.suggest-status-bar')); this.statusBarElement = append(this.element, $('.suggest-status-bar'));

View File

@@ -33,7 +33,7 @@ interface IEditorLayoutProviderOpts {
readonly minimapSide: 'left' | 'right'; readonly minimapSide: 'left' | 'right';
readonly minimapRenderCharacters: boolean; readonly minimapRenderCharacters: boolean;
readonly minimapMaxColumn: number; readonly minimapMaxColumn: number;
minimapMode?: 'actual' | 'cover' | 'contain'; minimapSize?: 'proportional' | 'fill' | 'fit';
readonly pixelRatio: number; readonly pixelRatio: number;
} }
@@ -47,7 +47,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
options._write(EditorOption.folding, false); options._write(EditorOption.folding, false);
const minimapOptions: EditorMinimapOptions = { const minimapOptions: EditorMinimapOptions = {
enabled: input.minimap, enabled: input.minimap,
mode: input.minimapMode || 'actual', size: input.minimapSize || 'proportional',
side: input.minimapSide, side: input.minimapSide,
renderCharacters: input.minimapRenderCharacters, renderCharacters: input.minimapRenderCharacters,
maxColumn: input.minimapMaxColumn, maxColumn: input.minimapMaxColumn,
@@ -978,7 +978,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
minimapSide: 'right', minimapSide: 'right',
minimapRenderCharacters: true, minimapRenderCharacters: true,
minimapMaxColumn: 150, minimapMaxColumn: 150,
minimapMode: 'cover', minimapSize: 'fill',
pixelRatio: 2, pixelRatio: 2,
}, { }, {
width: 1000, width: 1000,
@@ -1042,7 +1042,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
minimapSide: 'right', minimapSide: 'right',
minimapRenderCharacters: true, minimapRenderCharacters: true,
minimapMaxColumn: 150, minimapMaxColumn: 150,
minimapMode: 'cover', minimapSize: 'fill',
pixelRatio: 2, pixelRatio: 2,
}, { }, {
width: 1000, width: 1000,
@@ -1106,7 +1106,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
minimapSide: 'right', minimapSide: 'right',
minimapRenderCharacters: true, minimapRenderCharacters: true,
minimapMaxColumn: 150, minimapMaxColumn: 150,
minimapMode: 'contain', minimapSize: 'fit',
pixelRatio: 2, pixelRatio: 2,
}, { }, {
width: 1000, width: 1000,
@@ -1170,7 +1170,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
minimapSide: 'right', minimapSide: 'right',
minimapRenderCharacters: true, minimapRenderCharacters: true,
minimapMaxColumn: 150, minimapMaxColumn: 150,
minimapMode: 'contain', minimapSize: 'fit',
pixelRatio: 2, pixelRatio: 2,
}, { }, {
width: 1000, 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. * Controls whether clicking on the empty content after a folded line will unfold the line.
* Defaults to false. * Defaults to false.
*/ */
unfoldOnClickInEmptyContent?: boolean; unfoldOnClickAfterEndOfLine?: boolean;
/** /**
* Enable highlighting of matching brackets. * Enable highlighting of matching brackets.
* Defaults to 'always'. * Defaults to 'always'.
@@ -3448,7 +3448,7 @@ declare namespace monaco.editor {
* Control the minimap rendering mode. * Control the minimap rendering mode.
* Defaults to 'actual'. * Defaults to 'actual'.
*/ */
mode?: 'actual' | 'cover' | 'contain'; size?: 'proportional' | 'fill' | 'fit';
/** /**
* Control the rendering of the minimap slider. * Control the rendering of the minimap slider.
* Defaults to 'mouseover'. * Defaults to 'mouseover'.
@@ -3758,9 +3758,14 @@ declare namespace monaco.editor {
*/ */
showSnippets?: boolean; 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>>; export type InternalSuggestOptions = Readonly<Required<ISuggestOptions>>;
@@ -3829,7 +3834,7 @@ declare namespace monaco.editor {
folding = 31, folding = 31,
foldingStrategy = 32, foldingStrategy = 32,
foldingHighlight = 33, foldingHighlight = 33,
unfoldOnClickInEmptyContent = 34, unfoldOnClickAfterEndOfLine = 34,
fontFamily = 35, fontFamily = 35,
fontInfo = 36, fontInfo = 36,
fontLigatures = 37, fontLigatures = 37,
@@ -3945,7 +3950,7 @@ declare namespace monaco.editor {
folding: IEditorOption<EditorOption.folding, boolean>; folding: IEditorOption<EditorOption.folding, boolean>;
foldingStrategy: IEditorOption<EditorOption.foldingStrategy, 'auto' | 'indentation'>; foldingStrategy: IEditorOption<EditorOption.foldingStrategy, 'auto' | 'indentation'>;
foldingHighlight: IEditorOption<EditorOption.foldingHighlight, boolean>; foldingHighlight: IEditorOption<EditorOption.foldingHighlight, boolean>;
unfoldOnClickInEmptyContent: IEditorOption<EditorOption.unfoldOnClickInEmptyContent, boolean>; unfoldOnClickAfterEndOfLine: IEditorOption<EditorOption.unfoldOnClickAfterEndOfLine, boolean>;
fontFamily: IEditorOption<EditorOption.fontFamily, string>; fontFamily: IEditorOption<EditorOption.fontFamily, string>;
fontInfo: IEditorOption<EditorOption.fontInfo, FontInfo>; fontInfo: IEditorOption<EditorOption.fontInfo, FontInfo>;
fontLigatures2: IEditorOption<EditorOption.fontLigatures, string>; fontLigatures2: IEditorOption<EditorOption.fontLigatures, string>;
@@ -5451,7 +5456,7 @@ declare namespace monaco.languages {
preselect?: boolean; preselect?: boolean;
/** /**
* A string or snippet that should be inserted in a document when selecting * 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. * is used.
*/ */
insertText: string; insertText: string;

View File

@@ -112,7 +112,7 @@ export class EnvironmentService implements IEnvironmentService {
get settingsResource(): URI { return resources.joinPath(this.userRoamingDataHome, 'settings.json'); } get settingsResource(): URI { return resources.joinPath(this.userRoamingDataHome, 'settings.json'); }
@memoize @memoize
get userDataSyncHome(): URI { return resources.joinPath(this.userRoamingDataHome, '.sync'); } get userDataSyncHome(): URI { return resources.joinPath(this.userRoamingDataHome, 'sync'); }
@memoize @memoize
get settingsSyncPreviewResource(): URI { return resources.joinPath(this.userDataSyncHome, 'settings.json'); } 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! * this usecase and much easier to use!
*/ */
actions?: INotificationActions; 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 { export interface INotificationActions {
@@ -119,6 +125,24 @@ export interface INotificationActions {
secondary?: ReadonlyArray<IAction>; 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 { export interface INotificationProgress {
/** /**
@@ -149,6 +173,13 @@ export interface INotificationHandle {
*/ */
readonly onDidClose: Event<void>; 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 * Allows to indicate progress on the notification even after the
* notification is already visible. * notification is already visible.

View File

@@ -240,9 +240,36 @@ export class FileStorageDatabase extends Disposable implements IStorageDatabase
private async onDidStorageChangeExternal(): Promise<void> { private async onDidStorageChangeExternal(): Promise<void> {
const items = await this.doGetItemsFromFile(); 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.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>> { async getItems(): Promise<Map<string, string>> {

View File

@@ -24,15 +24,16 @@ interface ISerializableUpdateRequest {
} }
interface ISerializableItemsChangeEvent { interface ISerializableItemsChangeEvent {
items: Item[]; changed?: Item[];
deleted?: Key[];
} }
export class GlobalStorageDatabaseChannel extends Disposable implements IServerChannel { export class GlobalStorageDatabaseChannel extends Disposable implements IServerChannel {
private static readonly STORAGE_CHANGE_DEBOUNCE_TIME = 100; private static readonly STORAGE_CHANGE_DEBOUNCE_TIME = 100;
private readonly _onDidChangeItems: Emitter<ISerializableItemsChangeEvent> = this._register(new Emitter<ISerializableItemsChangeEvent>()); private readonly _onDidChangeItems = this._register(new Emitter<ISerializableItemsChangeEvent>());
readonly onDidChangeItems: Event<ISerializableItemsChangeEvent> = this._onDidChangeItems.event; readonly onDidChangeItems = this._onDidChangeItems.event;
private whenReady: Promise<void>; private whenReady: Promise<void>;
@@ -99,15 +100,18 @@ export class GlobalStorageDatabaseChannel extends Disposable implements IServerC
} }
private serializeEvents(events: IStorageChangeEvent[]): ISerializableItemsChangeEvent { 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 => { events.forEach(event => {
const existing = this.storageMainService.get(event.key); const existing = this.storageMainService.get(event.key);
if (typeof existing === 'string') { 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> { listen(_: unknown, event: string): Event<any> {
@@ -170,8 +174,11 @@ export class GlobalStorageDatabaseChannelClient extends Disposable implements IS
} }
private onDidChangeItemsOnMain(e: ISerializableItemsChangeEvent): void { private onDidChangeItemsOnMain(e: ISerializableItemsChangeEvent): void {
if (Array.isArray(e.items)) { if (Array.isArray(e.changed) || Array.isArray(e.deleted)) {
this._onDidChangeItemsExternal.fire({ items: serializableToMap(e.items) }); 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 { 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 { ILogService, LogLevel } from 'vs/platform/log/common/log';
import { IWorkspaceStorageChangeEvent, IStorageService, StorageScope, IWillSaveStateEvent, WillSaveStateReason, logStorage } from 'vs/platform/storage/common/storage'; import { IWorkspaceStorageChangeEvent, IStorageService, StorageScope, IWillSaveStateEvent, WillSaveStateReason, logStorage } from 'vs/platform/storage/common/storage';
import { SQLiteStorageDatabase, ISQLiteStorageDatabaseLoggingOptions } from 'vs/base/parts/storage/node/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_STORAGE_NAME = 'state.vscdb';
private static readonly WORKSPACE_META_NAME = 'workspace.json'; private static readonly WORKSPACE_META_NAME = 'workspace.json';
private readonly _onDidChangeStorage: Emitter<IWorkspaceStorageChangeEvent> = this._register(new Emitter<IWorkspaceStorageChangeEvent>()); private readonly _onDidChangeStorage = this._register(new Emitter<IWorkspaceStorageChangeEvent>());
readonly onDidChangeStorage: Event<IWorkspaceStorageChangeEvent> = this._onDidChangeStorage.event; readonly onDidChangeStorage = this._onDidChangeStorage.event;
private readonly _onWillSaveState: Emitter<IWillSaveStateEvent> = this._register(new Emitter<IWillSaveStateEvent>()); private readonly _onWillSaveState = this._register(new Emitter<IWillSaveStateEvent>());
readonly onWillSaveState: Event<IWillSaveStateEvent> = this._onWillSaveState.event; readonly onWillSaveState = this._onWillSaveState.event;
private globalStorage: IStorage; private globalStorage: IStorage;

View File

@@ -260,7 +260,7 @@ export class UndoRedoService implements IUndoRedoService {
this._splitPastWorkspaceElement(element, element.removedResources.set); this._splitPastWorkspaceElement(element, element.removedResources.set);
const message = nls.localize('cannotWorkspaceUndo', "Could not undo '{0}' across all files. {1}", element.label, element.removedResources.createMessage()); const message = nls.localize('cannotWorkspaceUndo', "Could not undo '{0}' across all files. {1}", element.label, element.removedResources.createMessage());
this._notificationService.info(message); this._notificationService.info(message);
return; return this.undo(resource);
} }
// this must be the last past element in all the impacted resources! // 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 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(', ')); 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); this._notificationService.info(message);
return; return this.undo(resource);
} }
return this._dialogService.show( return this._dialogService.show(
Severity.Info, Severity.Info,
nls.localize('confirmWorkspace', "Would you like to undo '{0}' across all files?", element.label), 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('ok', "Undo In {0} Files", affectedEditStacks.length),
nls.localize('nok', "No, change only this file.") nls.localize('nok', "Undo This File"),
] nls.localize('cancel', "Cancel"),
],
{
cancelId: 2
}
).then((result) => { ).then((result) => {
if (result.choice === 0) { if (result.choice === 2) {
// cancel
return;
} else if (result.choice === 0) {
for (const editStack of affectedEditStacks) { for (const editStack of affectedEditStacks) {
editStack.past.pop(); editStack.past.pop();
editStack.future.push(element); editStack.future.push(element);
@@ -344,7 +351,7 @@ export class UndoRedoService implements IUndoRedoService {
this._splitFutureWorkspaceElement(element, element.removedResources.set); this._splitFutureWorkspaceElement(element, element.removedResources.set);
const message = nls.localize('cannotWorkspaceRedo', "Could not redo '{0}' across all files. {1}", element.label, element.removedResources.createMessage()); const message = nls.localize('cannotWorkspaceRedo', "Could not redo '{0}' across all files. {1}", element.label, element.removedResources.createMessage());
this._notificationService.info(message); this._notificationService.info(message);
return; return this.redo(resource);
} }
// this must be the last future element in all the impacted resources! // 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 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(', ')); 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); this._notificationService.info(message);
return; return this.redo(resource);
} }
for (const editStack of affectedEditStacks) { for (const editStack of affectedEditStacks) {

View File

@@ -68,7 +68,7 @@ export abstract class AbstractSynchroniser extends Disposable {
) { ) {
super(); super();
this.syncFolder = joinPath(environmentService.userDataSyncHome, source); 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.cleanUpDelayer = new ThrottledDelayer(50);
this.cleanUpBackup(); this.cleanUpBackup();
} }
@@ -202,7 +202,7 @@ export abstract class AbstractSynchroniser extends Disposable {
} }
protected async backupLocal(content: VSBuffer): Promise<void> { 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 { try {
await this.fileService.writeFile(resource, content); await this.fileService.writeFile(resource, content);
} catch (e) { } catch (e) {
@@ -213,9 +213,13 @@ export abstract class AbstractSynchroniser extends Disposable {
private async cleanUpBackup(): Promise<void> { private async cleanUpBackup(): Promise<void> {
try { try {
if (!(await this.fileService.exists(this.syncFolder))) {
return;
}
const stat = await this.fileService.resolve(this.syncFolder); const stat = await this.fileService.resolve(this.syncFolder);
if (stat.children) { 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 */); const backUpMaxAge = 1000 * 60 * 60 * 24 * (this.configurationService.getValue<number>('sync.localBackupDuration') || 30 /* Default 30 days */);
let toDelete = all.filter(stat => { let toDelete = all.filter(stat => {
const ctime = stat.ctime || new Date( 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) { if (added.length || removed.length || updated.length) {
// back up all disabled or market place extensions // back up all disabled or market place extensions
const backUpExtensions = localExtensions.filter(e => e.disabled || !!e.identifier.uuid); 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); skippedExtensions = await this.updateLocalExtensions(added, removed, updated, skippedExtensions);
} }

View File

@@ -165,7 +165,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
if (local) { if (local) {
// update local // update local
this.logService.trace('UI State: Updating local ui state...'); 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); await this.writeLocalGlobalState(local);
this.logService.info('UI State: Updated local ui state'); 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 { Disposable } from 'vs/base/common/lifecycle';
import { IUserDataSyncLogService, IUserDataSyncService, SyncStatus, IUserDataAutoSyncService, UserDataSyncError, UserDataSyncErrorCode, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; import { IUserDataSyncLogService, IUserDataSyncService, SyncStatus, IUserDataAutoSyncService, UserDataSyncError, UserDataSyncErrorCode, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync';
import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication'; 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 { export class UserDataAutoSyncService extends Disposable implements IUserDataAutoSyncService {
@@ -25,6 +30,7 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto
@IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService, @IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService,
@IUserDataSyncLogService private readonly logService: IUserDataSyncLogService, @IUserDataSyncLogService private readonly logService: IUserDataSyncLogService,
@IAuthenticationTokenService private readonly authTokenService: IAuthenticationTokenService, @IAuthenticationTokenService private readonly authTokenService: IAuthenticationTokenService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
) { ) {
super(); super();
this.updateEnablement(false, true); 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>(authTokenService.onDidChangeToken)(() => this.updateEnablement(true, true)));
this._register(Event.any<any>(userDataSyncService.onDidChangeStatus)(() => 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.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> { private async updateEnablement(stopIfDisabled: boolean, auto: boolean): Promise<void> {
@@ -99,7 +105,8 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto
this.successiveFailures = 0; 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) { if (this.enabled) {
return this.syncDelayer.trigger(() => { return this.syncDelayer.trigger(() => {
this.logService.info('Auto Sync: Triggered.'); this.logService.info('Auto Sync: Triggered.');

View File

@@ -278,7 +278,7 @@ export interface IUserDataSyncService {
readonly conflictsSources: SyncSource[]; readonly conflictsSources: SyncSource[];
readonly onDidChangeConflicts: Event<SyncSource[]>; readonly onDidChangeConflicts: Event<SyncSource[]>;
readonly onDidChangeLocal: Event<void>; readonly onDidChangeLocal: Event<SyncSource>;
readonly onSyncErrors: Event<[SyncSource, UserDataSyncError][]>; readonly onSyncErrors: Event<[SyncSource, UserDataSyncError][]>;
readonly lastSyncTime: number | undefined; readonly lastSyncTime: number | undefined;
@@ -299,7 +299,7 @@ export const IUserDataAutoSyncService = createDecorator<IUserDataAutoSyncService
export interface IUserDataAutoSyncService { export interface IUserDataAutoSyncService {
_serviceBrand: any; _serviceBrand: any;
readonly onError: Event<UserDataSyncError>; readonly onError: Event<UserDataSyncError>;
triggerAutoSync(): Promise<void>; triggerAutoSync(sources: string[]): Promise<void>;
} }
export const IUserDataSyncUtilService = createDecorator<IUserDataSyncUtilService>('IUserDataSyncUtilService'); 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> { call(context: any, command: string, args?: any): Promise<any> {
switch (command) { switch (command) {
case 'triggerAutoSync': return this.service.triggerAutoSync(); case 'triggerAutoSync': return this.service.triggerAutoSync(args[0]);
} }
throw new Error('Invalid call'); 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>()); private _onDidChangeStatus: Emitter<SyncStatus> = this._register(new Emitter<SyncStatus>());
readonly onDidChangeStatus: Event<SyncStatus> = this._onDidChangeStatus.event; readonly onDidChangeStatus: Event<SyncStatus> = this._onDidChangeStatus.event;
readonly onDidChangeLocal: Event<void>; readonly onDidChangeLocal: Event<SyncSource>;
private _conflictsSources: SyncSource[] = []; private _conflictsSources: SyncSource[] = [];
get conflictsSources(): SyncSource[] { return this._conflictsSources; } 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._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> { 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 { IElectronService } from 'vs/platform/electron/node/electron';
import { UserDataAutoSyncService as BaseUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataAutoSyncService'; import { UserDataAutoSyncService as BaseUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataAutoSyncService';
import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication'; import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
export class UserDataAutoSyncService extends BaseUserDataAutoSyncService { export class UserDataAutoSyncService extends BaseUserDataAutoSyncService {
@@ -17,15 +18,15 @@ export class UserDataAutoSyncService extends BaseUserDataAutoSyncService {
@IElectronService electronService: IElectronService, @IElectronService electronService: IElectronService,
@IUserDataSyncLogService logService: IUserDataSyncLogService, @IUserDataSyncLogService logService: IUserDataSyncLogService,
@IAuthenticationTokenService authTokenService: IAuthenticationTokenService, @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<string, string[]>(Event.any<string>(
this._register(Event.debounce(Event.any<any>( Event.map(electronService.onWindowFocus, () => 'windowFocus'),
electronService.onWindowFocus, Event.map(electronService.onWindowOpen, () => 'windowOpen'),
electronService.onWindowOpen,
userDataSyncService.onDidChangeLocal, 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 * 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 * To get an instance of a `FileSystemWatcher` use
* [createFileSystemWatcher](#workspace.createFileSystemWatcher). * [createFileSystemWatcher](#workspace.createFileSystemWatcher).
@@ -1806,7 +1806,7 @@ declare module 'vscode' {
placeHolder?: string; 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; password?: boolean;
@@ -2020,7 +2020,7 @@ declare module 'vscode' {
* Base kind for source actions: `source` * 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 * 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. * can be run on save using `editor.codeActionsOnSave` and are also shown in the `source` context menu.
*/ */
static readonly Source: CodeActionKind; static readonly Source: CodeActionKind;
@@ -2086,7 +2086,7 @@ declare module 'vscode' {
/** /**
* Requested kind of actions to return. * 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; readonly only?: CodeActionKind;
} }
@@ -2138,7 +2138,15 @@ declare module 'vscode' {
/** /**
* Marks that the code action cannot currently be applied. * 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?: { disabled?: {
/** /**
@@ -2163,7 +2171,7 @@ declare module 'vscode' {
/** /**
* The code action interface defines the contract between extensions and * 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. * 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 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 { export interface EvaluatableExpressionProvider {
/** /**
* Provide an evaluatable expression for the given document and position. * 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. * 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 document The document for which the debug hover is about to appear.
* @param position The position in the document where the debug hover is opened. * @param position The line and character position in the document where the debug hover is about to appear.
* @param token A cancellation token. * @param token A cancellation token.
* @return An EvaluatableExpression or a thenable that resolves to such. The lack of a result can be * @return An EvaluatableExpression or a thenable that resolves to such. The lack of a result can be
* signaled by returning `undefined` or `null`. * signaled by returning `undefined` or `null`.
@@ -9174,6 +9184,7 @@ declare module 'vscode' {
/** /**
* Register a provider that locates evaluatable expressions in text documents. * 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. * 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 { export interface AuthenticationSession {
id: string; id: string;
accessToken(): Promise<string>; getAccessToken(): Thenable<string>;
accountName: string; accountName: string;
scopes: string[] scopes: string[]
} }
@@ -58,13 +58,13 @@ declare module 'vscode' {
/** /**
* Returns an array of current sessions. * Returns an array of current sessions.
*/ */
getSessions(): Promise<ReadonlyArray<AuthenticationSession>>; getSessions(): Thenable<ReadonlyArray<AuthenticationSession>>;
/** /**
* Prompts a user to login. * Prompts a user to login.
*/ */
login(scopes: string[]): Promise<AuthenticationSession>; login(scopes: string[]): Thenable<AuthenticationSession>;
logout(sessionId: string): Promise<void>; logout(sessionId: string): Thenable<void>;
} }
export namespace authentication { 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 * 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. * 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. * Represents a custom document for a custom webview editor.
* *
* Custom documents are only used within a given `WebviewCustomEditorProvider`. The lifecycle of a * Custom documents are only used within a given `CustomEditorProvider`. The lifecycle of a
* `WebviewEditorCustomDocument` is managed by VS Code. When more more references remain to a given `WebviewEditorCustomDocument` * `CustomDocument` is managed by VS Code. When more more references remain to a given `CustomDocument`
* then it is disposed of. * then it is disposed of.
* *
* @param UserDataType Type of custom object that extensions can store on the document. * @param UserDataType Type of custom object that extensions can store on the document.
@@ -1297,7 +1297,7 @@ declare module 'vscode' {
readonly uri: Uri; 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>; readonly onDidDispose: Event<void>;
@@ -1313,7 +1313,7 @@ declare module 'vscode' {
/** /**
* Provider for webview editors that use a custom data model. * 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. * 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 * 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 { 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>; resolveCustomDocument(document: CustomDocument): Thenable<CustomEditorCapabilities>;
/** /**
* Resolve a webview editor for a given resource. * 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`. * 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 document Document for the 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. * @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`. * 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`. * 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 { 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`. * the event listeners it is interested it. The provider should also take ownership of the passed in `WebviewPanel`.
* *
* @param document Resource being resolved. * @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. * @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 { namespace window {
@@ -1680,9 +1682,10 @@ declare module 'vscode' {
* *
* The documentation is shown in the code actions menu if either: * 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 * - Code actions of `kind` are requested by VS Code. In this case, VS Code will show the documentation that
* documentation. For example, if documentation for both `Refactor` and `RefactorExtract` is provided, and we * most closely matches the requested code action kind. For example, if a provider has documentation for
* request code actions for `RefactorExtract`, we prefer the more specific documentation for `RefactorExtract`. * 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. * - Any code actions of `kind` are returned by the provider.
*/ */
@@ -1703,7 +1706,10 @@ declare module 'vscode' {
*/ */
export interface OpenDialogOptions { 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; title?: string;
} }
@@ -1713,7 +1719,10 @@ declare module 'vscode' {
*/ */
export interface SaveDialogOptions { 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; title?: string;
} }

View File

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

View File

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

View File

@@ -624,7 +624,7 @@ export interface ExtHostWebviewsShape {
$onSave(resource: UriComponents, viewType: string): Promise<void>; $onSave(resource: UriComponents, viewType: string): Promise<void>;
$onSaveAs(resource: UriComponents, viewType: string, targetResource: UriComponents): 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 { export interface MainThreadUrlsShape extends IDisposable {

View File

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

View File

@@ -71,11 +71,17 @@ export class ExtHostTimeline implements IExtHostTimeline {
scheme: scheme, scheme: scheme,
onDidChange: undefined, onDidChange: undefined,
async provideTimeline(uri: URI, options: TimelineOptions, token: CancellationToken, internalOptions?: { cacheResults?: boolean }) { async provideTimeline(uri: URI, options: TimelineOptions, token: CancellationToken, internalOptions?: { cacheResults?: boolean }) {
timelineDisposables.clear();
// For now, only allow the caching of a single Uri // For now, only allow the caching of a single Uri
if (internalOptions?.cacheResults && !itemsBySourceByUriMap.has(getUriKey(uri))) { if (internalOptions?.cacheResults) {
itemsBySourceByUriMap.clear(); if (options.cursor === undefined) {
timelineDisposables.clear();
}
if (!itemsBySourceByUriMap.has(getUriKey(uri))) {
itemsBySourceByUriMap.clear();
}
} else {
timelineDisposables.clear();
} }
const result = await provider.provideTimeline(uri, options, token); 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; type EditType = unknown;
class WebviewEditorCustomDocument extends Disposable implements vscode.CustomDocument { class CustomDocument extends Disposable implements vscode.CustomDocument {
private _currentEditIndex: number = -1;
private _savePoint: number = -1;
private readonly _edits: Array<EditType> = [];
public userData: unknown; public static create(proxy: MainThreadWebviewsShape, viewType: string, uri: vscode.Uri) {
return Object.seal(new CustomDocument(proxy, viewType, uri));
public _capabilities?: vscode.CustomEditorCapabilities = undefined;
constructor(
private readonly _proxy: MainThreadWebviewsShape,
public readonly viewType: string,
public readonly uri: vscode.Uri,
) {
super();
} }
_setCapabilities(capabilities: vscode.CustomEditorCapabilities) { // Explicitly initialize all properties as we seal the object after creation!
if (this._capabilities) {
throw new Error('Capabilities already provided');
}
this._capabilities = capabilities; #currentEditIndex: number = -1;
capabilities.editing?.onDidEdit(edit => { #savePoint: number = -1;
this.pushEdit(edit, this); 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 //#region Public API
#_onDidDispose = this._register(new Emitter<void>()); public get viewType(): string { return this.#viewType; }
public readonly onDidDispose = this.#_onDidDispose.event;
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 //#endregion
dispose() { //#region Internal
this.#_onDidDispose.fire();
super.dispose(); /** @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) { /** @internal*/ _revert() {
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() {
const editing = this.getEditingCapability(); const editing = this.getEditingCapability();
if (this._currentEditIndex === this._savePoint) { if (this.#currentEditIndex === this.#savePoint) {
return true; return true;
} }
if (this._currentEditIndex >= this._savePoint) { if (this.#currentEditIndex >= this.#savePoint) {
const editsToUndo = this._edits.slice(this._savePoint, this._currentEditIndex); const editsToUndo = this.#edits.slice(this.#savePoint, this.#currentEditIndex);
editing.undoEdits(editsToUndo.reverse()); editing.undoEdits(editsToUndo.reverse());
} else if (this._currentEditIndex < this._savePoint) { } else if (this.#currentEditIndex < this.#savePoint) {
const editsToRedo = this._edits.slice(this._currentEditIndex, this._savePoint); const editsToRedo = this.#edits.slice(this.#currentEditIndex, this.#savePoint);
editing.applyEdits(editsToRedo); editing.applyEdits(editsToRedo);
} }
this._currentEditIndex = this._savePoint; this.#currentEditIndex = this.#savePoint;
this.spliceEdits(); this.spliceEdits();
this.updateState(); this.updateState();
return true; return true;
} }
undo() { /** @internal*/ _undo() {
const editing = this.getEditingCapability(); const editing = this.getEditingCapability();
if (this._currentEditIndex < 0) { if (this.#currentEditIndex < 0) {
// nothing to undo // nothing to undo
return; return;
} }
const undoneEdit = this._edits[this._currentEditIndex]; const undoneEdit = this.#edits[this.#currentEditIndex];
--this._currentEditIndex; --this.#currentEditIndex;
editing.undoEdits([undoneEdit]); editing.undoEdits([undoneEdit]);
this.updateState(); this.updateState();
} }
redo() { /** @internal*/ _redo() {
const editing = this.getEditingCapability(); const editing = this.getEditingCapability();
if (this._currentEditIndex >= this._edits.length - 1) { if (this.#currentEditIndex >= this.#edits.length - 1) {
// nothing to redo // nothing to redo
return; return;
} }
++this._currentEditIndex; ++this.#currentEditIndex;
const redoneEdit = this._edits[this._currentEditIndex]; const redoneEdit = this.#edits[this.#currentEditIndex];
editing.applyEdits([redoneEdit]); editing.applyEdits([redoneEdit]);
this.updateState(); this.updateState();
} }
save() { /** @internal*/ _save() {
return this.getEditingCapability().save(); return this.getEditingCapability().save();
} }
saveAs(target: vscode.Uri) { /** @internal*/ _saveAs(target: vscode.Uri) {
return this.getEditingCapability().saveAs(target); return this.getEditingCapability().saveAs(target);
} }
backup(cancellation: CancellationToken) { /** @internal*/ _backup(cancellation: CancellationToken) {
return this.getEditingCapability().backup(cancellation); 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 { private getEditingCapability(): vscode.CustomEditorEditingCapability {
if (!this._capabilities?.editing) { if (!this.#capabilities?.editing) {
throw new Error('Document is not editable'); throw new Error('Document is not editable');
} }
return this._capabilities.editing; return this.#capabilities.editing;
} }
} }
class WebviewDocumentStore { 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)); return this._documents.get(this.key(viewType, resource));
} }
public add(document: WebviewEditorCustomDocument) { public add(document: CustomDocument) {
const key = this.key(document.viewType, document.uri); const key = this.key(document.viewType, document.uri);
if (this._documents.has(key)) { if (this._documents.has(key)) {
throw new Error(`Document already exists for viewType:${document.viewType} resource:${document.uri}`); throw new Error(`Document already exists for viewType:${document.viewType} resource:${document.uri}`);
@@ -391,7 +408,7 @@ class WebviewDocumentStore {
this._documents.set(key, document); this._documents.set(key, document);
} }
public delete(document: WebviewEditorCustomDocument) { public delete(document: CustomDocument) {
const key = this.key(document.viewType, document.uri); const key = this.key(document.viewType, document.uri);
this._documents.delete(key); this._documents.delete(key);
} }
@@ -622,7 +639,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
} }
const revivedResource = URI.revive(resource); 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); const capabilities = await entry.provider.resolveCustomDocument(document);
document._setCapabilities(capabilities); document._setCapabilities(capabilities);
this._documents.add(document); this._documents.add(document);
@@ -687,39 +704,39 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
async $undo(resourceComponents: UriComponents, viewType: string): Promise<void> { async $undo(resourceComponents: UriComponents, viewType: string): Promise<void> {
const document = this.getDocument(viewType, resourceComponents); const document = this.getDocument(viewType, resourceComponents);
document.undo(); document._undo();
} }
async $redo(resourceComponents: UriComponents, viewType: string): Promise<void> { async $redo(resourceComponents: UriComponents, viewType: string): Promise<void> {
const document = this.getDocument(viewType, resourceComponents); const document = this.getDocument(viewType, resourceComponents);
document.redo(); document._redo();
} }
async $revert(resourceComponents: UriComponents, viewType: string): Promise<void> { async $revert(resourceComponents: UriComponents, viewType: string): Promise<void> {
const document = this.getDocument(viewType, resourceComponents); const document = this.getDocument(viewType, resourceComponents);
document.revert(); document._revert();
} }
async $onSave(resourceComponents: UriComponents, viewType: string): Promise<void> { async $onSave(resourceComponents: UriComponents, viewType: string): Promise<void> {
const document = this.getDocument(viewType, resourceComponents); const document = this.getDocument(viewType, resourceComponents);
document.save(); document._save();
} }
async $onSaveAs(resourceComponents: UriComponents, viewType: string, targetResource: UriComponents): Promise<void> { async $onSaveAs(resourceComponents: UriComponents, viewType: string, targetResource: UriComponents): Promise<void> {
const document = this.getDocument(viewType, resourceComponents); 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); const document = this.getDocument(viewType, resourceComponents);
return document.backup(cancellation); return document._backup(cancellation);
} }
private getWebviewPanel(handle: WebviewPanelHandle): ExtHostWebviewEditor | undefined { private getWebviewPanel(handle: WebviewPanelHandle): ExtHostWebviewEditor | undefined {
return this._webviewPanels.get(handle); 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)); const document = this._documents.get(viewType, URI.revive(resource));
if (!document) { if (!document) {
throw new Error('No webview editor custom document found'); 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 { SideBarVisibleContext } from 'vs/workbench/common/viewlet';
import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IViewDescriptorService, IViewContainersRegistry, Extensions as ViewContainerExtensions, IViewsService, FocusedViewContext, ViewContainerLocation } from 'vs/workbench/common/views'; 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 { INotificationService } from 'vs/platform/notification/common/notification';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; 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 registry = Registry.as<IWorkbenchActionRegistry>(WorkbenchExtensions.WorkbenchActions);
const viewCategory = nls.localize('view', "View"); const viewCategory = nls.localize('view', "View");
@@ -534,6 +535,7 @@ export class MoveFocusedViewAction extends Action {
@IQuickInputService private quickInputService: IQuickInputService, @IQuickInputService private quickInputService: IQuickInputService,
@IContextKeyService private contextKeyService: IContextKeyService, @IContextKeyService private contextKeyService: IContextKeyService,
@INotificationService private notificationService: INotificationService, @INotificationService private notificationService: INotificationService,
@IActivityBarService private activityBarService: IActivityBarService,
@IViewletService private viewletService: IViewletService @IViewletService private viewletService: IViewletService
) { ) {
super(id, label); super(id, label);
@@ -542,58 +544,64 @@ export class MoveFocusedViewAction extends Action {
run(): Promise<void> { run(): Promise<void> {
const viewContainerRegistry = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry); 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.")); this.notificationService.error(nls.localize('moveFocusedView.error.noFocusedView', "There is no view currently focused."));
return Promise.resolve(); return Promise.resolve();
} }
const viewDescriptor = this.viewDescriptorService.getViewDescriptor(focusedView); const viewDescriptor = this.viewDescriptorService.getViewDescriptor(focusedViewId);
if (!viewDescriptor || !viewDescriptor.canMoveView) { 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(); return Promise.resolve();
} }
const quickPick = this.quickInputService.createQuickPick(); const quickPick = this.quickInputService.createQuickPick();
quickPick.placeholder = nls.localize('moveFocusedView.selectDestination', "Select a destination area for the view..."); quickPick.placeholder = nls.localize('moveFocusedView.selectDestination', "Select a Destination for the View");
quickPick.autoFocusOnList = true;
quickPick.items = [ const pinnedViewlets = this.activityBarService.getPinnedViewletIds();
{ const items: Array<IQuickPickItem | IQuickPickSeparator> = this.viewletService.getViewlets()
id: 'sidebar', .filter(viewlet => {
label: nls.localize('sidebar', "Sidebar") if (viewlet.id === this.viewDescriptorService.getViewContainer(focusedViewId)!.id) {
}, return false;
{ }
id: 'panel',
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") label: nls.localize('panel', "Panel")
} });
]; items.push({
id: '_.panel.newcontainer',
label: nls.localize('moveFocusedView.newContainerInPanel', "New Container in Panel"),
});
}
quickPick.items = items;
quickPick.onDidAccept(() => { quickPick.onDidAccept(() => {
const destination = quickPick.selectedItems[0]; const destination = quickPick.selectedItems[0];
if (destination.id === 'panel') { if (destination.id === '_.panel.newcontainer') {
quickPick.hide();
this.viewDescriptorService.moveViewToLocation(viewDescriptor!, ViewContainerLocation.Panel); this.viewDescriptorService.moveViewToLocation(viewDescriptor!, ViewContainerLocation.Panel);
this.viewsService.openView(focusedView, true); this.viewsService.openView(focusedViewId, 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;
} else if (destination.id) { } else if (destination.id) {
quickPick.hide();
this.viewDescriptorService.moveViewsToContainer([viewDescriptor], viewContainerRegistry.get(destination.id)!); this.viewDescriptorService.moveViewsToContainer([viewDescriptor], viewContainerRegistry.get(destination.id)!);
this.viewsService.openView(focusedView, true); this.viewsService.openView(focusedViewId, true);
return;
} }
quickPick.hide(); 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 // --- Resize View

View File

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

View File

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

View File

@@ -544,14 +544,16 @@ export class CompositeActionViewItem extends ActivityActionViewItem {
if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype)) { if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype)) {
const data = this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype); const data = this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype);
if (Array.isArray(data) && data[0].id !== this.activity.id) { 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)) { if (this.compositeTransfer.hasData(DraggedViewIdentifier.prototype)) {
const data = this.compositeTransfer.getData(DraggedViewIdentifier.prototype); const data = this.compositeTransfer.getData(DraggedViewIdentifier.prototype);
if (Array.isArray(data) && data[0].id !== this.activity.id) { 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); const data = this.compositeTransfer.getData(DraggedViewIdentifier.prototype);
if (Array.isArray(data)) { if (Array.isArray(data)) {
const draggedViewId = data[0].id; 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); this.dndHandler.drop(new CompositeDragAndDropData('view', draggedViewId), this.activity.id, e);
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -46,7 +46,7 @@ export class NotificationsStatus extends Disposable {
if (!this.isNotificationsCenterVisible) { if (!this.isNotificationsCenterVisible) {
if (e.kind === NotificationChangeType.ADD) { if (e.kind === NotificationChangeType.ADD) {
this.newNotificationsCount++; this.newNotificationsCount++;
} else if (e.kind === NotificationChangeType.REMOVE) { } else if (e.kind === NotificationChangeType.REMOVE && this.newNotificationsCount > 0) {
this.newNotificationsCount--; 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 = { 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, command: this.isNotificationsCenterVisible ? HIDE_NOTIFICATIONS_CENTER : SHOW_NOTIFICATIONS_CENTER,
tooltip: this.getTooltip(notificationsInProgress), tooltip: this.getTooltip(notificationsInProgress),
showBeak: this.isNotificationsCenterVisible 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 }; const toast: INotificationToast = { item, list: notificationList, container: notificationToastContainer, toast: notificationToast, toDispose: itemDisposables };
this.mapNotificationToToast.set(item, toast); this.mapNotificationToToast.set(item, toast);
itemDisposables.add(toDisposable(() => { // When disposed, remove as visible
if (this.isToastVisible(toast) && notificationsToastsContainer) { itemDisposables.add(toDisposable(() => this.updateToastVisibility(toast, false)));
notificationsToastsContainer.removeChild(toast.container);
}
}));
// Make visible // Make visible
notificationList.show(); notificationList.show();
@@ -236,6 +233,9 @@ export class NotificationsToasts extends Themable implements INotificationsToast
addClass(notificationToast, 'notification-fade-in-done'); addClass(notificationToast, 'notification-fade-in-done');
})); }));
// Mark as visible
item.updateVisibility(true);
// Events // Events
if (!this._isVisible) { if (!this._isVisible) {
this._isVisible = true; this._isVisible = true;
@@ -292,12 +292,13 @@ export class NotificationsToasts extends Themable implements INotificationsToast
} }
private removeToast(item: INotificationViewItem): void { private removeToast(item: INotificationViewItem): void {
let focusEditor = false;
const notificationToast = this.mapNotificationToToast.get(item); const notificationToast = this.mapNotificationToToast.get(item);
let focusGroup = false;
if (notificationToast) { if (notificationToast) {
const toastHasDOMFocus = isAncestor(document.activeElement, notificationToast.container); const toastHasDOMFocus = isAncestor(document.activeElement, notificationToast.container);
if (toastHasDOMFocus) { 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 // Listeners
@@ -317,7 +318,7 @@ export class NotificationsToasts extends Themable implements INotificationsToast
this.doHide(); this.doHide();
// Move focus back to editor group as needed // Move focus back to editor group as needed
if (focusGroup) { if (focusEditor) {
this.editorGroupService.activeGroup.focus(); this.editorGroupService.activeGroup.focus();
} }
} }
@@ -346,11 +347,11 @@ export class NotificationsToasts extends Themable implements INotificationsToast
} }
hide(): void { hide(): void {
const focusGroup = this.notificationsToastsContainer ? isAncestor(document.activeElement, this.notificationsToastsContainer) : false; const focusEditor = this.notificationsToastsContainer ? isAncestor(document.activeElement, this.notificationsToastsContainer) : false;
this.removeToasts(); this.removeToasts();
if (focusGroup) { if (focusEditor) {
this.editorGroupService.activeGroup.focus(); this.editorGroupService.activeGroup.focus();
} }
} }
@@ -459,12 +460,12 @@ export class NotificationsToasts extends Themable implements INotificationsToast
notificationToasts.push(toast); notificationToasts.push(toast);
break; break;
case ToastVisibility.HIDDEN: case ToastVisibility.HIDDEN:
if (!this.isToastVisible(toast)) { if (!this.isToastInDOM(toast)) {
notificationToasts.push(toast); notificationToasts.push(toast);
} }
break; break;
case ToastVisibility.VISIBLE: case ToastVisibility.VISIBLE:
if (this.isToastVisible(toast)) { if (this.isToastInDOM(toast)) {
notificationToasts.push(toast); notificationToasts.push(toast);
} }
break; 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 // In order to measure the client height, the element cannot have display: none
toast.container.style.opacity = '0'; toast.container.style.opacity = '0';
this.setVisibility(toast, true); this.updateToastVisibility(toast, true);
heightToGive -= toast.container.offsetHeight; heightToGive -= toast.container.offsetHeight;
@@ -542,7 +543,7 @@ export class NotificationsToasts extends Themable implements INotificationsToast
} }
// Hide or show toast based on context // Hide or show toast based on context
this.setVisibility(toast, makeVisible); this.updateToastVisibility(toast, makeVisible);
toast.container.style.opacity = ''; toast.container.style.opacity = '';
if (makeVisible) { if (makeVisible) {
@@ -551,20 +552,24 @@ export class NotificationsToasts extends Themable implements INotificationsToast
}); });
} }
private setVisibility(toast: INotificationToast, visible: boolean): void { private updateToastVisibility(toast: INotificationToast, visible: boolean): void {
if (this.isToastVisible(toast) === visible) { if (this.isToastInDOM(toast) === visible) {
return; return;
} }
// Update visibility in DOM
const notificationsToastsContainer = assertIsDefined(this.notificationsToastsContainer); const notificationsToastsContainer = assertIsDefined(this.notificationsToastsContainer);
if (visible) { if (visible) {
notificationsToastsContainer.appendChild(toast.container); notificationsToastsContainer.appendChild(toast.container);
} else { } else {
notificationsToastsContainer.removeChild(toast.container); 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; 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 { .monaco-workbench .part.panel > .title > .panel-switcher-container > .monaco-action-bar {
line-height: 27px; /* matches panel titles in settings */ line-height: 27px; /* matches panel titles in settings */
height: 35px; 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 { ViewContainer, IViewContainersRegistry, Extensions as ViewContainerExtensions, IViewDescriptorService, IViewDescriptorCollection, ViewContainerLocation } from 'vs/workbench/common/views';
import { MenuId } from 'vs/platform/actions/common/actions'; import { MenuId } from 'vs/platform/actions/common/actions';
import { ViewMenuActions } from 'vs/workbench/browser/parts/views/viewMenuActions'; import { ViewMenuActions } from 'vs/workbench/browser/parts/views/viewMenuActions';
import { IPaneComposite } from 'vs/workbench/common/panecomposite';
interface ICachedPanel { interface ICachedPanel {
id: string; id: string;
@@ -143,7 +144,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
getDefaultCompositeId: () => this.panelRegistry.getDefaultPanelId(), getDefaultCompositeId: () => this.panelRegistry.getDefaultPanelId(),
hidePart: () => this.layoutService.setPanelHidden(true), hidePart: () => this.layoutService.setPanelHidden(true),
dndHandler: new CompositeDragAndDrop(this.viewDescriptorService, ViewContainerLocation.Panel, 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), (from: string, to: string) => this.compositeBar.move(from, to),
() => this.getPinnedPanels().map(p => p.id) () => this.getPinnedPanels().map(p => p.id)
), ),

View File

@@ -255,7 +255,10 @@ export abstract class ViewPane extends Pane implements IView {
this._onDidFocus.fire(); this._onDidFocus.fire();
})); }));
this._register(focusTracker.onDidBlur(() => { this._register(focusTracker.onDidBlur(() => {
this.focusedViewContextKey.reset(); if (this.focusedViewContextKey.get() === this.id) {
this.focusedViewContextKey.reset();
}
this._onDidBlur.fire(); this._onDidBlur.fire();
})); }));
} }
@@ -1006,7 +1009,8 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
} }
if (!this.areExtensionsReady) { if (!this.areExtensionsReady) {
if (this.visibleViewsCountFromCache === undefined) { 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 // Check in cache so that view do not jump. See #29609
return this.visibleViewsCountFromCache === 1; 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. * 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 { toErrorMessage } from 'vs/base/common/errorMessage';
import { Event, Emitter } from 'vs/base/common/event'; import { Event, Emitter } from 'vs/base/common/event';
import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; 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>()); private readonly _onDidClose = this._register(new Emitter<void>());
readonly onDidClose = this._onDidClose.event; 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) { constructor(private readonly item: INotificationViewItem, private readonly onClose: (item: INotificationViewItem) => void) {
super(); super();
@@ -99,6 +102,11 @@ export class NotificationHandle extends Disposable implements INotificationHandl
} }
private registerListeners(): void { private registerListeners(): void {
// Visibility
this._register(this.item.onDidChangeVisibility(visible => this._onDidChangeVisibility.fire(visible)));
// Closing
Event.once(this.item.onDidClose)(() => { Event.once(this.item.onDidClose)(() => {
this._onDidClose.fire(); this._onDidClose.fire();
@@ -265,6 +273,7 @@ export interface INotificationViewItem {
readonly onDidChangeExpansion: Event<void>; readonly onDidChangeExpansion: Event<void>;
readonly onDidClose: Event<void>; readonly onDidClose: Event<void>;
readonly onDidChangeVisibility: Event<boolean>;
readonly onDidChangeLabel: Event<INotificationViewItemLabelChangeEvent>; readonly onDidChangeLabel: Event<INotificationViewItemLabelChangeEvent>;
expand(): void; expand(): void;
@@ -275,6 +284,8 @@ export interface INotificationViewItem {
updateMessage(message: NotificationMessage): void; updateMessage(message: NotificationMessage): void;
updateActions(actions?: INotificationActions): void; updateActions(actions?: INotificationActions): void;
updateVisibility(visible: boolean): void;
close(): void; close(): void;
equals(item: INotificationViewItem): boolean; equals(item: INotificationViewItem): boolean;
@@ -398,6 +409,7 @@ export class NotificationViewItem extends Disposable implements INotificationVie
private static readonly MAX_MESSAGE_LENGTH = 1000; private static readonly MAX_MESSAGE_LENGTH = 1000;
private _expanded: boolean | undefined; private _expanded: boolean | undefined;
private _visible: boolean = false;
private _actions: INotificationActions | undefined; private _actions: INotificationActions | undefined;
private _progress: NotificationViewItemProgress | undefined; private _progress: NotificationViewItemProgress | undefined;
@@ -411,6 +423,9 @@ export class NotificationViewItem extends Disposable implements INotificationVie
private readonly _onDidChangeLabel = this._register(new Emitter<INotificationViewItemLabelChangeEvent>()); private readonly _onDidChangeLabel = this._register(new Emitter<INotificationViewItemLabelChangeEvent>());
readonly onDidChangeLabel = this._onDidChangeLabel.event; 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 { static create(notification: INotification, filter: NotificationsFilter = NotificationsFilter.OFF): INotificationViewItem | undefined {
if (!notification || !notification.message || isPromiseCanceledError(notification.message)) { if (!notification || !notification.message || isPromiseCanceledError(notification.message)) {
return undefined; // we need a message to show return undefined; // we need a message to show
@@ -435,7 +450,7 @@ export class NotificationViewItem extends Disposable implements INotificationVie
actions = { primary: notification.message.actions }; 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 { private static parseNotificationMessage(input: NotificationMessage): INotificationMessage | undefined {
@@ -472,13 +487,30 @@ export class NotificationViewItem extends Disposable implements INotificationVie
private _silent: boolean | undefined, private _silent: boolean | undefined,
private _message: INotificationMessage, private _message: INotificationMessage,
private _source: string | undefined, private _source: string | undefined,
progress: INotificationProgressProperties | undefined,
actions?: INotificationActions actions?: INotificationActions
) { ) {
super(); super();
if (progress) {
this.setProgress(progress);
}
this.setActions(actions); 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 { private setActions(actions: INotificationActions = { primary: [], secondary: [] }): void {
if (!Array.isArray(actions.primary)) { if (!Array.isArray(actions.primary)) {
actions.primary = []; actions.primary = [];
@@ -583,6 +615,14 @@ export class NotificationViewItem extends Disposable implements INotificationVie
this._onDidChangeLabel.fire({ kind: NotificationViewItemLabelKind.ACTIONS }); this._onDidChangeLabel.fire({ kind: NotificationViewItemLabelKind.ACTIONS });
} }
updateVisibility(visible: boolean): void {
if (this._visible !== visible) {
this._visible = visible;
this._onDidChangeVisibility.fire(visible);
}
}
expand(): void { expand(): void {
if (this._expanded || !this.canCollapse) { if (this._expanded || !this.canCollapse) {
return; return;

View File

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

View File

@@ -111,6 +111,7 @@ class BulkEditPreviewContribution {
private async _previewEdit(edit: WorkspaceEdit) { private async _previewEdit(edit: WorkspaceEdit) {
this._ctxEnabled.set(true); this._ctxEnabled.set(true);
const uxState = this._activeSession?.uxState ?? new UXState(this._panelService, this._editorGroupsService);
const view = await getBulkEditPane(this._viewsService); const view = await getBulkEditPane(this._viewsService);
if (!view) { if (!view) {
this._ctxEnabled.set(false); this._ctxEnabled.set(false);
@@ -136,9 +137,9 @@ class BulkEditPreviewContribution {
let session: PreviewSession; let session: PreviewSession;
if (this._activeSession) { if (this._activeSession) {
this._activeSession.cts.dispose(true); this._activeSession.cts.dispose(true);
session = new PreviewSession(this._activeSession.uxState); session = new PreviewSession(uxState);
} else { } else {
session = new PreviewSession(new UXState(this._panelService, this._editorGroupsService)); session = new PreviewSession(uxState);
} }
this._activeSession = session; 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 { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { Registry } from 'vs/platform/registry/common/platform'; import { Registry } from 'vs/platform/registry/common/platform';
import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; 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 { export class ToggleColumnSelectionAction extends Action {
public static readonly ID = 'editor.action.toggleColumnSelection'; public static readonly ID = 'editor.action.toggleColumnSelection';
@@ -18,26 +25,68 @@ export class ToggleColumnSelectionAction extends Action {
constructor( constructor(
id: string, id: string,
label: string, label: string,
@IConfigurationService private readonly _configurationService: IConfigurationService @IConfigurationService private readonly _configurationService: IConfigurationService,
@ICodeEditorService private readonly _codeEditorService: ICodeEditorService
) { ) {
super(id, label); super(id, label);
} }
public run(): Promise<any> { private _getCodeEditor(): ICodeEditor | null {
const newValue = !this._configurationService.getValue<boolean>('editor.columnSelection'); const codeEditor = this._codeEditorService.getFocusedCodeEditor();
return this._configurationService.updateValue('editor.columnSelection', newValue, ConfigurationTarget.USER); 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); 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, { MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, {
group: '3_multi', group: '4_config',
command: { command: {
id: ToggleColumnSelectionAction.ID, id: ToggleColumnSelectionAction.ID,
title: nls.localize({ key: 'miColumnSelection', comment: ['&& denotes a mnemonic'] }, "Column &&Selection Mode"), title: nls.localize({ key: 'miColumnSelection', comment: ['&& denotes a mnemonic'] }, "Column &&Selection Mode"),
toggled: ContextKeyExpr.equals('config.editor.columnSelection', true) 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); const registry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActions);
registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleMultiCursorModifierAction, ToggleMultiCursorModifierAction.ID, ToggleMultiCursorModifierAction.LABEL), 'Toggle Multi-Cursor Modifier'); registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleMultiCursorModifierAction, ToggleMultiCursorModifierAction.ID, ToggleMultiCursorModifierAction.LABEL), 'Toggle Multi-Cursor Modifier');
MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, { MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, {
group: '3_multi', group: '4_config',
command: { command: {
id: ToggleMultiCursorModifierAction.ID, id: ToggleMultiCursorModifierAction.ID,
title: nls.localize('miMultiCursorAlt', "Switch to Alt+Click for Multi-Cursor") title: nls.localize('miMultiCursorAlt', "Switch to Alt+Click for Multi-Cursor")
@@ -79,7 +79,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, {
order: 1 order: 1
}); });
MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, { MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, {
group: '3_multi', group: '4_config',
command: { command: {
id: ToggleMultiCursorModifierAction.ID, id: ToggleMultiCursorModifierAction.ID,
title: ( title: (

View File

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

View File

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

View File

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

View File

@@ -29,7 +29,7 @@ namespace HotExitState {
readonly type = Type.Pending; readonly type = Type.Pending;
constructor( 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>()); private readonly _onWillSaveAs = this._register(new Emitter<CustomEditorSaveAsEvent>());
public readonly onWillSaveAs = this._onWillSaveAs.event; 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) { if (this._onBackup) {
throw new Error('Backup already implemented'); throw new Error('Backup already implemented');
} }
@@ -182,7 +182,11 @@ export class CustomEditorModel extends Disposable implements ICustomEditorModel
this._hotExitState = pendingState; this._hotExitState = pendingState;
try { 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) { } catch (e) {
// Make sure state has not changed in the meantime // Make sure state has not changed in the meantime
if (this._hotExitState === pendingState) { if (this._hotExitState === pendingState) {

View File

@@ -267,7 +267,7 @@ configurationRegistry.registerConfiguration({
default: true default: true
}, },
'debug.onTaskErrors': { '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.")], 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."), description: nls.localize('debug.onTaskErrors', "Controls what to do when errors are encountered after running a preLaunchTask."),
default: 'prompt' default: 'prompt'

View File

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

View File

@@ -209,7 +209,7 @@ export class DebugHoverWidget implements IContentWidget {
if (!matchingExpression) { if (!matchingExpression) {
const lineContent = model.getLineContent(pos.lineNumber); 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 { IProductService } from 'vs/platform/product/common/productService';
import { IWorkspaceFolder, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IWorkspaceFolder, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IDisposable, dispose } from 'vs/base/common/lifecycle'; 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 { generateUuid } from 'vs/base/common/uuid';
import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IHostService } from 'vs/workbench/services/host/browser/host';
import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug'; import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug';
@@ -807,62 +807,58 @@ export class DebugSession implements IDebugSession {
this._onDidChangeState.fire(); this._onDidChangeState.fire();
})); }));
let outpuPromises: Promise<void>[] = []; const outputQueue = new Queue<void>();
this.rawListeners.push(this.raw.onDidOutput(async event => { this.rawListeners.push(this.raw.onDidOutput(async event => {
if (!event.body || !this.raw) { outputQueue.queue(async () => {
return; if (!event.body || !this.raw) {
}
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
return; return;
} }
}
if (event.body.variablesReference) { const outputSeverity = event.body.category === 'stderr' ? severity.Error : event.body.category === 'console' ? severity.Warning : severity.Info;
const container = new ExpressionContainer(this, undefined, event.body.variablesReference, generateUuid()); if (event.body.category === 'telemetry') {
outpuPromises.push(container.getChildren().then(async children => { // only log telemetry events from debug adapter if the debug extension provided the telemetry key
await Promise.all(waitFor); // and the user opted in telemetry
children.forEach(child => { if (this.raw.customTelemetryService && this.telemetryService.isOptedIn) {
// Since we can not display multiple trees in a row, we are displaying these variables one after the other (ignoring their names) // __GDPR__TODO__ We're sending events in the name of the debug extension and we can not ensure that those are declared correctly.
(<any>child).name = null; this.raw.customTelemetryService.publicLog(event.body.output, event.body.data);
this.appendToRepl(child, outputSeverity, source); }
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') {
} else if (typeof event.body.output === 'string') { this.appendToRepl(event.body.output, outputSeverity, source);
await Promise.all(waitFor); }
this.appendToRepl(event.body.output, outputSeverity, source); });
}
await Promise.all(outpuPromises);
outpuPromises = [];
})); }));
this.rawListeners.push(this.raw.onDidBreakpoint(event => { 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(editorService.onDidActiveEditorChange(setContextKey));
this._register(this.debugService.getConfigurationManager().onDidRegisterDebugger(setContextKey)); this._register(this.debugService.getConfigurationManager().onDidRegisterDebugger(setContextKey));
this.registerViews(); setContextKey();
const debugKeybinding = this.keybindingService.lookupKeybinding(StartAction.ID);
debugKeybindingLabel = debugKeybinding ? ` (${debugKeybinding.getLabel()})` : '';
} }
shouldShowWelcome(): boolean { shouldShowWelcome(): boolean {
return true; 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"), name: localize('extensions', "Extensions"),
ctorDescriptor: new SyncDescriptor(ExtensionsViewPaneContainer), ctorDescriptor: new SyncDescriptor(ExtensionsViewPaneContainer),
icon: 'codicon-extensions', icon: 'codicon-extensions',
order: 14 // {{SQL CARBON EDIT}} order: 14, // {{SQL CARBON EDIT}}
rejectAddedViews: true,
}, ViewContainerLocation.Sidebar); }, ViewContainerLocation.Sidebar);

View File

@@ -2690,7 +2690,7 @@ export class SystemDisabledWarningAction extends ExtensionAction {
if (server) { if (server) {
this.tooltip = localize('Install in other server to enable', "Install the extension on '{0}' to enable.", server.label); this.tooltip = localize('Install in other server to enable', "Install the extension on '{0}' to enable.", server.label);
} else { } 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; return;
} }

View File

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

View File

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

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