Merge branch 'main' into dev/t-andreamah/markdown-static-preview-scroll-state

This commit is contained in:
Andrea Mah 2021-05-19 12:15:49 -06:00
commit 44d135e94f
251 changed files with 6507 additions and 2955 deletions

View file

@ -20,8 +20,7 @@
"context-keys": {"assign": []},
"css-less-scss": {"assign": ["aeschli"]},
"custom-editors": {"assign": ["mjbvz"]},
"debug": {"assign": ["isidorn"]},
"debug-console": {"assign": ["isidorn"]},
"debug": {"assign": ["weinand"]},
"dialogs": {"assign": ["sbatten"]},
"diff-editor": {"assign": []},
"dropdown": {"assign": []},
@ -81,9 +80,10 @@
"icon-brand": {"assign": []},
"icons-product": {"assign": ["misolori"]},
"install-update": {"assign": []},
"integrated-terminal": {"assign": ["meganrogge"]},
"integrated-terminal-conpty": {"assign": ["meganrogge"]},
"integrated-terminal-links": {"assign": ["meganrogge"]},
"terminal": {"assign": ["meganrogge"]},
"terminal-conpty": {"assign": ["meganrogge"]},
"terminal-links": {"assign": ["meganrogge"]},
"terminal-external": {"assign": ["meganrogge"]},
"integration-test": {"assign": []},
"intellisense-config": {"assign": []},
"ipc": {"assign": ["joaomoreno"]},

View file

@ -1,3 +1,3 @@
disturl "https://electronjs.org/headers"
target "12.0.4"
target "12.0.7"
runtime "electron"

View file

@ -10,7 +10,7 @@ This repository ("`Code - OSS`") is where we (Microsoft) develop the [Visual Stu
## Visual Studio Code
<p align="center">
<img alt="VS Code in action" src="https://user-images.githubusercontent.com/1487073/58344409-70473b80-7e0a-11e9-8570-b2efc6f8fa44.png">
<img alt="VS Code in action" src="https://user-images.githubusercontent.com/35271042/118224532-3842c400-b438-11eb-923d-a5f66fa6785a.png">
</p>
[Visual Studio Code](https://code.visualstudio.com) is a distribution of the `Code - OSS` repository with Microsoft specific customizations released under a traditional [Microsoft product license](https://code.visualstudio.com/License/).

View file

@ -228,7 +228,14 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op
.pipe(jsFilter)
.pipe(util.rewriteSourceMappingURL(sourceMappingURLBase))
.pipe(jsFilter.restore)
.pipe(createAsar(path.join(process.cwd(), 'node_modules'), ['**/*.node', '**/vscode-ripgrep/bin/*', '**/node-pty/build/Release/*', '**/*.wasm'], 'node_modules.asar'));
.pipe(createAsar(path.join(process.cwd(), 'node_modules'), [
'**/*.node',
'**/vscode-ripgrep/bin/*',
'**/node-pty/build/Release/*',
'**/node-pty/lib/worker/conoutSocketWorker.js',
'**/node-pty/lib/shared/conout.js',
'**/*.wasm'
], 'node_modules.asar'));
let all = es.merge(
packageJsonStream,

View file

@ -6,7 +6,7 @@
"git": {
"name": "chromium",
"repositoryUrl": "https://chromium.googlesource.com/chromium/src",
"commitHash": "5342041f85833c038dcbc5632d62fc10f7592323"
"commitHash": "cd7a46bf02a768a1aabf9443f6ee469bc6e28e7c"
}
},
"licenseDetail": [
@ -40,7 +40,7 @@
"SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
],
"isOnlyProductionDependency": true,
"version": "89.0.4389.114"
"version": "89.0.4389.128"
},
{
"component": {
@ -60,12 +60,12 @@
"git": {
"name": "electron",
"repositoryUrl": "https://github.com/electron/electron",
"commitHash": "9ce7c512475aa6aa91417a3b08e19f85a8587a30"
"commitHash": "8d55658bfa8b5983e1a90ad079c2e2ac91ee7af0"
}
},
"isOnlyProductionDependency": true,
"license": "MIT",
"version": "12.0.4"
"version": "12.0.7"
},
{
"component": {

View file

@ -54,6 +54,19 @@
"type": "string",
"description": "Label that will be shown in the UI for this port.",
"default": "Application"
},
"requireLocalPort": {
"type": "boolean",
"markdownDescription": "When true, a modal dialog will show if the chosen local port isn't used for forwarding.",
"default": false
},
"protocol": {
"type": "string",
"enum": [
"http",
"https"
],
"description": "The protocol to use when forwarding this port."
}
},
"default": {
@ -80,7 +93,13 @@
"properties": {
"onAutoForward": {
"type": "string",
"enum": ["notify", "openBrowser", "openPreview", "silent", "ignore"],
"enum": [
"notify",
"openBrowser",
"openPreview",
"silent",
"ignore"
],
"enumDescriptions": [
"Shows a notification when a port is automatically forwarded.",
"Opens the browser when the port is automatically forwarded. Depending on your settings, this could open an embedded browser.",
@ -100,9 +119,28 @@
"type": "string",
"description": "Label that will be shown in the UI for this port.",
"default": "Application"
},
"requireLocalPort": {
"type": "boolean",
"markdownDescription": "When true, a modal dialog will show if the chosen local port isn't used for forwarding.",
"default": false
},
"protocol": {
"type": "string",
"enum": [
"http",
"https"
],
"description": "The protocol to use when forwarding this port."
}
},
"defaultSnippets": [{ "body": { "onAutoForward": "ignore" } }],
"defaultSnippets": [
{
"body": {
"onAutoForward": "ignore"
}
}
],
"markdownDescription": "Set default properties that are applied to all ports that don't get properties from the setting `remote.portsAttributes`. For example:\n\n```\n{\n \"onAutoForward\": \"ignore\"\n}\n```",
"additionalProperties": false
},

View file

@ -162,6 +162,19 @@
"type": "string",
"description": "Label that will be shown in the UI for this port.",
"default": "Application"
},
"requireLocalPort": {
"type": "boolean",
"markdownDescription": "When true, a modal dialog will show if the chosen local port isn't used for forwarding.",
"default": false
},
"protocol": {
"type": "string",
"enum": [
"http",
"https"
],
"description": "The protocol to use when forwarding this port."
}
},
"default": {
@ -215,6 +228,19 @@
"type": "string",
"description": "Label that will be shown in the UI for this port.",
"default": "Application"
},
"requireLocalPort": {
"type": "boolean",
"markdownDescription": "When true, a modal dialog will show if the chosen local port isn't used for forwarding.",
"default": false
},
"protocol": {
"type": "string",
"enum": [
"http",
"https"
],
"description": "The protocol to use when forwarding this port."
}
},
"defaultSnippets": [
@ -461,6 +487,19 @@
"type": "string",
"description": "Label that will be shown in the UI for this port.",
"default": "Application"
},
"requireLocalPort": {
"type": "boolean",
"markdownDescription": "When true, a modal dialog will show if the chosen local port isn't used for forwarding.",
"default": false
},
"protocol": {
"type": "string",
"enum": [
"http",
"https"
],
"description": "The protocol to use when forwarding this port."
}
},
"default": {
@ -514,6 +553,19 @@
"type": "string",
"description": "Label that will be shown in the UI for this port.",
"default": "Application"
},
"requireLocalPort": {
"type": "boolean",
"markdownDescription": "When true, a modal dialog will show if the chosen local port isn't used for forwarding.",
"default": false
},
"protocol": {
"type": "string",
"enum": [
"http",
"https"
],
"description": "The protocol to use when forwarding this port."
}
},
"defaultSnippets": [
@ -736,6 +788,19 @@
"type": "string",
"description": "Label that will be shown in the UI for this port.",
"default": "Application"
},
"requireLocalPort": {
"type": "boolean",
"markdownDescription": "When true, a modal dialog will show if the chosen local port isn't used for forwarding.",
"default": false
},
"protocol": {
"type": "string",
"enum": [
"http",
"https"
],
"description": "The protocol to use when forwarding this port."
}
},
"default": {
@ -789,6 +854,19 @@
"type": "string",
"description": "Label that will be shown in the UI for this port.",
"default": "Application"
},
"requireLocalPort": {
"type": "boolean",
"markdownDescription": "When true, a modal dialog will show if the chosen local port isn't used for forwarding.",
"default": false
},
"protocol": {
"type": "string",
"enum": [
"http",
"https"
],
"description": "The protocol to use when forwarding this port."
}
},
"defaultSnippets": [
@ -977,6 +1055,19 @@
"type": "string",
"description": "Label that will be shown in the UI for this port.",
"default": "Application"
},
"requireLocalPort": {
"type": "boolean",
"markdownDescription": "When true, a modal dialog will show if the chosen local port isn't used for forwarding.",
"default": false
},
"protocol": {
"type": "string",
"enum": [
"http",
"https"
],
"description": "The protocol to use when forwarding this port."
}
},
"default": {
@ -1030,6 +1121,19 @@
"type": "string",
"description": "Label that will be shown in the UI for this port.",
"default": "Application"
},
"requireLocalPort": {
"type": "boolean",
"markdownDescription": "When true, a modal dialog will show if the chosen local port isn't used for forwarding.",
"default": false
},
"protocol": {
"type": "string",
"enum": [
"http",
"https"
],
"description": "The protocol to use when forwarding this port."
}
},
"defaultSnippets": [
@ -1187,6 +1291,19 @@
"type": "string",
"description": "Label that will be shown in the UI for this port.",
"default": "Application"
},
"requireLocalPort": {
"type": "boolean",
"markdownDescription": "When true, a modal dialog will show if the chosen local port isn't used for forwarding.",
"default": false
},
"protocol": {
"type": "string",
"enum": [
"http",
"https"
],
"description": "The protocol to use when forwarding this port."
}
},
"default": {
@ -1240,6 +1357,19 @@
"type": "string",
"description": "Label that will be shown in the UI for this port.",
"default": "Application"
},
"requireLocalPort": {
"type": "boolean",
"markdownDescription": "When true, a modal dialog will show if the chosen local port isn't used for forwarding.",
"default": false
},
"protocol": {
"type": "string",
"enum": [
"http",
"https"
],
"description": "The protocol to use when forwarding this port."
}
},
"defaultSnippets": [

View file

@ -68,6 +68,16 @@
"type": "string",
"description": "Label that will be shown in the UI for this port.",
"default": "Application"
},
"requireLocalPort": {
"type": "boolean",
"markdownDescription": "When true, a modal dialog will show if the chosen local port isn't used for forwarding.",
"default": false
},
"protocol": {
"type": "string",
"enum": ["http", "https"],
"description": "The protocol to use when forwarding this port."
}
},
"default": {
@ -120,6 +130,16 @@
"type": "string",
"description": "Label that will be shown in the UI for this port.",
"default": "Application"
},
"requireLocalPort": {
"type": "boolean",
"markdownDescription": "When true, a modal dialog will show if the chosen local port isn't used for forwarding.",
"default": false
},
"protocol": {
"type": "string",
"enum": ["http", "https"],
"description": "The protocol to use when forwarding this port."
}
},
"defaultSnippets": [

View file

@ -315,12 +315,10 @@ function updateStatusBar(context: vscode.ExtensionContext, state: State, busy =
}
if (!statusItem) {
statusItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
statusItem = vscode.window.createStatusBarItem('status.debug.autoAttach', vscode.StatusBarAlignment.Left);
statusItem.name = localize('status.name.auto.attach', "Debug Auto Attach");
statusItem.command = TOGGLE_COMMAND;
statusItem.tooltip = localize(
'status.tooltip.auto.attach',
'Automatically attach to node.js processes in debug mode',
);
statusItem.tooltip = localize('status.tooltip.auto.attach', "Automatically attach to node.js processes in debug mode");
context.subscriptions.push(statusItem);
}

View file

@ -179,7 +179,8 @@ export class GitHubServer {
private updateStatusBarItem(isStart?: boolean) {
if (isStart && !this._statusBarItem) {
this._statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
this._statusBarItem = vscode.window.createStatusBarItem('status.git.signIn', vscode.StatusBarAlignment.Left);
this._statusBarItem.name = localize('status.git.signIn.name', "GitHub Sign-in");
this._statusBarItem.text = this.type === AuthProviderType.github
? localize('signingIn', "$(mark-github) Signing in to github.com...")
: localize('signingInEnterprise', "$(mark-github) Signing in to {0}...", this.getServerUri().authority);

View file

@ -39,12 +39,7 @@ class BinarySize {
export class BinarySizeStatusBarEntry extends PreviewStatusBarEntry {
constructor() {
super({
id: 'imagePreview.binarySize',
name: localize('sizeStatusBar.name', "Image Binary Size"),
alignment: vscode.StatusBarAlignment.Right,
priority: 100,
});
super('status.imagePreview.binarySize', localize('sizeStatusBar.name', "Image Binary Size"), vscode.StatusBarAlignment.Right, 100);
}
public show(owner: string, size: number | undefined) {

View file

@ -11,9 +11,10 @@ export abstract class PreviewStatusBarEntry extends Disposable {
protected readonly entry: vscode.StatusBarItem;
constructor(options: vscode.StatusBarItemOptions) {
constructor(id: string, name: string, alignment: vscode.StatusBarAlignment, priority: number) {
super();
this.entry = this._register(vscode.window.createStatusBarItem(options));
this.entry = this._register(vscode.window.createStatusBarItem(id, alignment, priority));
this.entry.name = name;
}
protected showItem(owner: string, text: string) {

View file

@ -12,12 +12,7 @@ const localize = nls.loadMessageBundle();
export class SizeStatusBarEntry extends PreviewStatusBarEntry {
constructor() {
super({
id: 'imagePreview.size',
name: localize('sizeStatusBar.name', "Image Size"),
alignment: vscode.StatusBarAlignment.Right,
priority: 101 /* to the left of editor status (100) */,
});
super('status.imagePreview.size', localize('sizeStatusBar.name', "Image Size"), vscode.StatusBarAlignment.Right, 101 /* to the left of editor status (100) */);
}
public show(owner: string, text: string) {

View file

@ -19,12 +19,7 @@ export class ZoomStatusBarEntry extends OwnedStatusBarEntry {
public readonly onDidChangeScale = this._onDidChangeScale.event;
constructor() {
super({
id: 'imagePreview.zoom',
name: localize('zoomStatusBar.name', "Image Zoom"),
alignment: vscode.StatusBarAlignment.Right,
priority: 102 /* to the left of editor size entry (101) */,
});
super('status.imagePreview.zoom', localize('zoomStatusBar.name', "Image Zoom"), vscode.StatusBarAlignment.Right, 102 /* to the left of editor size entry (101) */);
this._register(vscode.commands.registerCommand(selectZoomLevelCommandId, async () => {
type MyPickItem = vscode.QuickPickItem & { scale: Scale };

View file

@ -101,12 +101,8 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua
const documentSelector = ['json', 'jsonc'];
const schemaResolutionErrorStatusBarItem = window.createStatusBarItem({
id: 'status.json.resolveError',
name: localize('json.resolveError', "JSON: Schema Resolution Error"),
alignment: StatusBarAlignment.Right,
priority: 0,
});
const schemaResolutionErrorStatusBarItem = window.createStatusBarItem('status.json.resolveError', StatusBarAlignment.Right, 0);
schemaResolutionErrorStatusBarItem.name = localize('json.resolveError', "JSON: Schema Resolution Error");
schemaResolutionErrorStatusBarItem.text = '$(alert)';
toDispose.push(schemaResolutionErrorStatusBarItem);

View file

@ -1,7 +1,7 @@
{
"description": "Extension to add task support for npm scripts.",
"displayName": "NPM support for VS Code",
"workspaceTrust": "This extension calls the `tasks.executeTask()` API, which requires trust to run.",
"workspaceTrust": "This extension executes tasks, which require trust to run.",
"config.npm.autoDetect": "Controls whether npm scripts should be automatically detected.",
"config.npm.runSilent": "Run npm commands with the `--silent` option.",
"config.npm.packageManager": "The package manager used to run scripts.",

View file

@ -25,7 +25,7 @@
"notebook.cellBorderColor": "#E8E8E8",
"notebook.selectedCellBackground": "#c8ddf150",
"statusBarItem.errorBackground": "#c72e0f",
"list.focusHighlightForeground": "#33B6FF"
"list.focusHighlightForeground": "#9DDDFF"
},
"tokenColors": [
{

View file

@ -135,12 +135,8 @@ export default class VersionStatus extends Disposable {
) {
super();
this._statusBarEntry = this._register(vscode.window.createStatusBarItem({
id: 'status.typescript',
name: localize('projectInfo.name', "TypeScript: Project Info"),
alignment: vscode.StatusBarAlignment.Right,
priority: 99 /* to the right of editor status (100) */
}));
this._statusBarEntry = this._register(vscode.window.createStatusBarItem('status.typescript', vscode.StatusBarAlignment.Right, 99 /* to the right of editor status (100) */));
this._statusBarEntry.name = localize('projectInfo.name', "TypeScript: Project Info");
const command = new ProjectStatusCommand(this._client, () => this._state);
commandManager.register(command);

View file

@ -23,12 +23,8 @@ class ExcludeHintItem {
constructor(
private readonly telemetryReporter: TelemetryReporter
) {
this._item = vscode.window.createStatusBarItem({
id: 'status.typescript.exclude',
name: localize('statusExclude', "TypeScript: Configure Excludes"),
alignment: vscode.StatusBarAlignment.Right,
priority: 98 /* to the right of typescript version status (99) */
});
this._item = vscode.window.createStatusBarItem('status.typescript.exclude', vscode.StatusBarAlignment.Right, 98 /* to the right of typescript version status (99) */);
this._item.name = localize('statusExclude', "TypeScript: Configure Excludes");
this._item.command = 'js.projectStatus.command';
}

View file

@ -91,16 +91,27 @@ suite('Notebook Document', function () {
test('notebook open/close, notebook ready when cell-document open event is fired', async function () {
const uri = await utils.createRandomFile(undefined, undefined, '.nbdtest');
let didHappen = false;
const p = utils.asPromise(vscode.workspace.onDidOpenTextDocument).then(doc => {
if (doc.uri.scheme !== 'vscode-notebook-cell') {
return;
}
const notebook = vscode.notebook.notebookDocuments.find(notebook => {
const cell = notebook.getCells().find(cell => cell.document === doc);
return Boolean(cell);
const p = new Promise<void>((resolve, reject) => {
const sub = vscode.workspace.onDidOpenTextDocument(doc => {
if (doc.uri.scheme !== 'vscode-notebook-cell') {
// ignore other open events
return;
}
const notebook = vscode.notebook.notebookDocuments.find(notebook => {
const cell = notebook.getCells().find(cell => cell.document === doc);
return Boolean(cell);
});
assert.ok(notebook, `notebook for cell ${doc.uri} NOT found`);
didHappen = true;
sub.dispose();
resolve();
});
assert.ok(notebook, `notebook for cell ${doc.uri} NOT found`);
didHappen = true;
setTimeout(() => {
sub.dispose();
reject(new Error('TIMEOUT'));
}, 15000);
});
await vscode.notebook.openNotebookDocument(uri);

View file

@ -440,12 +440,12 @@ suite('Notebook API tests', function () {
await cellsChangeEvent;
await cellMetadataChangeEvent;
assert.strictEqual(vscode.window.activeNotebookEditor!.document.cellCount, 3);
assert.strictEqual(vscode.window.activeNotebookEditor!.document.cellAt(0)?.metadata?.inputCollapsed, false);
assert.strictEqual(vscode.window.activeNotebookEditor!.document.cellAt(0)?.metadata.inputCollapsed, false);
assert.strictEqual(version + 1, vscode.window.activeNotebookEditor!.document.version);
await vscode.commands.executeCommand('undo');
assert.strictEqual(version + 2, vscode.window.activeNotebookEditor!.document.version);
assert.strictEqual(vscode.window.activeNotebookEditor!.document.cellAt(0)?.metadata?.inputCollapsed, undefined);
assert.strictEqual(vscode.window.activeNotebookEditor!.document.cellAt(0)?.metadata.inputCollapsed, undefined);
assert.strictEqual(vscode.window.activeNotebookEditor!.document.cellCount, 2);
});

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { workspace, window, commands, ViewColumn, TextEditorViewColumnChangeEvent, Uri, Selection, Position, CancellationTokenSource, TextEditorSelectionChangeKind, QuickPickItem, TextEditor } from 'vscode';
import { workspace, window, commands, ViewColumn, TextEditorViewColumnChangeEvent, Uri, Selection, Position, CancellationTokenSource, TextEditorSelectionChangeKind, QuickPickItem, TextEditor, StatusBarAlignment } from 'vscode';
import { join } from 'path';
import { closeAllEditors, pathEquals, createRandomFile, assertNoRpc } from '../utils';
@ -638,4 +638,22 @@ suite('vscode API - window', () => {
});
});
test('createStatusBar', async function () {
const statusBarEntryWithoutId = window.createStatusBarItem(StatusBarAlignment.Left, 100);
assert.strictEqual(statusBarEntryWithoutId.id, 'vscode.vscode-api-tests');
assert.strictEqual(statusBarEntryWithoutId.alignment, StatusBarAlignment.Left);
assert.strictEqual(statusBarEntryWithoutId.priority, 100);
assert.strictEqual(statusBarEntryWithoutId.name, undefined);
statusBarEntryWithoutId.name = 'Test Name';
assert.strictEqual(statusBarEntryWithoutId.name, 'Test Name');
const statusBarEntryWithId = window.createStatusBarItem('testId', StatusBarAlignment.Right, 200);
assert.strictEqual(statusBarEntryWithId.alignment, StatusBarAlignment.Right);
assert.strictEqual(statusBarEntryWithId.priority, 200);
assert.strictEqual(statusBarEntryWithId.id, 'testId');
assert.strictEqual(statusBarEntryWithId.name, undefined);
statusBarEntryWithId.name = 'Test Name';
assert.strictEqual(statusBarEntryWithId.name, 'Test Name');
});
});

View file

@ -137,3 +137,9 @@ export async function asPromise<T>(event: vscode.Event<T>, timeout = vscode.env.
});
});
}
export function testRepeat(n: number, description: string, callback: (this: any) => any): void {
for (let i = 0; i < n; i++) {
test(`${description} (iteration ${i})`, callback);
}
}

View file

@ -1,7 +1,7 @@
{
"name": "code-oss-dev",
"version": "1.57.0",
"distro": "0a045304c3e628e1aaf00003d2c9063264bd9bb5",
"distro": "ecdb89a80900940251ce4a8083f6b2e4b032b583",
"author": {
"name": "Microsoft Corporation"
},
@ -69,7 +69,7 @@
"native-is-elevated": "0.4.3",
"native-keymap": "2.2.1",
"native-watchdog": "1.3.0",
"node-pty": "0.10.1",
"node-pty": "0.11.0-beta7",
"nsfw": "2.1.2",
"spdlog": "^0.13.0",
"sudo-prompt": "9.2.1",
@ -81,10 +81,10 @@
"vscode-ripgrep": "^1.11.3",
"vscode-sqlite3": "4.0.11",
"vscode-textmate": "5.4.0",
"xterm": "4.12.0-beta.26",
"xterm": "4.13.0-beta.1",
"xterm-addon-search": "0.9.0-beta.2",
"xterm-addon-unicode11": "0.3.0-beta.5",
"xterm-addon-webgl": "0.11.0-beta.8",
"xterm-addon-webgl": "0.11.1",
"yauzl": "^2.9.2",
"yazl": "^2.4.3"
},
@ -125,7 +125,7 @@
"cssnano": "^4.1.11",
"debounce": "^1.0.0",
"deemon": "^1.4.0",
"electron": "12.0.4",
"electron": "12.0.7",
"eslint": "6.8.0",
"eslint-plugin-jsdoc": "^19.1.0",
"event-stream": "3.3.4",

View file

@ -48,7 +48,7 @@
},
{
"name": "ms-vscode.node-debug2",
"version": "1.42.6",
"version": "1.42.7",
"repo": "https://github.com/microsoft/vscode-node-debug2",
"metadata": {
"id": "36d19e17-7569-4841-a001-947eb18602b2",

View file

@ -13,7 +13,7 @@
"jschardet": "3.0.0",
"minimist": "^1.2.5",
"native-watchdog": "1.3.0",
"node-pty": "0.10.1",
"node-pty": "0.11.0-beta7",
"nsfw": "2.1.2",
"spdlog": "^0.13.0",
"tas-client-umd": "0.1.4",
@ -22,10 +22,10 @@
"vscode-regexpp": "^3.1.0",
"vscode-ripgrep": "^1.11.3",
"vscode-textmate": "5.4.0",
"xterm": "4.12.0-beta.26",
"xterm": "4.13.0-beta.1",
"xterm-addon-search": "0.9.0-beta.2",
"xterm-addon-unicode11": "0.3.0-beta.5",
"xterm-addon-webgl": "0.11.0-beta.8",
"xterm-addon-webgl": "0.11.1",
"yauzl": "^2.9.2",
"yazl": "^2.4.3"
},

View file

@ -8,9 +8,9 @@
"tas-client-umd": "0.1.4",
"vscode-oniguruma": "1.5.1",
"vscode-textmate": "5.4.0",
"xterm": "4.12.0-beta.26",
"xterm": "4.13.0-beta.1",
"xterm-addon-search": "0.9.0-beta.2",
"xterm-addon-unicode11": "0.3.0-beta.5",
"xterm-addon-webgl": "0.11.0-beta.8"
"xterm-addon-webgl": "0.11.1"
}
}

View file

@ -37,12 +37,12 @@ xterm-addon-unicode11@0.3.0-beta.5:
resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.3.0-beta.5.tgz#7e490799d530d3b301125c7a4e92317c161761c4"
integrity sha512-SgDDL3PoMH1G48JO6T45whKAex4NPxi80UzUVitnrqyd8dFQP+oF6cxqUutULgm9HSGk62qy3mrZvIMGO5VXog==
xterm-addon-webgl@0.11.0-beta.8:
version "0.11.0-beta.8"
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.11.0-beta.8.tgz#8cb4925d67c31beb8144275daf46358f42eff9fe"
integrity sha512-udRmQ/jgH8cL8VQOZweytkToIROevVeiA7WY0tIe878Wt2zKY+AYHZV8js3c1W9wHDu5G90BhmzTidJ5UwZK3Q==
xterm-addon-webgl@0.11.1:
version "0.11.1"
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.11.1.tgz#33dd250ab52e9f51d2ff52396447962e6f53e24c"
integrity sha512-xF6DnEoV+rPtzetMBXBZVe1kLKtus7AKdEcyfq2eMHQzhaRvC+pfnU+XiCXC85kueguqu2UkBHXZs5mihK9jOQ==
xterm@4.12.0-beta.26:
version "4.12.0-beta.26"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.12.0-beta.26.tgz#57c75b732808795398a66bc1a3e06d09eaff2ada"
integrity sha512-yZB1kMBXQu2G0G1ch7TUi6f893iTZC+tmfjw/PQNZTmN46b4oX1l7rplc3sFcdrICHtmQ0Q5n1u0d6WUAdq1Kw==
xterm@4.13.0-beta.1:
version "4.13.0-beta.1"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.13.0-beta.1.tgz#ad2ad321c69a4add6e878c890f278a1da74fd7d0"
integrity sha512-gAMGqBglESTxQWph1uKVyd1jO/6eKsbicNG+Mr/YAsj06TjFVcLw839Iqu6P+DVFEV7lLLspcOb8fwX6qMBH/Q==

View file

@ -353,10 +353,10 @@ node-addon-api@*, node-addon-api@^3.0.2:
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.1.0.tgz#98b21931557466c6729e51cb77cd39c965f42239"
integrity sha512-flmrDNB06LIl5lywUz7YlNGZH/5p0M7W28k8hzd9Lshtdh1wshD2Y+U4h9LD6KObOy1f+fEVdgprPrEymjM5uw==
node-pty@0.10.1:
version "0.10.1"
resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.10.1.tgz#cd05d03a2710315ec40221232ec04186f6ac2c6d"
integrity sha512-JTdtUS0Im/yRsWJSx7yiW9rtpfmxqxolrtnyKwPLI+6XqTAPW/O2MjS8FYL4I5TsMbH2lVgDb2VMjp+9LoQGNg==
node-pty@0.11.0-beta7:
version "0.11.0-beta7"
resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.11.0-beta7.tgz#aed0888b5032d96c54d8473455e6adfae3bbebbe"
integrity sha512-uApPGLglZRiHQcUMWakbZOrBo8HVWvhzIqNnrWvBGJOvc6m/S5lCdbbg93BURyJqHFmBS0GV+4hwiMNDuGRbSA==
dependencies:
nan "^2.14.0"
@ -539,15 +539,15 @@ xterm-addon-unicode11@0.3.0-beta.5:
resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.3.0-beta.5.tgz#7e490799d530d3b301125c7a4e92317c161761c4"
integrity sha512-SgDDL3PoMH1G48JO6T45whKAex4NPxi80UzUVitnrqyd8dFQP+oF6cxqUutULgm9HSGk62qy3mrZvIMGO5VXog==
xterm-addon-webgl@0.11.0-beta.8:
version "0.11.0-beta.8"
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.11.0-beta.8.tgz#8cb4925d67c31beb8144275daf46358f42eff9fe"
integrity sha512-udRmQ/jgH8cL8VQOZweytkToIROevVeiA7WY0tIe878Wt2zKY+AYHZV8js3c1W9wHDu5G90BhmzTidJ5UwZK3Q==
xterm-addon-webgl@0.11.1:
version "0.11.1"
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.11.1.tgz#33dd250ab52e9f51d2ff52396447962e6f53e24c"
integrity sha512-xF6DnEoV+rPtzetMBXBZVe1kLKtus7AKdEcyfq2eMHQzhaRvC+pfnU+XiCXC85kueguqu2UkBHXZs5mihK9jOQ==
xterm@4.12.0-beta.26:
version "4.12.0-beta.26"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.12.0-beta.26.tgz#57c75b732808795398a66bc1a3e06d09eaff2ada"
integrity sha512-yZB1kMBXQu2G0G1ch7TUi6f893iTZC+tmfjw/PQNZTmN46b4oX1l7rplc3sFcdrICHtmQ0Q5n1u0d6WUAdq1Kw==
xterm@4.13.0-beta.1:
version "4.13.0-beta.1"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.13.0-beta.1.tgz#ad2ad321c69a4add6e878c890f278a1da74fd7d0"
integrity sha512-gAMGqBglESTxQWph1uKVyd1jO/6eKsbicNG+Mr/YAsj06TjFVcLw839Iqu6P+DVFEV7lLLspcOb8fwX6qMBH/Q==
yauzl@^2.9.2:
version "2.10.0"

View file

@ -312,6 +312,14 @@ function getInsaneOptions(options: { readonly isTrusted?: boolean }): InsaneOpti
};
}
/**
* Strips all markdown from `string`, if it's an IMarkdownString. For example
* `# Header` would be output as `Header`. If it's not, the string is returned.
*/
export function renderStringAsPlaintext(string: IMarkdownString | string) {
return typeof string === 'string' ? string : renderMarkdownAsPlaintext(string);
}
/**
* Strips all markdown from `markdown`. For example `# Header` would be output as `Header`.
*/

View file

@ -101,16 +101,17 @@ export class Dialog extends Disposable {
this.buttonsContainer = buttonsRowElement.appendChild($('.dialog-buttons'));
const messageRowElement = this.element.appendChild($('.dialog-message-row'));
this.iconElement = messageRowElement.appendChild($('.dialog-icon'));
this.iconElement = messageRowElement.appendChild($('#monaco-dialog-icon.dialog-icon'));
this.iconElement.setAttribute('aria-label', this.getIconAriaLabel());
this.messageContainer = messageRowElement.appendChild($('.dialog-message-container'));
if (this.options.detail || this.options.renderBody) {
const messageElement = this.messageContainer.appendChild($('.dialog-message'));
const messageTextElement = messageElement.appendChild($('.dialog-message-text'));
const messageTextElement = messageElement.appendChild($('#monaco-dialog-message-text.dialog-message-text'));
messageTextElement.innerText = this.message;
}
this.messageDetailElement = this.messageContainer.appendChild($('.dialog-message-detail'));
this.messageDetailElement = this.messageContainer.appendChild($('#monaco-dialog-message-detail.dialog-message-detail'));
if (this.options.detail || !this.options.renderBody) {
this.messageDetailElement.innerText = this.options.detail ? this.options.detail : message;
} else {
@ -118,7 +119,7 @@ export class Dialog extends Disposable {
}
if (this.options.renderBody) {
const customBody = this.messageContainer.appendChild($('.dialog-message-body'));
const customBody = this.messageContainer.appendChild($('#monaco-dialog-message-body.dialog-message-body'));
this.options.renderBody(customBody);
for (const el of this.messageContainer.querySelectorAll('a')) {
@ -161,7 +162,7 @@ export class Dialog extends Disposable {
this.toolbarContainer = toolbarRowElement.appendChild($('.dialog-toolbar'));
}
private getAriaLabel(): string {
private getIconAriaLabel(): string {
let typeLabel = nls.localize('dialogInfoMessage', 'Info');
switch (this.options.type) {
case 'error':
@ -180,7 +181,7 @@ export class Dialog extends Disposable {
break;
}
return `${typeLabel}: ${this.message} ${this.options.detail || ''}`;
return typeLabel;
}
updateMessage(message: string): void {
@ -390,7 +391,7 @@ export class Dialog extends Disposable {
this.applyStyles();
this.element.setAttribute('aria-label', this.getAriaLabel());
this.element.setAttribute('aria-labelledby', 'monaco-dialog-icon monaco-dialog-message-text monaco-dialog-message-detail monaco-dialog-message-body');
show(this.element);
// Focus first element (input or button)

View file

@ -22,6 +22,23 @@ export function toSlashes(osPath: string) {
return osPath.replace(/[\\/]/g, posix.sep);
}
/**
* Takes a Windows OS path (using backward or forward slashes) and turns it into a posix path:
* - turns backward slashes into forward slashes
* - makes it absolute if it starts with a drive letter
* This should only be done for OS paths from Windows (or user provided paths potentially from Windows).
* Using it on a Linux or MaxOS path might change it.
*/
export function toPosixPath(osPath: string) {
if (osPath.indexOf('/') === -1) {
osPath = toSlashes(osPath);
}
if (/^[a-zA-Z]:(\/|$)/.test(osPath)) { // starts with a drive letter
osPath = '/' + osPath;
}
return osPath;
}
/**
* Computes the _root_ this path, like `getRoot('c:\files') === c:\`,
* `getRoot('files:///files/path') === files:///`,

View file

@ -264,12 +264,7 @@ export class ExtUri implements IExtUri {
path: newURI.path
});
}
if (path.indexOf('/') === -1) { // no slashes? it's likely a Windows path
path = extpath.toSlashes(path);
if (/^[a-zA-Z]:(\/|$)/.test(path)) { // starts with a drive letter
path = '/' + path;
}
}
path = extpath.toPosixPath(path); // we allow path to be a windows path
return base.with({
path: paths.posix.resolve(base.path, path)
});

View file

@ -327,13 +327,15 @@ export class URI implements UriComponents {
}
static from(components: { scheme: string; authority?: string; path?: string; query?: string; fragment?: string }): URI {
return new Uri(
const result = new Uri(
components.scheme,
components.authority,
components.path,
components.query,
components.fragment,
);
_validateUri(result, true);
return result;
}
/**

View file

@ -49,6 +49,10 @@ body {
width: 90px;
}
.monaco-list:focus {
outline: 0;
}
.monaco-list-row:first-of-type {
border-bottom: 1px solid;
}

View file

@ -14,13 +14,15 @@ import { applyZoom, zoomIn, zoomOut } from 'vs/platform/windows/electron-sandbox
import { IContextMenuItem } from 'vs/base/parts/contextmenu/common/contextmenu';
import { popup } from 'vs/base/parts/contextmenu/electron-sandbox/contextmenu';
import { ProcessItem } from 'vs/base/common/processes';
import { append, $ } from 'vs/base/browser/dom';
import { append, $, createStyleSheet } from 'vs/base/browser/dom';
import { isRemoteDiagnosticError, IRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics';
import { ElectronIPCMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService';
import { ByteSize } from 'vs/platform/files/common/files';
import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
import { IDataSource, ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree';
import { DataTree } from 'vs/base/browser/ui/tree/dataTree';
import { getIconsStyleSheet } from 'vs/platform/theme/browser/iconsStyleSheet';
import { RunOnceScheduler } from 'vs/base/common/async';
const DEBUG_FLAGS_PATTERN = /\s--(inspect|debug)(-brk|port)?=(\d+)?/;
const DEBUG_PORT_PATTERN = /\s--(inspect|debug)-port=(\d+)/;
@ -310,8 +312,7 @@ class ProcessExplorer {
renderers,
new ProcessTreeDataSource(),
{
identityProvider:
{
identityProvider: {
getId: (element: ProcessTree | ProcessItem | MachineProcessInformation | ProcessInformation | IRemoteDiagnosticError) => {
if (isProcessItem(element)) {
return element.pid.toString();
@ -331,7 +332,7 @@ class ProcessExplorer {
return 'header';
}
}
},
});
this.tree.setInput({ processes: { processRoots } });
@ -378,21 +379,45 @@ class ProcessExplorer {
}
private applyStyles(styles: ProcessExplorerStyles): void {
const styleTag = document.createElement('style');
const styleElement = createStyleSheet();
const content: string[] = [];
if (styles.hoverBackground) {
content.push(`.monaco-list-row:hover { background-color: ${styles.hoverBackground}; }`);
if (styles.listFocusBackground) {
content.push(`.monaco-list:focus .monaco-list-row.focused { background-color: ${styles.listFocusBackground}; }`);
content.push(`.monaco-list:focus .monaco-list-row.focused:hover { background-color: ${styles.listFocusBackground}; }`);
}
if (styles.hoverForeground) {
content.push(`.monaco-list-row:hover { color: ${styles.hoverForeground}; }`);
if (styles.listFocusForeground) {
content.push(`.monaco-list:focus .monaco-list-row.focused { color: ${styles.listFocusForeground}; }`);
}
styleTag.textContent = content.join('\n');
if (document.head) {
document.head.appendChild(styleTag);
if (styles.listActiveSelectionBackground) {
content.push(`.monaco-list:focus .monaco-list-row.selected { background-color: ${styles.listActiveSelectionBackground}; }`);
content.push(`.monaco-list:focus .monaco-list-row.selected:hover { background-color: ${styles.listActiveSelectionBackground}; }`);
}
if (styles.listActiveSelectionForeground) {
content.push(`.monaco-list:focus .monaco-list-row.selected { color: ${styles.listActiveSelectionForeground}; }`);
}
if (styles.listHoverBackground) {
content.push(`.monaco-list-row:hover:not(.selected):not(.focused) { background-color: ${styles.listHoverBackground}; }`);
}
if (styles.listHoverForeground) {
content.push(`.monaco-list-row:hover:not(.selected):not(.focused) { color: ${styles.listHoverForeground}; }`);
}
if (styles.listFocusOutline) {
content.push(`.monaco-list:focus .monaco-list-row.focused { outline: 1px solid ${styles.listFocusOutline}; outline-offset: -1px; }`);
}
if (styles.listHoverOutline) {
content.push(`.monaco-list-row:hover { outline: 1px dashed ${styles.listHoverOutline}; outline-offset: -1px; }`);
}
styleElement.textContent = content.join('\n');
if (styles.color) {
document.body.style.color = styles.color;
}
@ -475,9 +500,24 @@ class ProcessExplorer {
}
}
function createCodiconStyleSheet() {
const codiconStyleSheet = createStyleSheet();
codiconStyleSheet.id = 'codiconStyles';
const iconsStyleSheet = getIconsStyleSheet();
function updateAll() {
codiconStyleSheet.textContent = iconsStyleSheet.getCSS();
}
const delayer = new RunOnceScheduler(updateAll, 0);
iconsStyleSheet.onDidChange(() => delayer.schedule());
delayer.schedule();
}
export function startup(configuration: ProcessExplorerWindowConfiguration): void {
const platformClass = configuration.data.platform === 'win32' ? 'windows' : configuration.data.platform === 'linux' ? 'linux' : 'mac';
document.body.classList.add(platformClass); // used by our fonts
createCodiconStyleSheet();
applyZoom(configuration.data.zoomLevel);
new ProcessExplorer(configuration.windowId, configuration.data);

View file

@ -222,6 +222,11 @@ export interface ICursorSimpleModel {
getLineFirstNonWhitespaceColumn(lineNumber: number): number;
getLineLastNonWhitespaceColumn(lineNumber: number): number;
normalizePosition(position: Position, affinity: PositionNormalizationAffinity): Position;
/**
* Gets the column at which indentation stops at a given line.
* @internal
*/
getLineIndentColumn(lineNumber: number): number;
}
/**

View file

@ -38,13 +38,15 @@ export class MoveOperations {
}
private static leftPositionAtomicSoftTabs(model: ICursorSimpleModel, position: Position, tabSize: number): Position {
const minColumn = model.getLineMinColumn(position.lineNumber);
const lineContent = model.getLineContent(position.lineNumber);
const newPosition = AtomicTabMoveOperations.atomicPosition(lineContent, position.column - 1, tabSize, Direction.Left);
if (newPosition === -1 || newPosition + 1 < minColumn) {
return this.leftPosition(model, position);
if (position.column <= model.getLineIndentColumn(position.lineNumber)) {
const minColumn = model.getLineMinColumn(position.lineNumber);
const lineContent = model.getLineContent(position.lineNumber);
const newPosition = AtomicTabMoveOperations.atomicPosition(lineContent, position.column - 1, tabSize, Direction.Left);
if (newPosition !== -1 && newPosition + 1 >= minColumn) {
return new Position(position.lineNumber, newPosition + 1);
}
}
return new Position(position.lineNumber, newPosition + 1);
return this.leftPosition(model, position);
}
private static left(config: CursorConfiguration, model: ICursorSimpleModel, position: Position): CursorPosition {
@ -115,12 +117,14 @@ export class MoveOperations {
}
public static rightPositionAtomicSoftTabs(model: ICursorSimpleModel, lineNumber: number, column: number, tabSize: number, indentSize: number): Position {
const lineContent = model.getLineContent(lineNumber);
const newPosition = AtomicTabMoveOperations.atomicPosition(lineContent, column - 1, tabSize, Direction.Right);
if (newPosition === -1) {
return this.rightPosition(model, lineNumber, column);
if (column < model.getLineIndentColumn(lineNumber)) {
const lineContent = model.getLineContent(lineNumber);
const newPosition = AtomicTabMoveOperations.atomicPosition(lineContent, column - 1, tabSize, Direction.Right);
if (newPosition !== -1) {
return new Position(lineNumber, newPosition + 1);
}
}
return new Position(lineNumber, newPosition + 1);
return this.rightPosition(model, lineNumber, column);
}
public static right(config: CursorConfiguration, model: ICursorSimpleModel, position: Position): CursorPosition {

View file

@ -1261,6 +1261,12 @@ export interface ITextModel {
* @internal
*/
normalizePosition(position: Position, affinity: PositionNormalizationAffinity): Position;
/**
* Gets the column at which indentation stops at a given line.
* @internal
*/
getLineIndentColumn(lineNumber: number): number;
}
/**

View file

@ -3031,6 +3031,27 @@ export class TextModel extends Disposable implements model.ITextModel {
normalizePosition(position: Position, affinity: model.PositionNormalizationAffinity): Position {
return position;
}
/**
* Gets the column at which indentation stops at a given line.
* @internal
*/
public getLineIndentColumn(lineNumber: number): number {
// Columns start with 1.
return indentOfLine(this.getLineContent(lineNumber)) + 1;
}
}
function indentOfLine(line: string): number {
let indent = 0;
for (const c of line) {
if (c === ' ' || c === '\t') {
indent++;
} else {
break;
}
}
return indent;
}
//#region Decorations

View file

@ -47,6 +47,10 @@ class LinePart {
public isWhitespace(): boolean {
return (this.metadata & LinePartMetadata.IS_WHITESPACE_MASK ? true : false);
}
public isPseudoAfter(): boolean {
return (this.metadata & LinePartMetadata.PSEUDO_AFTER_MASK ? true : false);
}
}
export class LineRange {
@ -792,14 +796,11 @@ function _applyInlineDecorations(lineContent: string, len: number, tokens: LineP
const lastTokenEndIndex = tokens[tokens.length - 1].endIndex;
if (lineDecorationIndex < lineDecorationsLen && lineDecorations[lineDecorationIndex].startOffset === lastTokenEndIndex) {
let classNames: string[] = [];
let metadata = 0;
while (lineDecorationIndex < lineDecorationsLen && lineDecorations[lineDecorationIndex].startOffset === lastTokenEndIndex) {
classNames.push(lineDecorations[lineDecorationIndex].className);
metadata |= lineDecorations[lineDecorationIndex].metadata;
const lineDecoration = lineDecorations[lineDecorationIndex];
result[resultLen++] = new LinePart(lastResultEndIndex, lineDecoration.className, lineDecoration.metadata);
lineDecorationIndex++;
}
result[resultLen++] = new LinePart(lastResultEndIndex, classNames.join(' '), metadata);
}
return result;
@ -827,6 +828,7 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render
const renderControlCharacters = input.renderControlCharacters;
const characterMapping = new CharacterMapping(len + 1, parts.length);
let lastCharacterMappingDefined = false;
let charIndex = 0;
let visibleColumn = startVisibleColumn;
@ -999,13 +1001,20 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render
partDisplacement = 0;
}
if (charIndex >= len && !lastCharacterMappingDefined && part.isPseudoAfter()) {
lastCharacterMappingDefined = true;
characterMapping.setPartData(charIndex, partIndex, charOffsetInPart, partAbsoluteOffset);
}
sb.appendASCIIString('</span>');
}
// When getting client rects for the last character, we will position the
// text range at the end of the span, insteaf of at the beginning of next span
characterMapping.setPartData(len, parts.length - 1, charOffsetInPart, partAbsoluteOffset);
if (!lastCharacterMappingDefined) {
// When getting client rects for the last character, we will position the
// text range at the end of the span, insteaf of at the beginning of next span
characterMapping.setPartData(len, parts.length - 1, charOffsetInPart, partAbsoluteOffset);
}
if (isOverflowing) {
sb.appendASCIIString('<span>&hellip;</span>');

View file

@ -78,6 +78,11 @@ export interface IViewModelLinesCollection extends IDisposable {
getDecorationsInRange(range: Range, ownerId: number, filterOutValidation: boolean): IModelDecoration[];
normalizePosition(position: Position, affinity: PositionNormalizationAffinity): Position;
/**
* Gets the column at which indentation stops at a given line.
* @internal
*/
getLineIndentColumn(lineNumber: number): number;
}
export class CoordinatesConverter implements ICoordinatesConverter {
@ -983,6 +988,22 @@ export class SplitLinesCollection implements IViewModelLinesCollection {
return this.lines[lineIndex].normalizePosition(this.model, lineIndex + 1, remainder, position, affinity);
}
public getLineIndentColumn(lineNumber: number): number {
const viewLineNumber = this._toValidViewLineNumber(lineNumber);
const r = this.prefixSumComputer.getIndexOf(viewLineNumber - 1);
const lineIndex = r.index;
const remainder = r.remainder;
if (remainder === 0) {
return this.model.getLineIndentColumn(lineIndex + 1);
}
// wrapped lines have no indentation.
// We deliberately don't handle the case that indentation is wrapped
// to avoid two view lines reporting indentation for the very same model line.
return 0;
}
}
class VisibleIdentitySplitLine implements ISplitLine {
@ -1575,6 +1596,10 @@ export class IdentityLinesCollection implements IViewModelLinesCollection {
normalizePosition(position: Position, affinity: PositionNormalizationAffinity): Position {
return this.model.normalizePosition(position, affinity);
}
public getLineIndentColumn(lineNumber: number): number {
return this.model.getLineIndentColumn(lineNumber);
}
}
class OverviewRulerDecorations {

View file

@ -1041,4 +1041,12 @@ export class ViewModel extends Disposable implements IViewModel {
normalizePosition(position: Position, affinity: PositionNormalizationAffinity): Position {
return this._lines.normalizePosition(position, affinity);
}
/**
* Gets the column at which indentation stops at a given line.
* @internal
*/
getLineIndentColumn(lineNumber: number): number {
return this._lines.getLineIndentColumn(lineNumber);
}
}

View file

@ -2760,6 +2760,34 @@ suite('Editor Controller - Regression tests', () => {
}
);
});
test('issue #123178: sticky tab in consecutive wrapped lines', () => {
const model = createTextModel(' aaaa aaaa', { tabSize: 4 });
withTestCodeEditor(
null,
{
model: model,
wordWrap: 'wordWrapColumn',
wordWrapColumn: 8,
stickyTabStops: true,
},
(editor, viewModel) => {
viewModel.setSelections('test', [
new Selection(1, 9, 1, 9)
]);
moveRight(editor, viewModel, false);
assertCursor(viewModel, [
new Selection(1, 10, 1, 10),
]);
moveLeft(editor, viewModel, false);
assertCursor(viewModel, [
new Selection(1, 9, 1, 9),
]);
}
);
});
});
suite('Editor Controller - Cursor Configuration', () => {

View file

@ -809,64 +809,64 @@ suite('viewLineRenderer.renderLine', () => {
const _expected = decodeCharacterMapping(expected);
assert.deepStrictEqual(_actual, _expected);
}
function assertCharacterMapping(actual: CharacterMapping, expectedCharPartOffsets: number[][], expectedPartLengths: number[]): void {
assertCharPartOffsets(actual, expectedCharPartOffsets);
let expectedCharAbsoluteOffset: number[] = [], currentPartAbsoluteOffset = 0;
for (let partIndex = 0; partIndex < expectedCharPartOffsets.length; partIndex++) {
const part = expectedCharPartOffsets[partIndex];
for (const charIndex of part) {
expectedCharAbsoluteOffset.push(currentPartAbsoluteOffset + charIndex);
}
currentPartAbsoluteOffset += expectedPartLengths[partIndex];
}
let actualCharOffset: number[] = [];
let tmp = actual.getAbsoluteOffsets();
for (let i = 0; i < tmp.length; i++) {
actualCharOffset[i] = tmp[i];
}
assert.deepStrictEqual(actualCharOffset, expectedCharAbsoluteOffset);
}
function assertCharPartOffsets(actual: CharacterMapping, expected: number[][]): void {
let charOffset = 0;
for (let partIndex = 0; partIndex < expected.length; partIndex++) {
let part = expected[partIndex];
for (const charIndex of part) {
// here
let _actualPartData = actual.charOffsetToPartData(charOffset);
let actualPartIndex = CharacterMapping.getPartIndex(_actualPartData);
let actualCharIndex = CharacterMapping.getCharIndex(_actualPartData);
assert.deepStrictEqual(
{ partIndex: actualPartIndex, charIndex: actualCharIndex },
{ partIndex: partIndex, charIndex: charIndex },
`character mapping for offset ${charOffset}`
);
// here
let actualOffset = actual.partDataToCharOffset(partIndex, part[part.length - 1] + 1, charIndex);
assert.strictEqual(
actualOffset,
charOffset,
`character mapping for part ${partIndex}, ${charIndex}`
);
charOffset++;
}
}
assert.strictEqual(actual.length, charOffset);
}
});
function assertCharacterMapping(actual: CharacterMapping, expectedCharPartOffsets: number[][], expectedPartLengths: number[]): void {
assertCharPartOffsets(actual, expectedCharPartOffsets);
let expectedCharAbsoluteOffset: number[] = [], currentPartAbsoluteOffset = 0;
for (let partIndex = 0; partIndex < expectedCharPartOffsets.length; partIndex++) {
const part = expectedCharPartOffsets[partIndex];
for (const charIndex of part) {
expectedCharAbsoluteOffset.push(currentPartAbsoluteOffset + charIndex);
}
currentPartAbsoluteOffset += expectedPartLengths[partIndex];
}
let actualCharOffset: number[] = [];
let tmp = actual.getAbsoluteOffsets();
for (let i = 0; i < tmp.length; i++) {
actualCharOffset[i] = tmp[i];
}
assert.deepStrictEqual(actualCharOffset, expectedCharAbsoluteOffset);
}
function assertCharPartOffsets(actual: CharacterMapping, expected: number[][]): void {
let charOffset = 0;
for (let partIndex = 0; partIndex < expected.length; partIndex++) {
let part = expected[partIndex];
for (const charIndex of part) {
// here
let _actualPartData = actual.charOffsetToPartData(charOffset);
let actualPartIndex = CharacterMapping.getPartIndex(_actualPartData);
let actualCharIndex = CharacterMapping.getCharIndex(_actualPartData);
assert.deepStrictEqual(
{ partIndex: actualPartIndex, charIndex: actualCharIndex },
{ partIndex: partIndex, charIndex: charIndex },
`character mapping for offset ${charOffset}`
);
// here
let actualOffset = actual.partDataToCharOffset(partIndex, part[part.length - 1] + 1, charIndex);
assert.strictEqual(
actualOffset,
charOffset,
`character mapping for part ${partIndex}, ${charIndex}`
);
charOffset++;
}
}
assert.strictEqual(actual.length, charOffset);
}
suite('viewLineRenderer.renderLine 2', () => {
function testCreateLineParts(fontIsMonospace: boolean, lineContent: string, tokens: ViewLineToken[], fauxIndentLength: number, renderWhitespace: 'none' | 'boundary' | 'selection' | 'trailing' | 'all', selections: LineRange[] | null, expected: string): void {
@ -1739,7 +1739,7 @@ suite('viewLineRenderer.renderLine 2', () => {
let expected = [
'<span>',
'<span class="mtk3">\u00a0\u00a0\u00a0\u00a0}</span>',
'<span class="ced-TextEditorDecorationType2-5e9b9b3f-3 ced-TextEditorDecorationType2-3 ced-TextEditorDecorationType2-5e9b9b3f-4 ced-TextEditorDecorationType2-4"></span>',
'<span class="ced-TextEditorDecorationType2-5e9b9b3f-3 ced-TextEditorDecorationType2-3"></span><span class="ced-TextEditorDecorationType2-5e9b9b3f-4 ced-TextEditorDecorationType2-4"></span>',
'</span>'
].join('');
@ -2138,6 +2138,52 @@ suite('viewLineRenderer.renderLine 2', () => {
assert.deepStrictEqual(actual.html, expected);
});
test('issue #124038: Multiple end-of-line text decorations get merged', () => {
const actual = renderViewLine(new RenderLineInput(
true,
false,
' if',
false,
true,
false,
0,
createViewLineTokens([createPart(4, 1), createPart(6, 2)]),
[
new LineDecoration(7, 7, 'ced-1-TextEditorDecorationType2-17c14d98-3 ced-1-TextEditorDecorationType2-3', InlineDecorationType.Before),
new LineDecoration(7, 7, 'ced-1-TextEditorDecorationType2-17c14d98-4 ced-1-TextEditorDecorationType2-4', InlineDecorationType.After),
new LineDecoration(7, 7, 'ced-ghost-text-1-4', InlineDecorationType.After),
],
4,
0,
10,
10,
10,
10000,
'all',
false,
false,
null
));
const expected = [
'<span>',
'<span class="mtkw">····</span><span class="mtk2">if</span><span class="ced-1-TextEditorDecorationType2-17c14d98-3 ced-1-TextEditorDecorationType2-3"></span><span class="ced-1-TextEditorDecorationType2-17c14d98-4 ced-1-TextEditorDecorationType2-4"></span><span class="ced-ghost-text-1-4"></span>',
'</span>'
].join('');
assert.deepStrictEqual(actual.html, expected);
assertCharacterMapping(actual.characterMapping,
[
[0, 1, 2, 3],
[0, 1],
[],
[0],
[],
],
[4, 2, 0, 0]
);
});
function createTestGetColumnOfLinePartOffset(lineContent: string, tabSize: number, parts: ViewLineToken[], expectedPartLengths: number[]): (partIndex: number, partLength: number, offset: number, expected: number) => void {
let renderLineOutput = renderViewLine(new RenderLineInput(

View file

@ -129,6 +129,7 @@ export class MenuId {
static readonly TouchBarContext = new MenuId('TouchBarContext');
static readonly TitleBarContext = new MenuId('TitleBarContext');
static readonly TunnelContext = new MenuId('TunnelContext');
static readonly TunnelProtocol = new MenuId('TunnelProtocol');
static readonly TunnelPortInline = new MenuId('TunnelInline');
static readonly TunnelTitle = new MenuId('TunnelTitle');
static readonly TunnelLocalAddressInline = new MenuId('TunnelLocalAddressInline');
@ -143,6 +144,7 @@ export class MenuId {
static readonly CommentTitle = new MenuId('CommentTitle');
static readonly CommentActions = new MenuId('CommentActions');
static readonly NotebookToolbar = new MenuId('NotebookToolbar');
static readonly NotebookRightToolbar = new MenuId('NotebookRightToolbar');
static readonly NotebookCellTitle = new MenuId('NotebookCellTitle');
static readonly NotebookCellInsert = new MenuId('NotebookCellInsert');
static readonly NotebookCellBetween = new MenuId('NotebookCellBetween');

View file

@ -10,8 +10,23 @@ export class TestDialogService implements IDialogService {
declare readonly _serviceBrand: undefined;
confirm(_confirmation: IConfirmation): Promise<IConfirmationResult> { return Promise.resolve({ confirmed: false }); }
show(_severity: Severity, _message: string, _buttons: string[], _options?: IDialogOptions): Promise<IShowResult> { return Promise.resolve({ choice: 0 }); }
input(): Promise<IInputResult> { { return Promise.resolve({ choice: 0, values: [] }); } }
about(): Promise<void> { return Promise.resolve(); }
private confirmResult: IConfirmationResult | undefined = undefined;
setConfirmResult(result: IConfirmationResult) {
this.confirmResult = result;
}
async confirm(confirmation: IConfirmation): Promise<IConfirmationResult> {
if (this.confirmResult) {
const confirmResult = this.confirmResult;
this.confirmResult = undefined;
return confirmResult;
}
return { confirmed: false };
}
async show(severity: Severity, message: string, buttons: string[], options?: IDialogOptions): Promise<IShowResult> { return { choice: 0 }; }
async input(): Promise<IInputResult> { { return { choice: 0, values: [] }; } }
async about(): Promise<void> { }
}

View file

@ -63,13 +63,13 @@ export const OPTIONS: OptionDescriptions<Required<NativeParsedArgs>> = {
'verbose': { type: 'boolean', cat: 't', description: localize('verbose', "Print verbose output (implies --wait).") },
'log': { type: 'string', cat: 't', args: 'level', description: localize('log', "Log level to use. Default is 'info'. Allowed values are 'critical', 'error', 'warn', 'info', 'debug', 'trace', 'off'.") },
'status': { type: 'boolean', alias: 's', cat: 't', description: localize('status', "Print process usage and diagnostics information.") },
'prof-startup': { type: 'boolean', cat: 't', description: localize('prof-startup', "Run CPU profiler during startup") },
'prof-startup': { type: 'boolean', cat: 't', description: localize('prof-startup', "Run CPU profiler during startup.") },
'prof-append-timers': { type: 'string' },
'prof-startup-prefix': { type: 'string' },
'prof-v8-extensions': { type: 'boolean' },
'disable-extensions': { type: 'boolean', deprecates: 'disableExtensions', cat: 't', description: localize('disableExtensions', "Disable all installed extensions.") },
'disable-extension': { type: 'string[]', cat: 't', args: 'extension-id', description: localize('disableExtension', "Disable an extension.") },
'sync': { type: 'string', cat: 't', description: localize('turn sync', "Turn sync on or off"), args: ['on', 'off'] },
'sync': { type: 'string', cat: 't', description: localize('turn sync', "Turn sync on or off."), args: ['on', 'off'] },
'inspect-extensions': { type: 'string', deprecates: 'debugPluginHost', args: 'port', cat: 't', description: localize('inspect-extensions', "Allow debugging and profiling of extensions. Check the developer tools for the connection URI.") },
'inspect-brk-extensions': { type: 'string', deprecates: 'debugBrkPluginHost', args: 'port', cat: 't', description: localize('inspect-brk-extensions', "Allow debugging and profiling of extensions with the extension host being paused after start. Check the developer tools for the connection URI.") },

View file

@ -70,8 +70,14 @@ export interface ISettingSearchResult {
}
export interface ProcessExplorerStyles extends WindowStyles {
hoverBackground?: string;
hoverForeground?: string;
listHoverBackground?: string;
listHoverForeground?: string;
listFocusBackground?: string;
listFocusForeground?: string;
listFocusOutline?: string;
listActiveSelectionBackground?: string;
listActiveSelectionForeground?: string;
listHoverOutline?: string;
}
export interface ProcessExplorerData extends WindowData {

View file

@ -38,6 +38,7 @@ function log(logger: spdlog.Logger, level: LogLevel, message: string): void {
case LogLevel.Critical: logger.critical(message); break;
default: throw new Error('Invalid log level');
}
logger.flush();
}
export class SpdLogLogger extends AbstractMessageLogger implements ILogger {

View file

@ -129,6 +129,10 @@ export interface ICommonNativeHostService {
toggleWindowTabsBar(): Promise<void>;
updateTouchBar(items: ISerializableCommandAction[][]): Promise<void>;
// macOS Shell command
installShellCommand(): Promise<void>;
uninstallShellCommand(): Promise<void>;
// Lifecycle
notifyReady(): Promise<void>
relaunch(options?: { addArgs?: string[], removeArgs?: string[] }): Promise<void>;

View file

@ -3,6 +3,11 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as fs from 'fs';
import { exec } from 'child_process';
import { promisify } from 'util';
import { localize } from 'vs/nls';
import { realpath } from 'vs/base/node/extpath';
import { Emitter, Event } from 'vs/base/common/event';
import { IWindowsMainService, ICodeWindow, OpenContext } from 'vs/platform/windows/electron-main/windows';
import { MessageBoxOptions, MessageBoxReturnValue, shell, OpenDevToolsOptions, SaveDialogOptions, SaveDialogReturnValue, OpenDialogOptions, OpenDialogReturnValue, Menu, BrowserWindow, app, clipboard, powerMonitor, nativeTheme, screen, Display } from 'electron';
@ -15,7 +20,7 @@ import { ISerializableCommandAction } from 'vs/platform/actions/common/actions';
import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService';
import { AddFirstParameterToFunctions } from 'vs/base/common/types';
import { IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogMainService';
import { SymlinkSupport } from 'vs/base/node/pfs';
import { exists, SymlinkSupport } from 'vs/base/node/pfs';
import { URI } from 'vs/base/common/uri';
import { ITelemetryData, ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
@ -23,7 +28,7 @@ import { MouseInputEvent } from 'vs/base/parts/sandbox/common/electronTypes';
import { arch, totalmem, release, platform, type, loadavg, freemem, cpus } from 'os';
import { virtualMachineHint } from 'vs/base/node/id';
import { ILogService } from 'vs/platform/log/common/log';
import { dirname, join } from 'vs/base/common/path';
import { dirname, join, resolve } from 'vs/base/common/path';
import { IProductService } from 'vs/platform/product/common/productService';
import { memoize } from 'vs/base/common/decorators';
import { Disposable } from 'vs/base/common/lifecycle';
@ -256,6 +261,99 @@ export class NativeHostMainService extends Disposable implements INativeHostMain
//#endregion
//#region macOS Shell Command
async installShellCommand(windowId: number | undefined): Promise<void> {
const { source, target } = await this.getShellCommandLink();
// Only install unless already existing
try {
const { symbolicLink } = await SymlinkSupport.stat(source);
if (symbolicLink && !symbolicLink.dangling) {
const linkTargetRealPath = await realpath(source);
if (target === linkTargetRealPath) {
return;
}
}
// Different source, delete it first
await fs.promises.unlink(source);
} catch (error) {
if (error.code !== 'ENOENT') {
throw error; // throw on any error but file not found
}
}
try {
await fs.promises.symlink(target, source);
} catch (error) {
if (error.code !== 'EACCES' && error.code !== 'ENOENT') {
throw error;
}
const { response } = await this.showMessageBox(windowId, {
type: 'info',
message: localize('warnEscalation', "{0} will now prompt with 'osascript' for Administrator privileges to install the shell command.", this.productService.nameShort),
buttons: [localize('ok', "OK"), localize('cancel', "Cancel")],
cancelId: 1
});
if (response === 0 /* OK */) {
try {
const command = `osascript -e "do shell script \\"mkdir -p /usr/local/bin && ln -sf \'${target}\' \'${source}\'\\" with administrator privileges"`;
await promisify(exec)(command);
} catch (error) {
throw new Error(localize('cantCreateBinFolder', "Unable to install the shell command '{0}'.", source));
}
}
}
}
async uninstallShellCommand(windowId: number | undefined): Promise<void> {
const { source } = await this.getShellCommandLink();
try {
await fs.promises.unlink(source);
} catch (error) {
switch (error.code) {
case 'EACCES':
const { response } = await this.showMessageBox(windowId, {
type: 'info',
message: localize('warnEscalationUninstall', "{0} will now prompt with 'osascript' for Administrator privileges to uninstall the shell command.", this.productService.nameShort),
buttons: [localize('ok', "OK"), localize('cancel', "Cancel")],
cancelId: 1
});
if (response === 0 /* OK */) {
try {
const command = `osascript -e "do shell script \\"rm \'${source}\'\\" with administrator privileges"`;
await promisify(exec)(command);
} catch (error) {
throw new Error(localize('cantUninstall', "Unable to uninstall the shell command '{0}'.", source));
}
}
break;
case 'ENOENT':
break; // ignore file not found
default:
throw error;
}
}
}
private async getShellCommandLink(): Promise<{ readonly source: string, readonly target: string }> {
const target = resolve(this.environmentMainService.appRoot, 'bin', 'code');
const source = `/usr/local/bin/${this.productService.applicationName}`;
// Ensure source exists
const sourceExists = await exists(target);
if (!sourceExists) {
throw new Error(localize('sourceMissing', "Unable to find shell script in '{0}'", target));
}
return { source, target };
}
//#region Dialog
async showMessageBox(windowId: number | undefined, options: MessageBoxOptions): Promise<MessageBoxReturnValue> {

View file

@ -6,11 +6,12 @@
import { Event } from 'vs/base/common/event';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { $, EventHelper, EventLike } from 'vs/base/browser/dom';
import { domEvent } from 'vs/base/browser/event';
import { DomEmitter, domEvent } from 'vs/base/browser/event';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
import { Disposable } from 'vs/base/common/lifecycle';
import { Color } from 'vs/base/common/color';
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { textLinkActiveForeground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry';
export interface ILinkDescriptor {
readonly label: string;
@ -20,20 +21,37 @@ export interface ILinkDescriptor {
export interface ILinkOptions {
readonly opener?: (href: string) => void;
}
export interface ILinkStyles {
readonly textLinkForeground?: Color;
readonly disabled?: boolean;
readonly textLinkForeground?: string;
}
export class Link extends Disposable {
readonly el: HTMLAnchorElement;
private disabled: boolean;
private styles: ILinkStyles = {
textLinkForeground: Color.fromHex('#006AB1')
};
private _enabled: boolean = true;
get enabled(): boolean {
return this._enabled;
}
set enabled(enabled: boolean) {
if (enabled) {
this.el.setAttribute('aria-disabled', 'false');
this.el.tabIndex = 0;
this.el.style.pointerEvents = 'auto';
this.el.style.opacity = '1';
this.el.style.cursor = 'pointer';
this._enabled = false;
} else {
this.el.setAttribute('aria-disabled', 'true');
this.el.tabIndex = -1;
this.el.style.pointerEvents = 'none';
this.el.style.opacity = '0.4';
this.el.style.cursor = 'default';
this._enabled = true;
}
this._enabled = enabled;
}
constructor(
link: ILinkDescriptor,
@ -42,60 +60,45 @@ export class Link extends Disposable {
) {
super();
this.el = $<HTMLAnchorElement>('a', {
this.el = $<HTMLAnchorElement>('a.monaco-link', {
tabIndex: 0,
href: link.href,
title: link.title
}, link.label);
const onClick = domEvent(this.el, 'click');
const onClickEmitter = this._register(new DomEmitter(this.el, 'click'));
const onEnterPress = Event.chain(domEvent(this.el, 'keypress'))
.map(e => new StandardKeyboardEvent(e))
.filter(e => e.keyCode === KeyCode.Enter)
.event;
const onOpen = Event.any<EventLike>(onClick, onEnterPress);
const onOpen = Event.any<EventLike>(onClickEmitter.event, onEnterPress);
this._register(onOpen(e => {
if (!this.enabled) {
return;
}
EventHelper.stop(e, true);
if (!this.disabled) {
if (options?.opener) {
options.opener(link.href);
} else {
openerService.open(link.href, { allowCommands: true });
}
if (options?.opener) {
options.opener(link.href);
} else {
openerService.open(link.href, { allowCommands: true });
}
}));
this.disabled = false;
this.applyStyles();
}
style(styles: ILinkStyles): void {
this.styles = styles;
this.applyStyles();
}
private applyStyles(): void {
const color = this.styles.textLinkForeground?.toString();
if (color) {
this.el.style.color = color;
}
if (typeof this.styles.disabled === 'boolean' && this.styles.disabled !== this.disabled) {
if (this.styles.disabled) {
this.el.setAttribute('aria-disabled', 'true');
this.el.tabIndex = -1;
this.el.style.pointerEvents = 'none';
this.el.style.opacity = '0.4';
this.el.style.cursor = 'default';
this.disabled = true;
} else {
this.el.setAttribute('aria-disabled', 'false');
this.el.tabIndex = 0;
this.el.style.pointerEvents = 'auto';
this.el.style.opacity = '1';
this.el.style.cursor = 'pointer';
this.disabled = false;
}
}
this.enabled = true;
}
}
registerThemingParticipant((theme, collector) => {
const textLinkForegroundColor = theme.getColor(textLinkForeground);
if (textLinkForegroundColor) {
collector.addRule(`.monaco-link { color: ${textLinkForegroundColor}; }`);
}
const textLinkActiveForegroundColor = theme.getColor(textLinkActiveForeground);
if (textLinkActiveForegroundColor) {
collector.addRule(`.monaco-link:hover { color: ${textLinkActiveForegroundColor}; }`);
}
});

View file

@ -10,7 +10,7 @@ import { IStateMainService } from 'vs/platform/state/electron-main/state';
import { ILogService } from 'vs/platform/log/common/log';
import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService';
import { ThrottledDelayer } from 'vs/base/common/async';
import { IFileService } from 'vs/platform/files/common/files';
import { FileOperationError, FileOperationResult, IFileService } from 'vs/platform/files/common/files';
import { VSBuffer } from 'vs/base/common/buffer';
type StorageDatabase = { [key: string]: unknown; };
@ -45,7 +45,7 @@ export class FileStorage {
this.lastSavedStorageContents = (await this.fileService.readFile(this.storagePath)).value.toString();
this.storage = JSON.parse(this.lastSavedStorageContents);
} catch (error) {
if (error.code !== 'ENOENT') {
if ((<FileOperationError>error).fileOperationResult !== FileOperationResult.FILE_NOT_FOUND) {
this.logService.error(error);
}
}

View file

@ -418,6 +418,12 @@ export interface ITerminalLaunchError {
code?: number;
}
export interface IProcessReadyEvent {
pid: number,
cwd: string,
requiresWindowsMode?: boolean
}
/**
* An interface representing a raw terminal child process, this contains a subset of the
* child_process.ChildProcess node.js interface.
@ -437,7 +443,7 @@ export interface ITerminalChildProcess {
onProcessData: Event<IProcessDataEvent | string>;
onProcessExit: Event<number | undefined>;
onProcessReady: Event<{ pid: number, cwd: string }>;
onProcessReady: Event<IProcessReadyEvent>;
onProcessTitleChanged: Event<string>;
onProcessOverrideDimensions?: Event<ITerminalDimensionsOverride | undefined>;
onProcessResolvedShellLaunchConfig?: Event<IShellLaunchConfig>;

View file

@ -187,6 +187,13 @@ const terminalPlatformConfiguration: IConfigurationNode = {
description: localize('terminalProfile.windowsSource', 'A profile source that will auto detect the paths to the shell.'),
enum: ['PowerShell', 'Git Bash']
},
args: {
description: localize('terminalProfile.args', 'An optional set of arguments to run the shell executable with.'),
type: 'array',
items: {
type: 'string'
}
},
overrideName: {
description: localize('terminalProfile.overrideName', 'Controls whether or not the profile name overrides the auto detected one.'),
type: 'boolean'

View file

@ -5,7 +5,7 @@
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { IProcessEnvironment, isWindows, OperatingSystem, OS } from 'vs/base/common/platform';
import { IPtyService, IProcessDataEvent, IShellLaunchConfig, ITerminalDimensionsOverride, ITerminalLaunchError, LocalReconnectConstants, ITerminalsLayoutInfo, IRawTerminalInstanceLayoutInfo, ITerminalTabLayoutInfoById, ITerminalInstanceLayoutInfoById, TerminalShellType } from 'vs/platform/terminal/common/terminal';
import { IPtyService, IProcessDataEvent, IShellLaunchConfig, ITerminalDimensionsOverride, ITerminalLaunchError, LocalReconnectConstants, ITerminalsLayoutInfo, IRawTerminalInstanceLayoutInfo, ITerminalTabLayoutInfoById, ITerminalInstanceLayoutInfoById, TerminalShellType, IProcessReadyEvent } from 'vs/platform/terminal/common/terminal';
import { AutoOpenBarrier, Queue, RunOnceScheduler } from 'vs/base/common/async';
import { Emitter } from 'vs/base/common/event';
import { TerminalRecorder } from 'vs/platform/terminal/common/terminalRecorder';
@ -281,7 +281,7 @@ export class PersistentTerminalProcess extends Disposable {
private readonly _onProcessReplay = this._register(new Emitter<IPtyHostProcessReplayEvent>());
readonly onProcessReplay = this._onProcessReplay.event;
private readonly _onProcessReady = this._register(new Emitter<{ pid: number, cwd: string }>());
private readonly _onProcessReady = this._register(new Emitter<IProcessReadyEvent>());
readonly onProcessReady = this._onProcessReady.event;
private readonly _onProcessTitleChanged = this._register(new Emitter<string>());
readonly onProcessTitleChanged = this._onProcessTitleChanged.event;
@ -377,7 +377,7 @@ export class PersistentTerminalProcess extends Disposable {
}
this._isStarted = true;
} else {
this._onProcessReady.fire({ pid: this._pid, cwd: this._cwd });
this._onProcessReady.fire({ pid: this._pid, cwd: this._cwd, requiresWindowsMode: isWindows && getWindowsBuildNumber() < 21376 });
this._onProcessTitleChanged.fire(this._terminalProcess.currentTitle);
this._onProcessShellTypeChanged.fire(this._terminalProcess.shellType);
this.triggerReplay();

View file

@ -9,7 +9,7 @@ import * as fs from 'fs';
import * as os from 'os';
import { Event, Emitter } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { IShellLaunchConfig, ITerminalLaunchError, FlowControlConstants, ITerminalChildProcess, ITerminalDimensionsOverride, TerminalShellType } from 'vs/platform/terminal/common/terminal';
import { IShellLaunchConfig, ITerminalLaunchError, FlowControlConstants, ITerminalChildProcess, ITerminalDimensionsOverride, TerminalShellType, IProcessReadyEvent } from 'vs/platform/terminal/common/terminal';
import { exec } from 'child_process';
import { ILogService } from 'vs/platform/log/common/log';
import { findExecutable, getWindowsBuildNumber } from 'vs/platform/terminal/node/terminalEnvironment';
@ -99,8 +99,8 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
get onProcessData(): Event<string> { return this._onProcessData.event; }
private readonly _onProcessExit = this._register(new Emitter<number>());
get onProcessExit(): Event<number> { return this._onProcessExit.event; }
private readonly _onProcessReady = this._register(new Emitter<{ pid: number, cwd: string }>());
get onProcessReady(): Event<{ pid: number, cwd: string }> { return this._onProcessReady.event; }
private readonly _onProcessReady = this._register(new Emitter<IProcessReadyEvent>());
get onProcessReady(): Event<IProcessReadyEvent> { return this._onProcessReady.event; }
private readonly _onProcessTitleChanged = this._register(new Emitter<string>());
get onProcessTitleChanged(): Event<string> { return this._onProcessTitleChanged.event; }
private readonly _onProcessShellTypeChanged = this._register(new Emitter<TerminalShellType>());
@ -324,7 +324,7 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
}
private _sendProcessId(pid: number) {
this._onProcessReady.fire({ pid, cwd: this._initialCwd });
this._onProcessReady.fire({ pid, cwd: this._initialCwd, requiresWindowsMode: isWindows && getWindowsBuildNumber() < 21376 });
}
private _sendProcessTitle(ptyProcess: pty.IPty): void {

View file

@ -35,7 +35,7 @@ export function computeStyles(theme: IColorTheme, styleMap: IColorMapping): ICom
}
export function attachStyler<T extends IColorMapping>(themeService: IThemeService, styleMap: T, widgetOrCallback: IThemable | styleFn): IDisposable {
function applyStyles(theme: IColorTheme): void {
function applyStyles(): void {
const styles = computeStyles(themeService.getColorTheme(), styleMap);
if (typeof widgetOrCallback === 'function') {
@ -45,7 +45,7 @@ export function attachStyler<T extends IColorMapping>(themeService: IThemeServic
}
}
applyStyles(themeService.getColorTheme());
applyStyles();
return themeService.onDidColorThemeChange(applyStyles);
}
@ -254,16 +254,6 @@ export function attachKeybindingLabelStyler(widget: IThemable, themeService: ITh
} as IKeybindingLabelStyleOverrides, widget);
}
export interface ILinkStyleOverrides extends IStyleOverrides {
textLinkForeground?: ColorIdentifier;
}
export function attachLinkStyler(widget: IThemable, themeService: IThemeService, style?: ILinkStyleOverrides): IDisposable {
return attachStyler(themeService, {
textLinkForeground: style?.textLinkForeground || textLinkForeground,
} as ILinkStyleOverrides, widget);
}
export interface IProgressBarStyleOverrides extends IStyleOverrides {
progressBarBackground?: ColorIdentifier;
}
@ -324,7 +314,7 @@ export function attachMenuStyler(widget: IThemable, themeService: IThemeService,
return attachStyler(themeService, { ...defaultMenuStyles, ...style }, widget);
}
export interface IDialogStyleOverrides extends IButtonStyleOverrides, ILinkStyleOverrides {
export interface IDialogStyleOverrides extends IButtonStyleOverrides {
dialogForeground?: ColorIdentifier;
dialogBackground?: ColorIdentifier;
dialogShadow?: ColorIdentifier;

View file

@ -34,7 +34,6 @@ import { IFileService } from 'vs/platform/files/common/files';
import { FileAccess, Schemas } from 'vs/base/common/network';
import { isLaunchedFromCli } from 'vs/platform/environment/node/argvHelper';
import { CancellationToken } from 'vs/base/common/cancellation';
import { INativeHostMainService } from 'vs/platform/native/electron-main/nativeHostMainService';
import { IProtocolMainService } from 'vs/platform/protocol/electron-main/protocol';
export interface IWindowCreationOptions {
@ -153,7 +152,6 @@ export class CodeWindow extends Disposable implements ICodeWindow {
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IDialogMainService private readonly dialogMainService: IDialogMainService,
@ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService,
@INativeHostMainService private readonly nativeHostMainService: INativeHostMainService,
@IProductService private readonly productService: IProductService,
@IProtocolMainService private readonly protocolMainService: IProtocolMainService
) {
@ -510,22 +508,6 @@ export class CodeWindow extends Disposable implements ICodeWindow {
this._lastFocusTime = Date.now();
});
if (isMacintosh) {
this._register(this.nativeHostMainService.onDidChangeDisplay(() => {
if (!this._win) {
return; // disposed
}
// Simple fullscreen doesn't resize automatically when the resolution changes so as a workaround
// we need to detect when display metrics change or displays are added/removed and toggle the
// fullscreen manually.
if (!this.useNativeFullScreen() && this.isFullScreen) {
this.setFullScreen(false);
this.setFullScreen(true);
}
}));
}
// Window (Un)Maximize
this._win.on('maximize', (e: Event) => {
if (this.currentConfig) {

View file

@ -53,6 +53,12 @@ export interface IWorkspaceTrustManagementService {
setTrustedFolders(folders: URI[]): Promise<void>;
}
export const enum WorkspaceTrustUriResponse {
Open = 1,
OpenInNewWindow = 2,
Cancel = 3
}
export const IWorkspaceTrustRequestService = createDecorator<IWorkspaceTrustRequestService>('workspaceTrustRequestService');
export interface IWorkspaceTrustRequestService {
@ -60,6 +66,7 @@ export interface IWorkspaceTrustRequestService {
readonly onDidInitiateWorkspaceTrustRequest: Event<WorkspaceTrustRequestOptions | undefined>;
requestOpenUris(uris: URI[]): Promise<WorkspaceTrustUriResponse>;
cancelRequest(): void;
completeRequest(trusted?: boolean): Promise<void>;
requestWorkspaceTrust(options?: WorkspaceTrustRequestOptions): Promise<boolean | undefined>;

38
src/vs/vscode.d.ts vendored
View file

@ -1154,7 +1154,9 @@ declare module 'vscode' {
/**
* Adds a set of decorations to the text editor. If a set of decorations already exists with
* the given {@link TextEditorDecorationType decoration type}, they will be replaced.
* the given {@link TextEditorDecorationType decoration type}, they will be replaced. If
* `rangesOrOptions` is empty, the existing decorations with the given {@link TextEditorDecorationType decoration type}
* will be removed.
*
* @see {@link window.createTextEditorDecorationType createTextEditorDecorationType}.
*
@ -1312,6 +1314,15 @@ declare module 'vscode' {
*/
static joinPath(base: Uri, ...pathSegments: string[]): Uri;
/**
* Create an URI from its component parts
*
* @see {@link Uri.toString}
* @param components The component parts of an Uri.
* @return A new Uri instance.
*/
static from(components: { scheme: string; authority?: string; path?: string; query?: string; fragment?: string }): Uri;
/**
* Use the `file` and `parse` factory functions to create new `Uri` objects.
*/
@ -5573,6 +5584,14 @@ declare module 'vscode' {
*/
export interface StatusBarItem {
/**
* The identifier of this item.
*
* *Note*: if no identifier was provided by the {@link window.createStatusBarItem `window.createStatusBarItem`}
* method, the identifier will match the {@link Extension.id extension identifier}.
*/
readonly id: string;
/**
* The alignment of this item.
*/
@ -5584,6 +5603,13 @@ declare module 'vscode' {
*/
readonly priority?: number;
/**
* The name of the entry, like 'Python Language Indicator', 'Git Status' etc.
* Try to keep the length of the name short, yet descriptive enough that
* users can understand what the status bar item is about.
*/
name: string | undefined;
/**
* The text to show for the entry. You can embed icons in the text by leveraging the syntax:
*
@ -8793,6 +8819,16 @@ declare module 'vscode' {
*/
export function createStatusBarItem(alignment?: StatusBarAlignment, priority?: number): StatusBarItem;
/**
* Creates a status bar {@link StatusBarItem item}.
*
* @param id The unique identifier of the item.
* @param alignment The alignment of the item.
* @param priority The priority of the item. Higher values mean the item should be shown more to the left.
* @return A new status bar item.
*/
export function createStatusBarItem(id: string, alignment?: StatusBarAlignment, priority?: number): StatusBarItem;
/**
* Creates a {@link Terminal} with a backing shell process. The cwd of the terminal will be the workspace
* directory if it exists.

View file

@ -915,24 +915,18 @@ declare module 'vscode' {
//#region Custom Tree View Drag and Drop https://github.com/microsoft/vscode/issues/32592
export interface TreeViewOptions<T> {
/**
* * Whether the tree supports drag and drop.
*/
canDragAndDrop?: boolean;
dragAndDropController?: DragAndDropController<T>;
}
export interface TreeDataProvider<T> {
export interface DragAndDropController<T> extends Disposable {
/**
* Optional method to reparent an `element`.
* Extensions should fire `TreeDataProvider.onDidChangeTreeData` for any elements that need to be refreshed.
*
* **NOTE:** This method should be implemented if the tree supports drag and drop.
*
* @param elements The selected elements that will be reparented.
* @param targetElement The new parent of the elements.
* @param source
* @param target
*/
setParent?(elements: T[], targetElement: T): Thenable<void>;
onDrop(source: T[], target: T): Thenable<void>;
}
//#endregion
//#region Task presentation group: https://github.com/microsoft/vscode/issues/47265
@ -944,59 +938,6 @@ declare module 'vscode' {
}
//#endregion
//#region Status bar item with ID and Name: https://github.com/microsoft/vscode/issues/74972
/**
* Options to configure the status bar item.
*/
export interface StatusBarItemOptions {
/**
* A unique identifier of the status bar item. The identifier
* is for example used to allow a user to show or hide the
* status bar item in the UI.
*/
id: string;
/**
* A human readable name of the status bar item. The name is
* for example used as a label in the UI to show or hide the
* status bar item.
*/
name: string;
/**
* Accessibility information used when screen reader interacts with this status bar item.
*/
accessibilityInformation?: AccessibilityInformation;
/**
* The alignment of the status bar item.
*/
alignment?: StatusBarAlignment;
/**
* The priority of the status bar item. Higher value means the item should
* be shown more to the left.
*/
priority?: number;
}
export namespace window {
/**
* Creates a status bar {@link StatusBarItem item}.
*
* @param options The options of the item. If not provided, some default values
* will be assumed. For example, the `StatusBarItemOptions.id` will be the id
* of the extension and the `StatusBarItemOptions.name` will be the extension name.
* @return A new status bar item.
*/
export function createStatusBarItem(options?: StatusBarItemOptions): StatusBarItem;
}
//#endregion
//#region Custom editor move https://github.com/microsoft/vscode/issues/86146
// TODO: Also for custom editor
@ -1057,7 +998,6 @@ declare module 'vscode' {
*
* NotebookCell instances are immutable and are kept in sync for as long as they are part of their notebook.
*/
// todo@API support ids https://github.com/jupyter/enhancement-proposals/blob/master/62-cell-id/cell-id.md
export interface NotebookCell {
/**
@ -1322,7 +1262,6 @@ declare module 'vscode' {
/**
* NotebookCellData is the raw representation of notebook cells. Its is part of {@link NotebookData `NotebookData`}.
*/
// todo@API support ids https://github.com/jupyter/enhancement-proposals/blob/master/62-cell-id/cell-id.md
export class NotebookCellData {
/**
@ -3323,4 +3262,28 @@ declare module 'vscode' {
}
//#endregion
//#region Expose parent session on DebugSessions - https://github.com/microsoft/vscode/issues/123403#issuecomment-843269200
export interface DebugSession {
/**
* The parent session of this debug session, if it was created as a child.
* @see DebugSessionOptions.parentSession
*/
readonly parentSession?: DebugSession;
}
//#endregion
//#region https://github.com/microsoft/vscode/issues/87110 @eamodio
export interface Memento {
/**
* The stored keys.
*/
readonly keys: readonly string[];
}
//#endregion
}

View file

@ -16,7 +16,7 @@ import { isEqual, isEqualOrParent, toLocalResource } from 'vs/base/common/resour
import { URI, UriComponents } from 'vs/base/common/uri';
import { localize } from 'vs/nls';
import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
import { FileSystemProviderCapabilities, IFileService } from 'vs/platform/files/common/files';
import { FileOperation, FileSystemProviderCapabilities, IFileService } from 'vs/platform/files/common/files';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ILabelService } from 'vs/platform/label/common/label';
import { IUndoRedoService, UndoRedoElementType } from 'vs/platform/undoRedo/common/undoRedo';
@ -35,7 +35,7 @@ import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editor
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IPathService } from 'vs/workbench/services/path/common/pathService';
import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService';
import { IWorkingCopyFileService, WorkingCopyFileEvent } from 'vs/workbench/services/workingCopy/common/workingCopyFileService';
import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService';
import { IWorkingCopy, IWorkingCopyBackup, NO_TYPE_ID, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopy';
import { ResourceWorkingCopy } from 'vs/workbench/services/workingCopy/common/resourceWorkingCopy';
@ -51,6 +51,8 @@ export class MainThreadCustomEditors extends Disposable implements extHostProtoc
private readonly _editorProviders = new Map<string, IDisposable>();
private readonly _editorRenameBackups = new Map<string, CustomDocumentBackupData>();
constructor(
context: extHostProtocol.IExtHostContext,
private readonly mainThreadWebview: MainThreadWebviews,
@ -61,7 +63,7 @@ export class MainThreadCustomEditors extends Disposable implements extHostProtoc
@ICustomEditorService private readonly _customEditorService: ICustomEditorService,
@IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService,
@IWebviewWorkbenchService private readonly _webviewWorkbenchService: IWebviewWorkbenchService,
@IInstantiationService private readonly _instantiationService: IInstantiationService
@IInstantiationService private readonly _instantiationService: IInstantiationService,
) {
super();
@ -90,6 +92,9 @@ export class MainThreadCustomEditors extends Disposable implements extHostProtoc
},
resolveWebview: () => { throw new Error('not implemented'); }
}));
// Working copy operations
this._register(workingCopyFileService.onWillRunWorkingCopyFileOperation(async e => this.onWillRunWorkingCopyFileOperation(e)));
}
override dispose() {
@ -138,9 +143,18 @@ export class MainThreadCustomEditors extends Disposable implements extHostProtoc
webviewInput.webview.options = options;
webviewInput.webview.extension = extension;
// If there's an old resource this was a move and we must resolve the backup at the same time as the webview
// This is because the backup must be ready upon model creation, and the input resolve method comes after
let backupId = webviewInput.backupId;
if (webviewInput.oldResource && !webviewInput.backupId) {
const backup = this._editorRenameBackups.get(webviewInput.oldResource.toString());
backupId = backup?.backupId;
this._editorRenameBackups.delete(webviewInput.oldResource.toString());
}
let modelRef: IReference<ICustomEditorModel>;
try {
modelRef = await this.getOrCreateCustomEditorModel(modelType, resource, viewType, { backupId: webviewInput.backupId }, cancellation);
modelRef = await this.getOrCreateCustomEditorModel(modelType, resource, viewType, { backupId }, cancellation);
} catch (error) {
onUnexpectedError(error);
webviewInput.webview.html = this.mainThreadWebview.getWebviewResolvedFailedContent(viewType);
@ -253,6 +267,31 @@ export class MainThreadCustomEditors extends Disposable implements extHostProtoc
}
return model;
}
//#region Working Copy
private async onWillRunWorkingCopyFileOperation(e: WorkingCopyFileEvent) {
if (e.operation !== FileOperation.MOVE) {
return;
}
e.waitUntil((async () => {
const models = [];
for (const file of e.files) {
if (file.source) {
models.push(...(await this._customEditorService.models.getAllModels(file.source)));
}
}
for (const model of models) {
if (model instanceof MainThreadCustomEditorModel && model.isDirty()) {
const workingCopy = await model.backup(CancellationToken.None);
if (workingCopy.meta) {
// This cast is safe because we do an instanceof check above and a custom document backup data is always returned
this._editorRenameBackups.set(model.editorResource.toString(), workingCopy.meta as CustomDocumentBackupData);
}
}
}
})());
}
//#endregion
}
namespace HotExitState {

View file

@ -329,7 +329,8 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
type: session.configuration.type,
name: session.name,
folderUri: session.root ? session.root.uri : undefined,
configuration: session.configuration
configuration: session.configuration,
parent: session.parentSession?.getId(),
};
}
}

View file

@ -7,7 +7,7 @@ import { DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle'
import { URI } from 'vs/base/common/uri';
import { ExtHostContext, IExtHostEditorTabsShape, IExtHostContext, MainContext, IEditorTabDto } from 'vs/workbench/api/common/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { Verbosity } from 'vs/workbench/common/editor';
import { EditorResourceAccessor, Verbosity } from 'vs/workbench/common/editor';
import { GroupChangeKind, IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
@ -33,7 +33,7 @@ export class MainThreadEditorTabs {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostEditorTabs);
this._editorGroupsService.groups.forEach(this._subscribeToGroup, this);
this._editorGroupsService.whenReady.then(() => this._editorGroupsService.groups.forEach(this._subscribeToGroup, this));
this._dispoables.add(_editorGroupsService.onDidAddGroup(this._subscribeToGroup, this));
this._dispoables.add(_editorGroupsService.onDidRemoveGroup(e => {
const subscription = this._groups.get(e);
@ -72,7 +72,7 @@ export class MainThreadEditorTabs {
tabs.push({
group: group.id,
name: editor.getTitle(Verbosity.SHORT) ?? '',
resource: editor.resource,
resource: EditorResourceAccessor.getOriginalUri(editor) ?? editor.resource,
isActive: (this._editorGroupsService.activeGroup === group) && group.isActive(editor)
});
}

View file

@ -114,7 +114,8 @@ export class MainThreadNotebookDocuments implements MainThreadNotebookDocumentsS
language: cell.language,
cellKind: cell.cellKind,
outputs: cell.outputs,
metadata: cell.metadata
metadata: cell.metadata,
internalMetadata: cell.internalMetadata,
};
}

View file

@ -234,7 +234,8 @@ export class MainThreadNotebooksAndEditors {
language: cell.language,
cellKind: cell.cellKind,
outputs: cell.outputs,
metadata: cell.metadata
metadata: cell.metadata,
internalMetadata: cell.internalMetadata,
}))
};
}

View file

@ -185,7 +185,6 @@ export class MainThreadNotebookKernels implements MainThreadNotebookKernelsShape
await that._proxy.$cancelCells(handle, uri, handles);
}
}(data, this._modeService);
const registration = this._notebookKernelService.registerKernel(kernel);
const listener = this._notebookKernelService.onDidChangeNotebookKernelBinding(e => {
if (e.oldKernel === kernel.id) {
@ -195,6 +194,7 @@ export class MainThreadNotebookKernels implements MainThreadNotebookKernelsShape
}
});
const registration = this._notebookKernelService.registerKernel(kernel);
this._kernels.set(handle, [kernel, combinedDisposable(listener, registration)]);
}

View file

@ -27,7 +27,7 @@ export class MainThreadStatusBar implements MainThreadStatusBarShape {
this.entries.clear();
}
$setEntry(id: number, statusId: string, statusName: string, text: string, tooltip: string | undefined, command: Command | undefined, color: string | ThemeColor | undefined, backgroundColor: string | ThemeColor | undefined, alignment: MainThreadStatusBarAlignment, priority: number | undefined, accessibilityInformation: IAccessibilityInformation): void {
$setEntry(entryId: number, id: string, name: string, text: string, tooltip: string | undefined, command: Command | undefined, color: string | ThemeColor | undefined, backgroundColor: string | ThemeColor | undefined, alignment: MainThreadStatusBarAlignment, priority: number | undefined, accessibilityInformation: IAccessibilityInformation): void {
// if there are icons in the text use the tooltip for the aria label
let ariaLabel: string;
let role: string | undefined = undefined;
@ -37,23 +37,23 @@ export class MainThreadStatusBar implements MainThreadStatusBarShape {
} else {
ariaLabel = getCodiconAriaLabel(text);
}
const entry: IStatusbarEntry = { text, tooltip, command, color, backgroundColor, ariaLabel, role };
const entry: IStatusbarEntry = { name, text, tooltip, command, color, backgroundColor, ariaLabel, role };
if (typeof priority === 'undefined') {
priority = 0;
}
// Reset existing entry if alignment or priority changed
let existingEntry = this.entries.get(id);
let existingEntry = this.entries.get(entryId);
if (existingEntry && (existingEntry.alignment !== alignment || existingEntry.priority !== priority)) {
dispose(existingEntry.accessor);
this.entries.delete(id);
this.entries.delete(entryId);
existingEntry = undefined;
}
// Create new entry if not existing
if (!existingEntry) {
this.entries.set(id, { accessor: this.statusbarService.addEntry(entry, statusId, statusName, alignment, priority), alignment, priority });
this.entries.set(entryId, { accessor: this.statusbarService.addEntry(entry, id, alignment, priority), alignment, priority });
}
// Otherwise update

View file

@ -5,7 +5,7 @@
import { Disposable } from 'vs/base/common/lifecycle';
import { ExtHostContext, MainThreadTreeViewsShape, ExtHostTreeViewsShape, MainContext, IExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
import { ITreeViewDataProvider, ITreeItem, IViewsService, ITreeView, IViewsRegistry, ITreeViewDescriptor, IRevealOptions, Extensions, ResolvableTreeItem } from 'vs/workbench/common/views';
import { ITreeViewDataProvider, ITreeItem, IViewsService, ITreeView, IViewsRegistry, ITreeViewDescriptor, IRevealOptions, Extensions, ResolvableTreeItem, ITreeViewDragAndDropController } from 'vs/workbench/common/views';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { distinct } from 'vs/base/common/arrays';
import { INotificationService } from 'vs/platform/notification/common/notification';
@ -37,13 +37,14 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
this.extensionService.whenInstalledExtensionsRegistered().then(() => {
const dataProvider = new TreeViewDataProvider(treeViewId, this._proxy, this.notificationService);
this._dataProviders.set(treeViewId, dataProvider);
const dndController = options.canDragAndDrop ? new TreeViewDragAndDropController(treeViewId, this._proxy) : undefined;
const viewer = this.getTreeView(treeViewId);
if (viewer) {
// Order is important here. The internal tree isn't created until the dataProvider is set.
// Set all other properties first!
viewer.showCollapseAllAction = !!options.showCollapseAll;
viewer.canSelectMany = !!options.canSelectMany;
viewer.canDragAndDrop = !!options.canDragAndDrop;
viewer.dragAndDropController = dndController;
viewer.dataProvider = dataProvider;
this.registerListeners(treeViewId, viewer);
this._proxy.$setVisible(treeViewId, viewer.visible);
@ -162,6 +163,16 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
type TreeItemHandle = string;
class TreeViewDragAndDropController implements ITreeViewDragAndDropController {
constructor(private readonly treeViewId: string,
private readonly _proxy: ExtHostTreeViewsShape) { }
onDrop(treeItem: ITreeItem[], targetTreeItem: ITreeItem): Promise<void> {
return this._proxy.$onDrop(this.treeViewId, treeItem.map(item => item.handle), targetTreeItem.handle);
}
}
class TreeViewDataProvider implements ITreeViewDataProvider {
private readonly itemsMap: Map<TreeItemHandle, ITreeItem> = new Map<TreeItemHandle, ITreeItem>();
@ -184,10 +195,6 @@ class TreeViewDataProvider implements ITreeViewDataProvider {
}));
}
setParent(treeItem: ITreeItem[], targetTreeItem: ITreeItem): Promise<void> {
return this._proxy.$setParent(this.treeViewId, treeItem.map(item => item.handle), targetTreeItem.handle);
}
getItemsToRefresh(itemsToRefreshByHandle: { [treeItemHandle: string]: ITreeItem }): ITreeItem[] {
const itemsToRefresh: ITreeItem[] = [];
if (itemsToRefreshByHandle) {

View file

@ -78,23 +78,6 @@ export class RemoveFromRecentlyOpenedAPICommand {
}
CommandsRegistry.registerCommand(RemoveFromRecentlyOpenedAPICommand.ID, adjustHandler(RemoveFromRecentlyOpenedAPICommand.execute));
export interface OpenIssueReporterArgs {
readonly extensionId: string;
readonly issueTitle?: string;
readonly issueBody?: string;
}
export class OpenIssueReporter {
public static readonly ID = 'vscode.openIssueReporter';
public static execute(executor: ICommandsExecutor, args: string | OpenIssueReporterArgs): Promise<void> {
const commandArgs = typeof args === 'string'
? { extensionId: args }
: args;
return executor.executeCommand('workbench.action.openIssueReporter', commandArgs);
}
}
interface RecentEntry {
uri: URI;
type: 'workspace' | 'folder' | 'file';

View file

@ -3,7 +3,6 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import * as errors from 'vs/base/common/errors';
import { Emitter, Event } from 'vs/base/common/event';
@ -597,25 +596,21 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
showSaveDialog(options) {
return extHostDialogs.showSaveDialog(options);
},
createStatusBarItem(alignmentOrOptions?: vscode.StatusBarAlignment | vscode.StatusBarItemOptions, priority?: number): vscode.StatusBarItem {
let id: string;
let name: string;
createStatusBarItem(alignmentOrId?: vscode.StatusBarAlignment | string, priorityOrAlignment?: number | vscode.StatusBarAlignment, priorityArg?: number): vscode.StatusBarItem {
let id: string | undefined;
let alignment: number | undefined;
let accessibilityInformation: vscode.AccessibilityInformation | undefined = undefined;
let priority: number | undefined;
if (alignmentOrOptions && typeof alignmentOrOptions !== 'number') {
id = alignmentOrOptions.id;
name = alignmentOrOptions.name;
alignment = alignmentOrOptions.alignment;
priority = alignmentOrOptions.priority;
accessibilityInformation = alignmentOrOptions.accessibilityInformation;
if (typeof alignmentOrId === 'string') {
id = alignmentOrId;
alignment = priorityOrAlignment;
priority = priorityArg;
} else {
id = extension.identifier.value;
name = nls.localize('extensionLabel', "{0} (Extension)", extension.displayName || extension.name);
alignment = alignmentOrOptions;
alignment = alignmentOrId;
priority = priorityOrAlignment;
}
return extHostStatusBar.createStatusBarEntry(id, name, alignment, priority, accessibilityInformation);
return extHostStatusBar.createStatusBarEntry(extension, id, alignment, priority);
},
setStatusBarMessage(text: string, timeoutOrThenable?: number | Thenable<any>): vscode.Disposable {
return extHostStatusBar.setStatusBarMessage(text, timeoutOrThenable);

View file

@ -1217,7 +1217,7 @@ export interface ExtHostDocumentsAndEditorsShape {
export interface ExtHostTreeViewsShape {
$getChildren(treeViewId: string, treeItemHandle?: string): Promise<ITreeItem[]>;
$setParent(treeViewId: string, treeItemHandle: string[], newParentTreeItemHandle: string): Promise<void>;
$onDrop(treeViewId: string, treeItemHandle: string[], newParentTreeItemHandle: string): Promise<void>;
$setExpanded(treeViewId: string, treeItemHandle: string, expanded: boolean): void;
$setSelection(treeViewId: string, treeItemHandles: string[]): void;
$setVisible(treeViewId: string, visible: boolean): void;
@ -1780,6 +1780,7 @@ export interface IDebugSessionFullDto {
id: DebugSessionUUID;
type: string;
name: string;
parent: DebugSessionUUID | undefined;
folderUri: UriComponents | undefined;
configuration: IConfig;
}

View file

@ -14,7 +14,7 @@ import * as search from 'vs/workbench/contrib/search/common/search';
import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
import { ApiCommand, ApiCommandArgument, ApiCommandResult, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
import { CustomCodeAction } from 'vs/workbench/api/common/extHostLanguageFeatures';
import { ICommandsExecutor, RemoveFromRecentlyOpenedAPICommand, OpenIssueReporter, OpenIssueReporterArgs } from './apiCommands';
import { ICommandsExecutor, RemoveFromRecentlyOpenedAPICommand } from './apiCommands';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import { IRange } from 'vs/editor/common/core/range';
import { IPosition } from 'vs/editor/common/core/position';
@ -456,13 +456,6 @@ export class ExtHostApiCommands {
{ name: 'path', description: 'Path to remove from recently opened.', constraint: (value: any) => typeof value === 'string' }
]
});
this._register(OpenIssueReporter.ID, adjustHandler(OpenIssueReporter.execute), {
description: 'Opens the issue reporter with the provided extension id as the selected source',
args: [
{ name: 'extensionId', description: 'extensionId to report an issue on', constraint: (value: unknown) => typeof value === 'string' || (typeof value === 'object' && typeof (value as OpenIssueReporterArgs).extensionId === 'string') }
]
});
}
// --- command impl

View file

@ -845,7 +845,8 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E
let ds = this._debugSessions.get(dto.id);
if (!ds) {
const folder = await this.getFolder(dto.folderUri);
ds = new ExtHostDebugSession(this._debugServiceProxy, dto.id, dto.type, dto.name, folder, dto.configuration);
const parent = dto.parent ? this._debugSessions.get(dto.parent) : undefined;
ds = new ExtHostDebugSession(this._debugServiceProxy, dto.id, dto.type, dto.name, folder, dto.configuration, parent);
this._debugSessions.set(ds.id, ds);
this._debugServiceProxy.$sessionCached(ds.id);
}
@ -872,7 +873,8 @@ export class ExtHostDebugSession implements vscode.DebugSession {
private _type: string,
private _name: string,
private _workspaceFolder: vscode.WorkspaceFolder | undefined,
private _configuration: vscode.DebugConfiguration) {
private _configuration: vscode.DebugConfiguration,
private _parentSession: vscode.DebugSession | undefined) {
}
public get id(): string {
@ -886,12 +888,15 @@ export class ExtHostDebugSession implements vscode.DebugSession {
public get name(): string {
return this._name;
}
public set name(name: string) {
this._name = name;
this._debugServiceProxy.$setDebugSessionName(this._id, name);
}
public get parentSession(): vscode.DebugSession | undefined {
return this._parentSession;
}
_acceptNameChanged(name: string) {
this._name = name;
}

View file

@ -292,19 +292,19 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
try {
if (typeof extension.module.deactivate === 'function') {
result = Promise.resolve(extension.module.deactivate()).then(undefined, (err) => {
// TODO: Do something with err if this is not the shutdown case
this._logService.error(err);
return Promise.resolve(undefined);
});
}
} catch (err) {
// TODO: Do something with err if this is not the shutdown case
this._logService.error(err);
}
// clean up subscriptions
try {
dispose(extension.subscriptions);
} catch (err) {
// TODO: Do something with err if this is not the shutdown case
this._logService.error(err);
}
return result;

View file

@ -56,6 +56,11 @@ export class ExtensionMemento implements vscode.Memento {
}, 0);
}
get keys(): readonly string[] {
// Filter out `undefined` values, as they can stick around in the `_value` until the `onDidChangeStorage` event runs
return Object.entries(this._value ?? {}).filter(([, value]) => value !== undefined).map(([key]) => key);
}
get whenReady(): Promise<ExtensionMemento> {
return this._init;
}

View file

@ -11,7 +11,7 @@ import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
import { ExtHostDocumentsAndEditors, IExtHostModelAddedData } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
import * as extHostTypeConverters from 'vs/workbench/api/common/extHostTypeConverters';
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
import { CellKind, IMainCellDto, IOutputDto, IOutputItemDto, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookCellsSplice2 } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CellKind, IMainCellDto, IOutputDto, IOutputItemDto, NotebookCellInternalMetadata, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookCellsSplice2 } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import * as vscode from 'vscode';
class RawContentChangeEvent {
@ -48,7 +48,7 @@ export class ExtHostCell {
private _metadata: extHostTypes.NotebookCellMetadata;
private _previousResult: vscode.NotebookCellExecutionSummary | undefined;
private _internalMetadata: NotebookCellMetadata;
private _internalMetadata: NotebookCellInternalMetadata;
readonly handle: number;
readonly uri: URI;
readonly cellKind: CellKind;
@ -64,12 +64,12 @@ export class ExtHostCell {
this.uri = URI.revive(_cellData.uri);
this.cellKind = _cellData.cellKind;
this._outputs = _cellData.outputs.map(extHostTypeConverters.NotebookCellOutput.to);
this._internalMetadata = _cellData.metadata ?? {};
this._metadata = extHostTypeConverters.NotebookCellMetadata.to(this._internalMetadata);
this._previousResult = extHostTypeConverters.NotebookCellPreviousExecutionResult.to(this._internalMetadata);
this._internalMetadata = _cellData.internalMetadata ?? {};
this._metadata = extHostTypeConverters.NotebookCellMetadata.to(_cellData.metadata ?? {});
this._previousResult = extHostTypeConverters.NotebookCellExecutionSummary.to(_cellData.internalMetadata ?? {});
}
get internalMetadata(): NotebookCellMetadata {
get internalMetadata(): NotebookCellInternalMetadata {
return this._internalMetadata;
}
@ -109,9 +109,12 @@ export class ExtHostCell {
}
setMetadata(newMetadata: NotebookCellMetadata): void {
this._internalMetadata = newMetadata;
this._metadata = extHostTypeConverters.NotebookCellMetadata.to(newMetadata);
this._previousResult = extHostTypeConverters.NotebookCellPreviousExecutionResult.to(newMetadata);
}
setInternalMetadata(newInternalMetadata: NotebookCellInternalMetadata): void {
this._internalMetadata = newInternalMetadata;
this._previousResult = extHostTypeConverters.NotebookCellExecutionSummary.to(newInternalMetadata);
}
}
@ -213,6 +216,8 @@ export class ExtHostNotebookDocument {
this._changeCellLanguage(rawEvent.index, rawEvent.language);
} else if (rawEvent.kind === NotebookCellsChangeType.ChangeCellMetadata) {
this._changeCellMetadata(rawEvent.index, rawEvent.metadata);
} else if (rawEvent.kind === NotebookCellsChangeType.ChangeCellInternalMetadata) {
this._changeCellInternalMetadata(rawEvent.index, rawEvent.internalMetadata);
}
}
}
@ -334,7 +339,6 @@ export class ExtHostNotebookDocument {
private _changeCellMetadata(index: number, newMetadata: NotebookCellMetadata): void {
const cell = this._cells[index];
const originalInternalMetadata = cell.internalMetadata;
const originalExtMetadata = cell.apiCell.metadata;
cell.setMetadata(newMetadata);
const newExtMetadata = cell.apiCell.metadata;
@ -342,9 +346,16 @@ export class ExtHostNotebookDocument {
if (!equals(originalExtMetadata, newExtMetadata)) {
this._emitter.emitCellMetadataChange(deepFreeze({ document: this.apiNotebook, cell: cell.apiCell }));
}
}
if (originalInternalMetadata.runState !== newMetadata.runState) {
const executionState = newMetadata.runState ?? extHostTypes.NotebookCellExecutionState.Idle;
private _changeCellInternalMetadata(index: number, newInternalMetadata: NotebookCellInternalMetadata): void {
const cell = this._cells[index];
const originalInternalMetadata = cell.internalMetadata;
cell.setInternalMetadata(newInternalMetadata);
if (originalInternalMetadata.runState !== newInternalMetadata.runState) {
const executionState = newInternalMetadata.runState ?? extHostTypes.NotebookCellExecutionState.Idle;
this._emitter.emitCellExecutionStateChange(deepFreeze({ document: this.apiNotebook, cell: cell.apiCell, executionState }));
}
}

View file

@ -16,9 +16,8 @@ import { asWebviewUri } from 'vs/workbench/api/common/shared/webview';
import { ResourceMap } from 'vs/base/common/map';
import { timeout } from 'vs/base/common/async';
import { ExtHostCell, ExtHostNotebookDocument } from 'vs/workbench/api/common/extHostNotebookDocument';
import { CellEditType, IImmediateCellEditOperation, NullablePartialNotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CellEditType, IImmediateCellEditOperation, NotebookCellExecutionState, NullablePartialNotebookCellInternalMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { NotebookCellExecutionState } from 'vs/workbench/api/common/extHostTypes';
import { asArray } from 'vs/base/common/arrays';
import { ILogService } from 'vs/platform/log/common/log';
@ -373,8 +372,8 @@ class NotebookCellExecutionTask extends Disposable {
}
}
private mixinMetadata(mixinMetadata: NullablePartialNotebookCellMetadata) {
const edit: IImmediateCellEditOperation = { editType: CellEditType.PartialMetadata, handle: this._cell.handle, metadata: mixinMetadata };
private mixinMetadata(mixinMetadata: NullablePartialNotebookCellInternalMetadata) {
const edit: IImmediateCellEditOperation = { editType: CellEditType.PartialInternalMetadata, handle: this._cell.handle, internalMetadata: mixinMetadata };
this.applyEdits([edit]);
}
@ -426,7 +425,7 @@ class NotebookCellExecutionTask extends Disposable {
that._onDidChangeState.fire();
that.mixinMetadata({
runState: NotebookCellExecutionState.Idle,
runState: null,
lastRunSuccess: result?.success ?? null,
runEndTime: result?.endTime ?? null,
});

View file

@ -10,8 +10,10 @@ import { MainContext, MainThreadStatusBarShape, IMainContext, ICommandDto } from
import { localize } from 'vs/nls';
import { CommandsConverter } from 'vs/workbench/api/common/extHostCommands';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
export class ExtHostStatusBarEntry implements vscode.StatusBarItem {
private static ID_GEN = 0;
private static ALLOWED_BACKGROUND_COLORS = new Map<string, ThemeColor>(
@ -21,17 +23,20 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem {
#proxy: MainThreadStatusBarShape;
#commands: CommandsConverter;
private _id: number;
private _entryId: number;
private _extension?: IExtensionDescription;
private _id?: string;
private _alignment: number;
private _priority?: number;
private _disposed: boolean = false;
private _visible: boolean = false;
private _statusId: string;
private _statusName: string;
private _text: string = '';
private _tooltip?: string;
private _name?: string;
private _color?: string | ThemeColor;
private _backgroundColor?: ThemeColor;
private readonly _internalCommandRegistration = new DisposableStore();
@ -43,20 +48,23 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem {
private _timeoutHandle: any;
private _accessibilityInformation?: vscode.AccessibilityInformation;
constructor(proxy: MainThreadStatusBarShape, commands: CommandsConverter, id: string, name: string, alignment: ExtHostStatusBarAlignment = ExtHostStatusBarAlignment.Left, priority?: number, accessibilityInformation?: vscode.AccessibilityInformation) {
constructor(proxy: MainThreadStatusBarShape, commands: CommandsConverter, extension: IExtensionDescription, id?: string, alignment?: ExtHostStatusBarAlignment, priority?: number);
constructor(proxy: MainThreadStatusBarShape, commands: CommandsConverter, extension: IExtensionDescription | undefined, id: string, alignment?: ExtHostStatusBarAlignment, priority?: number);
constructor(proxy: MainThreadStatusBarShape, commands: CommandsConverter, extension?: IExtensionDescription, id?: string, alignment: ExtHostStatusBarAlignment = ExtHostStatusBarAlignment.Left, priority?: number) {
this.#proxy = proxy;
this.#commands = commands;
this._id = ExtHostStatusBarEntry.ID_GEN++;
this._statusId = id;
this._statusName = name;
this._entryId = ExtHostStatusBarEntry.ID_GEN++;
this._extension = extension;
this._id = id;
this._alignment = alignment;
this._priority = priority;
this._accessibilityInformation = accessibilityInformation;
}
public get id(): number {
return this._id;
public get id(): string {
return this._id ?? this._extension!.identifier.value;
}
public get alignment(): vscode.StatusBarAlignment {
@ -71,6 +79,10 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem {
return this._text;
}
public get name(): string | undefined {
return this._name;
}
public get tooltip(): string | undefined {
return this._tooltip;
}
@ -96,6 +108,11 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem {
this.update();
}
public set name(name: string | undefined) {
this._name = name;
this.update();
}
public set tooltip(tooltip: string | undefined) {
this._tooltip = tooltip;
this.update();
@ -150,7 +167,7 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem {
public hide(): void {
clearTimeout(this._timeoutHandle);
this._visible = false;
this.#proxy.$dispose(this.id);
this.#proxy.$dispose(this._entryId);
}
private update(): void {
@ -164,6 +181,28 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem {
this._timeoutHandle = setTimeout(() => {
this._timeoutHandle = undefined;
// If the id is not set, derive it from the extension identifier,
// otherwise make sure to prefix it with the extension identifier
// to get a more unique value across extensions.
let id: string;
if (this._extension) {
if (this._id) {
id = `${this._extension.identifier.value}.${this._id}`;
} else {
id = this._extension.identifier.value;
}
} else {
id = this._id!;
}
// If the name is not set, derive it from the extension descriptor
let name: string;
if (this._name) {
name = this._name;
} else {
name = localize('extensionLabel', "{0} (Extension)", this._extension!.displayName || this._extension!.name);
}
// If a background color is set, the foreground is determined
let color = this._color;
if (this._backgroundColor) {
@ -171,7 +210,7 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem {
}
// Set to status bar
this.#proxy.$setEntry(this.id, this._statusId, this._statusName, this._text, this._tooltip, this._command?.internal, color,
this.#proxy.$setEntry(this._entryId, id, name, this._text, this._tooltip, this._command?.internal, color,
this._backgroundColor, this._alignment === ExtHostStatusBarAlignment.Left ? MainThreadStatusBarAlignment.LEFT : MainThreadStatusBarAlignment.RIGHT,
this._priority, this._accessibilityInformation);
}, 0);
@ -189,7 +228,8 @@ class StatusBarMessage {
private _messages: { message: string }[] = [];
constructor(statusBar: ExtHostStatusBar) {
this._item = statusBar.createStatusBarEntry('status.extensionMessage', localize('status.extensionMessage', "Extension Status"), ExtHostStatusBarAlignment.Left, Number.MIN_VALUE);
this._item = statusBar.createStatusBarEntry(undefined, 'status.extensionMessage', ExtHostStatusBarAlignment.Left, Number.MIN_VALUE);
this._item.name = localize('status.extensionMessage', "Extension Status");
}
dispose() {
@ -233,12 +273,13 @@ export class ExtHostStatusBar {
this._statusMessage = new StatusBarMessage(this);
}
createStatusBarEntry(id: string, name: string, alignment?: ExtHostStatusBarAlignment, priority?: number, accessibilityInformation?: vscode.AccessibilityInformation): vscode.StatusBarItem {
return new ExtHostStatusBarEntry(this._proxy, this._commands, id, name, alignment, priority, accessibilityInformation);
createStatusBarEntry(extension: IExtensionDescription | undefined, id: string, alignment?: ExtHostStatusBarAlignment, priority?: number): vscode.StatusBarItem;
createStatusBarEntry(extension: IExtensionDescription, id?: string, alignment?: ExtHostStatusBarAlignment, priority?: number): vscode.StatusBarItem;
createStatusBarEntry(extension: IExtensionDescription, id: string, alignment?: ExtHostStatusBarAlignment, priority?: number): vscode.StatusBarItem {
return new ExtHostStatusBarEntry(this._proxy, this._commands, extension, id, alignment, priority);
}
setStatusBarMessage(text: string, timeoutOrThenable?: number | Thenable<any>): Disposable {
const d = this._statusMessage.setMessage(text);
let handle: any;

View file

@ -18,7 +18,7 @@ import { serializeEnvironmentVariableCollection } from 'vs/workbench/contrib/ter
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { generateUuid } from 'vs/base/common/uuid';
import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
import { IShellLaunchConfigDto, ITerminalChildProcess, ITerminalDimensionsOverride, ITerminalEnvironment, ITerminalLaunchError, ITerminalProfile, TerminalShellType } from 'vs/platform/terminal/common/terminal';
import { IProcessReadyEvent, IShellLaunchConfigDto, ITerminalChildProcess, ITerminalDimensionsOverride, ITerminalEnvironment, ITerminalLaunchError, ITerminalProfile, TerminalShellType } from 'vs/platform/terminal/common/terminal';
import { TerminalDataBufferer } from 'vs/platform/terminal/common/terminalDataBuffering';
export interface IExtHostTerminalService extends ExtHostTerminalServiceShape, IDisposable {
@ -193,8 +193,8 @@ export class ExtHostPseudoterminal implements ITerminalChildProcess {
public readonly onProcessData: Event<string> = this._onProcessData.event;
private readonly _onProcessExit = new Emitter<number | undefined>();
public readonly onProcessExit: Event<number | undefined> = this._onProcessExit.event;
private readonly _onProcessReady = new Emitter<{ pid: number, cwd: string }>();
public get onProcessReady(): Event<{ pid: number, cwd: string }> { return this._onProcessReady.event; }
private readonly _onProcessReady = new Emitter<IProcessReadyEvent>();
public get onProcessReady(): Event<IProcessReadyEvent> { return this._onProcessReady.event; }
private readonly _onProcessTitleChanged = new Emitter<string>();
public readonly onProcessTitleChanged: Event<string> = this._onProcessTitleChanged.event;
private readonly _onProcessOverrideDimensions = new Emitter<ITerminalDimensionsOverride | undefined>();

View file

@ -84,7 +84,8 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
if (!options || !options.treeDataProvider) {
throw new Error('Options with treeDataProvider is mandatory');
}
const registerPromise = this._proxy.$registerTreeViewDataProvider(viewId, { showCollapseAll: !!options.showCollapseAll, canSelectMany: !!options.canSelectMany, canDragAndDrop: !!options.canDragAndDrop });
const canDragAndDrop = options.dragAndDropController !== undefined;
const registerPromise = this._proxy.$registerTreeViewDataProvider(viewId, { showCollapseAll: !!options.showCollapseAll, canSelectMany: !!options.canSelectMany, canDragAndDrop: canDragAndDrop });
const treeView = this.createExtHostTreeView(viewId, options, extension);
return {
get onDidCollapseElement() { return treeView.onDidCollapseElement; },
@ -127,12 +128,12 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
return treeView.getChildren(treeItemHandle);
}
$setParent(treeViewId: string, treeItemHandles: string[], newParentItemHandle: string): Promise<void> {
$onDrop(treeViewId: string, treeItemHandles: string[], newParentItemHandle: string): Promise<void> {
const treeView = this.treeViews.get(treeViewId);
if (!treeView) {
return Promise.reject(new Error(localize('treeView.notRegistered', 'No tree view with id \'{0}\' registered.', treeViewId)));
}
return treeView.setParent(treeItemHandles, newParentItemHandle);
return treeView.onDrop(treeItemHandles, newParentItemHandle);
}
async $hasResolve(treeViewId: string): Promise<boolean> {
@ -204,6 +205,7 @@ class ExtHostTreeView<T> extends Disposable {
private static readonly ID_HANDLE_PREFIX = '1';
private readonly dataProvider: vscode.TreeDataProvider<T>;
private readonly dndController: vscode.DragAndDropController<T> | undefined;
private roots: TreeNode[] | null = null;
private elements: Map<TreeItemHandle, T> = new Map<TreeItemHandle, T>();
@ -250,6 +252,7 @@ class ExtHostTreeView<T> extends Disposable {
}
}
this.dataProvider = options.treeDataProvider;
this.dndController = options.dragAndDropController;
if (this.dataProvider.onDidChangeTreeData) {
this._register(this.dataProvider.onDidChangeTreeData(element => this._onDidChangeData.fire({ message: false, element })));
}
@ -377,11 +380,11 @@ class ExtHostTreeView<T> extends Disposable {
}
}
setParent(treeItemHandleOrNodes: TreeItemHandle[], newParentHandleOrNode: TreeItemHandle): Promise<void> {
onDrop(treeItemHandleOrNodes: TreeItemHandle[], targetHandleOrNode: TreeItemHandle): Promise<void> {
const elements = <T[]>treeItemHandleOrNodes.map(item => this.getExtensionElement(item)).filter(element => !isUndefinedOrNull(element));
const newParentElement = this.getExtensionElement(newParentHandleOrNode);
if (this.dataProvider.setParent && elements && newParentElement) {
return asPromise(() => this.dataProvider.setParent!(elements, newParentElement));
const target = this.getExtensionElement(targetHandleOrNode);
if (elements && target) {
return asPromise(() => this.dndController?.onDrop(elements, target));
}
return Promise.resolve(undefined);
}

View file

@ -1443,8 +1443,8 @@ export namespace NotebookDocumentMetadata {
}
}
export namespace NotebookCellPreviousExecutionResult {
export function to(data: notebooks.NotebookCellMetadata): vscode.NotebookCellExecutionSummary {
export namespace NotebookCellExecutionSummary {
export function to(data: notebooks.NotebookCellInternalMetadata): vscode.NotebookCellExecutionSummary {
return {
startTime: data.runStartTime,
endTime: data.runEndTime,
@ -1453,7 +1453,7 @@ export namespace NotebookCellPreviousExecutionResult {
};
}
export function from(data: vscode.NotebookCellExecutionSummary): Partial<notebooks.NotebookCellMetadata> {
export function from(data: vscode.NotebookCellExecutionSummary): Partial<notebooks.NotebookCellInternalMetadata> {
return {
lastRunSuccess: data.success,
runStartTime: data.startTime,
@ -1492,10 +1492,8 @@ export namespace NotebookCellData {
cellKind: NotebookCellKind.from(data.kind),
language: data.languageId,
source: data.value,
metadata: {
...data.metadata,
...NotebookCellPreviousExecutionResult.from(data.executionSummary ?? {})
},
metadata: data.metadata,
internalMetadata: NotebookCellExecutionSummary.from(data.executionSummary ?? {}),
outputs: data.outputs ? data.outputs.map(NotebookCellOutput.from) : []
};
}
@ -1642,15 +1640,7 @@ export namespace NotebookDocumentContentOptions {
export function from(options: vscode.NotebookDocumentContentOptions | undefined): notebooks.TransientOptions {
return {
transientOutputs: options?.transientOutputs ?? false,
transientCellMetadata: {
...options?.transientCellMetadata,
executionOrder: true,
runState: true,
runStartTime: true,
runStartTimeAdjustment: true,
runEndTime: true,
lastRunSuccess: true
},
transientCellMetadata: options?.transientCellMetadata ?? {},
transientDocumentMetadata: options?.transientDocumentMetadata ?? {}
};
}

View file

@ -182,6 +182,12 @@ const apiMenus: IAPIMenu[] = [
description: localize('notebook.toolbar', "The contributed notebook toolbar menu"),
proposed: true
},
{
key: 'notebook/toolbar/right',
id: MenuId.NotebookRightToolbar,
description: localize('notebook.toolbar.right', "The contributed notebook right toolbar menu"),
proposed: true
},
{
key: 'notebook/cell/title',
id: MenuId.NotebookCellTitle,

View file

@ -32,6 +32,7 @@ class OutputAppender {
append(content: string): void {
this.appender.critical(content);
this.flush();
}
flush(): void {

View file

@ -319,7 +319,8 @@ MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
command: {
id: OpenWorkspaceConfigFileAction.ID,
title: { value: `${workspacesCategory}: ${OpenWorkspaceConfigFileAction.LABEL}`, original: 'Workspaces: Open Workspace Configuration File' },
title: { value: OpenWorkspaceConfigFileAction.LABEL, original: 'Workspaces: Open Workspace Configuration File' },
category: workspacesCategory
},
when: WorkbenchStateContext.isEqualTo('workspace')
});

View file

@ -232,11 +232,11 @@ export class ResourcesDropHandler {
private async handleDirtyEditorDrop(droppedDirtyEditor: IDraggedEditor): Promise<boolean> {
const fileEditorFactory = Registry.as<IEditorInputFactoryRegistry>(EditorExtensions.EditorInputFactories).getFileEditorInputFactory();
// Untitled: always ensure that we open a new untitled editor for each file we drop
// Untitled: always ensure that we open a new untitled text editor for each file we drop
if (droppedDirtyEditor.resource.scheme === Schemas.untitled) {
const untitledEditorResource = this.editorService.createEditorInput({ mode: droppedDirtyEditor.mode, encoding: droppedDirtyEditor.encoding, forceUntitled: true }).resource;
if (untitledEditorResource) {
droppedDirtyEditor.resource = untitledEditorResource;
const untitledTextEditorResource = this.editorService.createEditorInput({ mode: droppedDirtyEditor.mode, encoding: droppedDirtyEditor.encoding, forceUntitled: true }).resource;
if (untitledTextEditorResource) {
droppedDirtyEditor.resource = untitledTextEditorResource;
}
}

View file

@ -1231,6 +1231,10 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
this.storageService.store(Storage.PANEL_SIZE, panelSize, StorageScope.GLOBAL, StorageTarget.MACHINE);
this.storageService.store(Storage.PANEL_DIMENSION, positionToString(this.state.panel.position), StorageScope.GLOBAL, StorageTarget.MACHINE);
// Remember last panel size for both dimensions
this.storageService.store(Storage.PANEL_LAST_NON_MAXIMIZED_HEIGHT, this.state.panel.lastNonMaximizedHeight, StorageScope.GLOBAL, StorageTarget.MACHINE);
this.storageService.store(Storage.PANEL_LAST_NON_MAXIMIZED_WIDTH, this.state.panel.lastNonMaximizedWidth, StorageScope.GLOBAL, StorageTarget.MACHINE);
const gridSize = grid.getViewSize();
this.storageService.store(Storage.GRID_WIDTH, gridSize.width, StorageScope.GLOBAL, StorageTarget.MACHINE);
this.storageService.store(Storage.GRID_HEIGHT, gridSize.height, StorageScope.GLOBAL, StorageTarget.MACHINE);
@ -1362,6 +1366,10 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
this.workbenchGrid.setViewVisible(this.activityBarPartView, !hidden);
}
setBannerHidden(hidden: boolean): void {
this.workbenchGrid.setViewVisible(this.bannerPartView, !hidden);
}
setEditorHidden(hidden: boolean, skipLayout?: boolean): void {
this.state.editor.hidden = hidden;
@ -1802,7 +1810,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
{
type: 'leaf',
data: { type: Parts.BANNER_PART },
size: bannerHeight
size: bannerHeight,
visible: false
},
{
type: 'branch',

View file

@ -1,33 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { localize } from 'vs/nls';
import { Action2, registerAction2 } from 'vs/platform/actions/common/actions';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { CATEGORIES } from 'vs/workbench/common/actions';
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
class FocusBannerAction extends Action2 {
static readonly ID = 'workbench.action.focusBanner';
static readonly LABEL = localize('focusBanner', "Focus Banner");
constructor() {
super({
id: FocusBannerAction.ID,
title: { value: FocusBannerAction.LABEL, original: 'Focus Banner' },
category: CATEGORIES.View,
f1: true
});
}
async run(accessor: ServicesAccessor): Promise<void> {
const layoutService = accessor.get(IWorkbenchLayoutService);
layoutService.focusPart(Parts.BANNER_PART);
}
}
registerAction2(FocusBannerAction);

View file

@ -4,23 +4,28 @@
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/bannerpart';
import { $, append, clearNode } from 'vs/base/browser/dom';
import { localize } from 'vs/nls';
import { $, addDisposableListener, append, clearNode, EventType } from 'vs/base/browser/dom';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { Codicon, registerCodicon } from 'vs/base/common/codicons';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IStorageService, StorageTarget } from 'vs/platform/storage/common/storage';
import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { Part } from 'vs/workbench/browser/part';
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
import { Action } from 'vs/base/common/actions';
import { Link } from 'vs/platform/opener/browser/link';
import { attachLinkStyler } from 'vs/platform/theme/common/styler';
import { MarkdownString } from 'vs/base/common/htmlContent';
import { Emitter } from 'vs/base/common/event';
import { IBannerItem, IBannerService } from 'vs/workbench/services/banner/browser/bannerService';
import { MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer';
import { BANNER_BACKGROUND, BANNER_FOREGROUND, BANNER_ICON_FOREGROUND } from 'vs/workbench/common/theme';
import { Action2, registerAction2 } from 'vs/platform/actions/common/actions';
import { CATEGORIES } from 'vs/workbench/common/actions';
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { KeyCode } from 'vs/base/common/keyCodes';
import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
// Icons
@ -32,18 +37,21 @@ const bannerCloseIcon = registerCodicon('banner-close', Codicon.close);
registerThemingParticipant((theme, collector) => {
const backgroundColor = theme.getColor(BANNER_BACKGROUND);
const foregroundColor = theme.getColor(BANNER_FOREGROUND);
const iconForegroundColor = theme.getColor(BANNER_ICON_FOREGROUND);
if (backgroundColor) {
collector.addRule(`.monaco-workbench .part.banner { background-color: ${backgroundColor}; }`);
}
const foregroundColor = theme.getColor(BANNER_FOREGROUND);
if (foregroundColor) {
collector.addRule(`.monaco-workbench .part.banner { color: ${foregroundColor}; }`);
collector.addRule(`.monaco-workbench .part.banner .action-container .codicon { color: ${foregroundColor}; }`);
collector.addRule(`
.monaco-workbench .part.banner,
.monaco-workbench .part.banner .action-container .codicon,
.monaco-workbench .part.banner .message-actions-container .monaco-link
{ color: ${foregroundColor}; }
`);
}
const iconForegroundColor = theme.getColor(BANNER_ICON_FOREGROUND);
if (iconForegroundColor) {
collector.addRule(`.monaco-workbench .part.banner .icon-container .codicon { color: ${iconForegroundColor} }`);
}
@ -52,6 +60,8 @@ registerThemingParticipant((theme, collector) => {
// Banner Part
const CONTEXT_BANNER_FOCUSED = new RawContextKey<boolean>('bannerFocused', false, localize('bannerFocused', "Whether the banner has keyboard focus"));
export class BannerPart extends Part implements IBannerService {
declare readonly _serviceBrand: undefined;
@ -79,9 +89,14 @@ export class BannerPart extends Part implements IBannerService {
private readonly markdownRenderer: MarkdownRenderer;
private visible = false;
private actionBar: ActionBar | undefined;
private messageActionsContainer: HTMLElement | undefined;
private focusedActionIndex: number = -1;
constructor(
@IThemeService themeService: IThemeService,
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IStorageService private readonly storageService: IStorageService,
) {
@ -92,6 +107,19 @@ export class BannerPart extends Part implements IBannerService {
override createContentArea(parent: HTMLElement): HTMLElement {
this.element = parent;
this.element.tabIndex = 0;
// Restore focused action if needed
this._register(addDisposableListener(this.element, EventType.FOCUS, () => {
if (this.focusedActionIndex !== -1) {
this.focusActionLink();
}
}));
// Track focus
const scopedContextKeyService = this.contextKeyService.createScoped(this.element);
CONTEXT_BANNER_FOCUSED.bindTo(scopedContextKeyService).set(true);
return this.element;
}
@ -110,6 +138,31 @@ export class BannerPart extends Part implements IBannerService {
this.item = undefined;
}
private focusActionLink(): void {
const length = this.item?.actions?.length ?? 0;
if (this.focusedActionIndex < length) {
const actionLink = this.messageActionsContainer?.children[this.focusedActionIndex];
if (actionLink instanceof HTMLElement) {
this.actionBar?.setFocusable(false);
actionLink.focus();
}
} else {
this.actionBar?.focus(0);
}
}
private getAriaLabel(item: IBannerItem): string | undefined {
if (item.ariaLabel) {
return item.ariaLabel;
}
if (typeof item.message === 'string') {
return item.message;
}
return undefined;
}
private getBannerMessage(message: MarkdownString | string): HTMLElement {
if (typeof message === 'string') {
const element = $('span');
@ -123,11 +176,32 @@ export class BannerPart extends Part implements IBannerService {
private setVisibility(visible: boolean): void {
if (visible !== this.visible) {
this.visible = visible;
this.focusedActionIndex = -1;
this.layoutService.setBannerHidden(!visible);
this._onDidChangeSize.fire(undefined);
}
}
focus(): void {
this.focusedActionIndex = -1;
this.element.focus();
}
focusNextAction(): void {
const length = this.item?.actions?.length ?? 0;
this.focusedActionIndex = this.focusedActionIndex < length ? this.focusedActionIndex + 1 : 0;
this.focusActionLink();
}
focusPreviousAction(): void {
const length = this.item?.actions?.length ?? 0;
this.focusedActionIndex = this.focusedActionIndex > 0 ? this.focusedActionIndex - 1 : length;
this.focusActionLink();
}
hide(id: string): void {
if (this.item?.id !== id) {
return;
@ -149,30 +223,40 @@ export class BannerPart extends Part implements IBannerService {
// Clear previous item
clearNode(this.element);
// Banner aria label
const ariaLabel = this.getAriaLabel(item);
if (ariaLabel) {
this.element.setAttribute('aria-label', ariaLabel);
}
// Icon
const iconContainer = append(this.element, $('div.icon-container'));
iconContainer.setAttribute('aria-hidden', 'true');
iconContainer.appendChild($(`div${item.icon.cssSelector}`));
// Message
const messageContainer = append(this.element, $('div.message-container'));
messageContainer.setAttribute('aria-hidden', 'true');
messageContainer.appendChild(this.getBannerMessage(item.message));
// Message Actions
if (item.actions) {
const actionContainer = append(this.element, $('div.message-actions-container'));
this.messageActionsContainer = append(this.element, $('div.message-actions-container'));
for (const action of item.actions) {
const actionLink = this._register(this.instantiationService.createInstance(Link, action, {}));
this._register(attachLinkStyler(actionLink, this.themeService, { textLinkForeground: BANNER_FOREGROUND }));
actionContainer.appendChild(actionLink.el);
actionLink.el.tabIndex = -1;
actionLink.el.setAttribute('role', 'button');
this.messageActionsContainer.appendChild(actionLink.el);
}
}
// Action
const actionBarContainer = append(this.element, $('div.action-container'));
const actionBar = this._register(new ActionBar(actionBarContainer));
this.actionBar = this._register(new ActionBar(actionBarContainer));
const closeAction = this._register(new Action('banner.close', 'Close Banner', bannerCloseIcon.classNames, true, () => this.close(item)));
actionBar.push(closeAction, { icon: true, label: false });
this.actionBar.push(closeAction, { icon: true, label: false });
this.actionBar.setFocusable(false);
this.setVisibility(true);
this.item = item;
@ -186,3 +270,66 @@ export class BannerPart extends Part implements IBannerService {
}
registerSingleton(IBannerService, BannerPart);
// Keybindings
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'workbench.banner.focusBanner',
weight: KeybindingWeight.WorkbenchContrib,
primary: KeyCode.Escape,
when: CONTEXT_BANNER_FOCUSED,
handler: (accessor: ServicesAccessor) => {
const bannerService = accessor.get(IBannerService);
bannerService.focus();
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'workbench.banner.focusNextAction',
weight: KeybindingWeight.WorkbenchContrib,
primary: KeyCode.RightArrow,
secondary: [KeyCode.DownArrow],
when: CONTEXT_BANNER_FOCUSED,
handler: (accessor: ServicesAccessor) => {
const bannerService = accessor.get(IBannerService);
bannerService.focusNextAction();
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'workbench.banner.focusPreviousAction',
weight: KeybindingWeight.WorkbenchContrib,
primary: KeyCode.LeftArrow,
secondary: [KeyCode.UpArrow],
when: CONTEXT_BANNER_FOCUSED,
handler: (accessor: ServicesAccessor) => {
const bannerService = accessor.get(IBannerService);
bannerService.focusPreviousAction();
}
});
// Actions
class FocusBannerAction extends Action2 {
static readonly ID = 'workbench.action.focusBanner';
static readonly LABEL = localize('focusBanner', "Focus Banner");
constructor() {
super({
id: FocusBannerAction.ID,
title: { value: FocusBannerAction.LABEL, original: 'Focus Banner' },
category: CATEGORIES.View,
f1: true
});
}
async run(accessor: ServicesAccessor): Promise<void> {
const layoutService = accessor.get(IWorkbenchLayoutService);
layoutService.focusPart(Parts.BANNER_PART);
}
}
registerAction2(FocusBannerAction);

View file

@ -7,7 +7,7 @@
box-sizing: border-box;
cursor: default;
width: 100%;
height: 26px;
height: 100%;
font-size: 12px;
display: flex;
overflow: visible;
@ -38,6 +38,7 @@
}
.monaco-workbench .part.banner .message-actions-container a {
padding: 3px;
margin-left: 12px;
text-decoration: underline;
}

View file

@ -332,8 +332,8 @@ class DropOverlay extends Themable {
proposedFilePath = joinPath(defaultFilePath, name);
}
// Open as untitled file with the provided contents
const untitledEditor = this.editorService.createEditorInput({
// Open as untitled text file with the provided contents
const untitledTextEditor = this.editorService.createEditorInput({
resource: proposedFilePath,
forceUntitled: true,
contents: VSBuffer.wrap(new Uint8Array(event.target.result)).toString()
@ -343,7 +343,7 @@ class DropOverlay extends Themable {
targetGroup = ensureTargetGroup();
}
await targetGroup.openEditor(untitledEditor);
await targetGroup.openEditor(untitledTextEditor);
}
};
}
@ -355,9 +355,7 @@ class DropOverlay extends Themable {
else {
const dropHandler = this.instantiationService.createInstance(ResourcesDropHandler, { allowWorkspaceOpen: true /* open workspace instead of file if dropped */ });
dropHandler.handleDrop(event, () => ensureTargetGroup(), targetGroup => {
if (targetGroup) {
targetGroup.focus();
}
targetGroup?.focus();
});
}
}

View file

@ -404,13 +404,14 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
if (!this.tabFocusModeElement.value) {
const text = localize('tabFocusModeEnabled', "Tab Moves Focus");
this.tabFocusModeElement.value = this.statusbarService.addEntry({
name: localize('status.editor.tabFocusMode', "Accessibility Mode"),
text,
ariaLabel: text,
tooltip: localize('disableTabMode', "Disable Accessibility Mode"),
command: 'editor.action.toggleTabFocusMode',
backgroundColor: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_BACKGROUND),
color: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_FOREGROUND)
}, 'status.editor.tabFocusMode', localize('status.editor.tabFocusMode', "Accessibility Mode"), StatusbarAlignment.RIGHT, 100.7);
}, 'status.editor.tabFocusMode', StatusbarAlignment.RIGHT, 100.7);
}
} else {
this.tabFocusModeElement.clear();
@ -422,13 +423,14 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
if (!this.columnSelectionModeElement.value) {
const text = localize('columnSelectionModeEnabled', "Column Selection");
this.columnSelectionModeElement.value = this.statusbarService.addEntry({
name: localize('status.editor.columnSelectionMode', "Column Selection Mode"),
text,
ariaLabel: text,
tooltip: 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', localize('status.editor.columnSelectionMode', "Column Selection Mode"), StatusbarAlignment.RIGHT, 100.8);
}, 'status.editor.columnSelectionMode', StatusbarAlignment.RIGHT, 100.8);
}
} else {
this.columnSelectionModeElement.clear();
@ -440,12 +442,13 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
if (!this.screenRedearModeElement.value) {
const text = localize('screenReaderDetected', "Screen Reader Optimized");
this.screenRedearModeElement.value = this.statusbarService.addEntry({
name: localize('status.editor.screenReaderMode', "Screen Reader Mode"),
text,
ariaLabel: text,
command: 'showEditorScreenReaderNotification',
backgroundColor: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_BACKGROUND),
color: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_FOREGROUND)
}, 'status.editor.screenReaderMode', localize('status.editor.screenReaderMode', "Screen Reader Mode"), StatusbarAlignment.RIGHT, 100.6);
}, 'status.editor.screenReaderMode', StatusbarAlignment.RIGHT, 100.6);
}
} else {
this.screenRedearModeElement.clear();
@ -459,13 +462,14 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
}
const props: IStatusbarEntry = {
name: localize('status.editor.selection', "Editor Selection"),
text,
ariaLabel: text,
tooltip: localize('gotoLine', "Go to Line/Column"),
command: 'workbench.action.gotoLine'
};
this.updateElement(this.selectionElement, props, 'status.editor.selection', localize('status.editor.selection', "Editor Selection"), StatusbarAlignment.RIGHT, 100.5);
this.updateElement(this.selectionElement, props, 'status.editor.selection', StatusbarAlignment.RIGHT, 100.5);
}
private updateIndentationElement(text: string | undefined): void {
@ -475,13 +479,14 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
}
const props: IStatusbarEntry = {
name: localize('status.editor.indentation', "Editor Indentation"),
text,
ariaLabel: text,
tooltip: localize('selectIndentation', "Select Indentation"),
command: 'changeEditorIndentation'
};
this.updateElement(this.indentationElement, props, 'status.editor.indentation', localize('status.editor.indentation', "Editor Indentation"), StatusbarAlignment.RIGHT, 100.4);
this.updateElement(this.indentationElement, props, 'status.editor.indentation', StatusbarAlignment.RIGHT, 100.4);
}
private updateEncodingElement(text: string | undefined): void {
@ -491,13 +496,14 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
}
const props: IStatusbarEntry = {
name: localize('status.editor.encoding', "Editor Encoding"),
text,
ariaLabel: text,
tooltip: localize('selectEncoding', "Select Encoding"),
command: 'workbench.action.editor.changeEncoding'
};
this.updateElement(this.encodingElement, props, 'status.editor.encoding', localize('status.editor.encoding', "Editor Encoding"), StatusbarAlignment.RIGHT, 100.3);
this.updateElement(this.encodingElement, props, 'status.editor.encoding', StatusbarAlignment.RIGHT, 100.3);
}
private updateEOLElement(text: string | undefined): void {
@ -507,13 +513,14 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
}
const props: IStatusbarEntry = {
name: localize('status.editor.eol', "Editor End of Line"),
text,
ariaLabel: text,
tooltip: localize('selectEOL', "Select End of Line Sequence"),
command: 'workbench.action.editor.changeEOL'
};
this.updateElement(this.eolElement, props, 'status.editor.eol', localize('status.editor.eol', "Editor End of Line"), StatusbarAlignment.RIGHT, 100.2);
this.updateElement(this.eolElement, props, 'status.editor.eol', StatusbarAlignment.RIGHT, 100.2);
}
private updateModeElement(text: string | undefined): void {
@ -523,13 +530,14 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
}
const props: IStatusbarEntry = {
name: localize('status.editor.mode', "Editor Language"),
text,
ariaLabel: text,
tooltip: localize('selectLanguageMode', "Select Language Mode"),
command: 'workbench.action.editor.changeLanguageMode'
};
this.updateElement(this.modeElement, props, 'status.editor.mode', localize('status.editor.mode', "Editor Language"), StatusbarAlignment.RIGHT, 100.1);
this.updateElement(this.modeElement, props, 'status.editor.mode', StatusbarAlignment.RIGHT, 100.1);
}
private updateMetadataElement(text: string | undefined): void {
@ -539,17 +547,18 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
}
const props: IStatusbarEntry = {
name: localize('status.editor.info', "File Information"),
text,
ariaLabel: text,
tooltip: localize('fileInfo', "File Information")
};
this.updateElement(this.metadataElement, props, 'status.editor.info', localize('status.editor.info', "File Information"), StatusbarAlignment.RIGHT, 100);
this.updateElement(this.metadataElement, props, 'status.editor.info', StatusbarAlignment.RIGHT, 100);
}
private updateElement(element: MutableDisposable<IStatusbarEntryAccessor>, props: IStatusbarEntry, id: string, name: string, alignment: StatusbarAlignment, priority: number) {
private updateElement(element: MutableDisposable<IStatusbarEntryAccessor>, props: IStatusbarEntry, id: string, alignment: StatusbarAlignment, priority: number) {
if (!element.value) {
element.value = this.statusbarService.addEntry(props, id, name, alignment, priority);
element.value = this.statusbarService.addEntry(props, id, alignment, priority);
} else {
element.value.update(props);
}
@ -923,9 +932,9 @@ class ShowCurrentMarkerInStatusbarContribution extends Disposable {
const line = splitLines(this.currentMarker.message)[0];
const text = `${this.getType(this.currentMarker)} ${line}`;
if (!this.statusBarEntryAccessor.value) {
this.statusBarEntryAccessor.value = this.statusbarService.addEntry({ text: '', ariaLabel: '' }, 'statusbar.currentProblem', localize('currentProblem', "Current Problem"), StatusbarAlignment.LEFT);
this.statusBarEntryAccessor.value = this.statusbarService.addEntry({ name: localize('currentProblem', "Current Problem"), text: '', ariaLabel: '' }, 'statusbar.currentProblem', StatusbarAlignment.LEFT);
}
this.statusBarEntryAccessor.value.update({ text, ariaLabel: text });
this.statusBarEntryAccessor.value.update({ name: localize('currentProblem', "Current Problem"), text, ariaLabel: text });
} else {
this.statusBarEntryAccessor.clear();
}

View file

@ -290,7 +290,7 @@ export abstract class TitleControl extends Themable {
return false;
}
const editorOptions: ITextEditorOptions = {
let editorOptions: ITextEditorOptions = {
viewState: (() => {
if (this.group.activeEditor === editor) {
const activeControl = this.group.activeEditorPane?.getControl();
@ -304,6 +304,14 @@ export abstract class TitleControl extends Themable {
sticky: this.group.isSticky(editor)
};
// If it's a custom editor or a notebook add the viewtype
if ((editor as object).hasOwnProperty('viewType')) {
interface EditorInputWithViewType extends IEditorInput {
viewType: string;
}
editorOptions = { ...editorOptions, override: (editor as EditorInputWithViewType).viewType };
}
this.instantiationService.invokeFunction(fillResourceDataTransfers, [resource], () => editorOptions, e);
return true;

View file

@ -71,6 +71,7 @@ export class NotificationsStatus extends Disposable {
// Show the bell with a dot if there are unread or in-progress notifications
const statusProperties: IStatusbarEntry = {
name: localize('status.notifications', "Notifications"),
text: `${notificationsInProgress > 0 || this.newNotificationsCount > 0 ? '$(bell-dot)' : '$(bell)'}`,
ariaLabel: localize('status.notifications', "Notifications"),
command: this.isNotificationsCenterVisible ? HIDE_NOTIFICATIONS_CENTER : SHOW_NOTIFICATIONS_CENTER,
@ -82,7 +83,6 @@ export class NotificationsStatus extends Disposable {
this.notificationsCenterStatusItem = this.statusbarService.addEntry(
statusProperties,
'status.notifications',
localize('status.notifications', "Notifications"),
StatusbarAlignment.RIGHT,
-Number.MAX_VALUE /* towards the far end of the right hand side */
);
@ -180,9 +180,12 @@ export class NotificationsStatus extends Disposable {
let statusMessageEntry: IStatusbarEntryAccessor;
let showHandle: any = setTimeout(() => {
statusMessageEntry = this.statusbarService.addEntry(
{ text: message, ariaLabel: message },
{
name: localize('status.message', "Status Message"),
text: message,
ariaLabel: message
},
'status.message',
localize('status.message', "Status Message"),
StatusbarAlignment.LEFT,
-Number.MAX_VALUE /* far right on left hand side */
);

View file

@ -42,13 +42,33 @@ import { syncing } from 'vs/platform/theme/common/iconRegistry';
import { Action2, registerAction2 } from 'vs/platform/actions/common/actions';
import { CATEGORIES } from 'vs/workbench/common/actions';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { hash } from 'vs/base/common/hash';
interface IStatusbarEntryPriority {
/**
* The main priority of the entry that
* defines the order of appearance.
*
* May not be unique across all entries.
*/
primary: number;
/**
* The secondary priority of the entry
* is used in case the main priority
* matches another one's priority.
*
* Should be unique across all entries.
*/
secondary: number;
}
interface IPendingStatusbarEntry {
id: string;
name: string;
entry: IStatusbarEntry;
alignment: StatusbarAlignment;
priority: number;
priority: IStatusbarEntryPriority;
accessor?: IStatusbarEntryAccessor;
}
@ -56,7 +76,7 @@ interface IStatusbarViewModelEntry {
id: string;
name: string;
alignment: StatusbarAlignment;
priority: number;
priority: IStatusbarEntryPriority;
container: HTMLElement;
labelContainer: HTMLElement;
}
@ -294,14 +314,16 @@ class StatusbarViewModel extends Disposable {
this._entries.sort((entryA, entryB) => {
if (entryA.alignment === entryB.alignment) {
if (entryA.priority !== entryB.priority) {
return entryB.priority - entryA.priority; // higher priority towards the left
if (entryA.priority.primary !== entryB.priority.primary) {
return entryB.priority.primary - entryA.priority.primary; // higher priority towards the left (primary)
}
const indexA = mapEntryToIndex.get(entryA);
const indexB = mapEntryToIndex.get(entryB);
if (entryA.priority.secondary !== entryB.priority.secondary) {
return entryB.priority.secondary - entryA.priority.secondary; // higher priority towards the left (secondary)
}
return indexA! - indexB!; // otherwise maintain stable order (both values known to be in map)
// otherwise maintain stable order (both values known to be in map)
return mapEntryToIndex.get(entryA)! - mapEntryToIndex.get(entryB)!;
}
if (entryA.alignment === StatusbarAlignment.LEFT) {
@ -422,20 +444,24 @@ export class StatusbarPart extends Part implements IStatusbarService {
this._register(this.contextService.onDidChangeWorkbenchState(() => this.updateStyles()));
}
addEntry(entry: IStatusbarEntry, id: string, name: string, alignment: StatusbarAlignment, priority: number = 0): IStatusbarEntryAccessor {
addEntry(entry: IStatusbarEntry, id: string, alignment: StatusbarAlignment, primaryPriority = 0): IStatusbarEntryAccessor {
const priority: IStatusbarEntryPriority = {
primary: primaryPriority,
secondary: hash(id) // derive from identifier to accomplish uniqueness
};
// As long as we have not been created into a container yet, record all entries
// that are pending so that they can get created at a later point
if (!this.element) {
return this.doAddPendingEntry(entry, id, name, alignment, priority);
return this.doAddPendingEntry(entry, id, alignment, priority);
}
// Otherwise add to view
return this.doAddEntry(entry, id, name, alignment, priority);
return this.doAddEntry(entry, id, alignment, priority);
}
private doAddPendingEntry(entry: IStatusbarEntry, id: string, name: string, alignment: StatusbarAlignment, priority: number): IStatusbarEntryAccessor {
const pendingEntry: IPendingStatusbarEntry = { entry, id, name, alignment, priority };
private doAddPendingEntry(entry: IStatusbarEntry, id: string, alignment: StatusbarAlignment, priority: IStatusbarEntryPriority): IStatusbarEntryAccessor {
const pendingEntry: IPendingStatusbarEntry = { entry, id, alignment, priority };
this.pendingEntries.push(pendingEntry);
const accessor: IStatusbarEntryAccessor = {
@ -459,7 +485,7 @@ export class StatusbarPart extends Part implements IStatusbarService {
return accessor;
}
private doAddEntry(entry: IStatusbarEntry, id: string, name: string, alignment: StatusbarAlignment, priority: number): IStatusbarEntryAccessor {
private doAddEntry(entry: IStatusbarEntry, id: string, alignment: StatusbarAlignment, priority: IStatusbarEntryPriority): IStatusbarEntryAccessor {
// Create item
const itemContainer = this.doCreateStatusItem(id, alignment, ...coalesce([entry.showBeak ? 'has-beak' : undefined]));
@ -469,7 +495,15 @@ export class StatusbarPart extends Part implements IStatusbarService {
this.appendOneStatusbarEntry(itemContainer, alignment, priority);
// Add to view model
const viewModelEntry: IStatusbarViewModelEntry = { id, name, alignment, priority, container: itemContainer, labelContainer: item.labelContainer };
const viewModelEntry: IStatusbarViewModelEntry = new class implements IStatusbarViewModelEntry {
readonly id = id;
readonly alignment = alignment;
readonly priority = priority;
readonly container = itemContainer;
readonly labelContainer = item.labelContainer;
get name() { return item.name; }
};
const viewModelEntryDispose = this.viewModel.add(viewModelEntry);
return {
@ -553,7 +587,7 @@ export class StatusbarPart extends Part implements IStatusbarService {
while (this.pendingEntries.length) {
const pending = this.pendingEntries.shift();
if (pending) {
pending.accessor = this.addEntry(pending.entry, pending.id, pending.name, pending.alignment, pending.priority);
pending.accessor = this.addEntry(pending.entry, pending.id, pending.alignment, pending.priority.primary);
}
}
}
@ -571,7 +605,7 @@ export class StatusbarPart extends Part implements IStatusbarService {
});
}
private appendOneStatusbarEntry(itemContainer: HTMLElement, alignment: StatusbarAlignment, priority: number): void {
private appendOneStatusbarEntry(itemContainer: HTMLElement, alignment: StatusbarAlignment, priority: IStatusbarEntryPriority): void {
const entries = this.viewModel.getEntries(alignment);
if (alignment === StatusbarAlignment.RIGHT) {
@ -584,9 +618,20 @@ export class StatusbarPart extends Part implements IStatusbarService {
// and then insert the item before that one
let appended = false;
for (const entry of entries) {
// pick a priority that ideally is not the same
// by falling back to secondary priority
let existingEntryPriority = entry.priority.primary;
let newEntryPriority = priority.primary;
if (existingEntryPriority === newEntryPriority) {
existingEntryPriority = entry.priority.secondary;
newEntryPriority = priority.secondary;
}
// insert according to priority
if (
alignment === StatusbarAlignment.LEFT && entry.priority < priority ||
alignment === StatusbarAlignment.RIGHT && entry.priority > priority // reversing due to flex: row-reverse
alignment === StatusbarAlignment.LEFT && existingEntryPriority < newEntryPriority ||
alignment === StatusbarAlignment.RIGHT && existingEntryPriority > newEntryPriority // reversing due to flex: row-reverse
) {
target.insertBefore(itemContainer, entry.container);
appended = true;
@ -777,6 +822,7 @@ class StatusbarEntryItem extends Disposable {
private readonly label: StatusBarCodiconLabel;
private entry: IStatusbarEntry | undefined = undefined;
get name(): string { return assertIsDefined(this.entry).name; }
private readonly foregroundListener = this._register(new MutableDisposable());
private readonly backgroundListener = this._register(new MutableDisposable());

View file

@ -10,7 +10,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { MenuId, IMenuService, registerAction2, Action2 } from 'vs/platform/actions/common/actions';
import { IContextKeyService, ContextKeyExpr, ContextKeyEqualsExpr, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { ITreeView, ITreeViewDescriptor, IViewsRegistry, Extensions, IViewDescriptorService, ITreeItem, TreeItemCollapsibleState, ITreeViewDataProvider, TreeViewItemHandleArg, ITreeItemLabel, ViewContainer, ViewContainerLocation, ResolvableTreeItem } from 'vs/workbench/common/views';
import { ITreeView, ITreeViewDescriptor, IViewsRegistry, Extensions, IViewDescriptorService, ITreeItem, TreeItemCollapsibleState, ITreeViewDataProvider, TreeViewItemHandleArg, ITreeItemLabel, ViewContainer, ViewContainerLocation, ResolvableTreeItem, ITreeViewDragAndDropController } from 'vs/workbench/common/views';
import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IThemeService, FileThemeIcon, FolderThemeIcon, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService';
@ -159,7 +159,6 @@ export class TreeView extends Disposable implements ITreeView {
private treeContainer!: HTMLElement;
private _messageValue: string | undefined;
private _canSelectMany: boolean = false;
private _canDragAndDrop = false;
private messageElement!: HTMLDivElement;
private tree: Tree | undefined;
private treeLabels: ResourceLabels | undefined;
@ -241,6 +240,13 @@ export class TreeView extends Disposable implements ITreeView {
get viewLocation(): ViewContainerLocation {
return this.viewDescriptorService.getViewLocationById(this.id)!;
}
private _dragAndDropController: ITreeViewDragAndDropController | undefined;
get dragAndDropController(): ITreeViewDragAndDropController | undefined {
return this._dragAndDropController;
}
set dragAndDropController(dnd: ITreeViewDragAndDropController | undefined) {
this._dragAndDropController = dnd;
}
private _dataProvider: ITreeViewDataProvider | undefined;
get dataProvider(): ITreeViewDataProvider | undefined {
@ -281,12 +287,6 @@ export class TreeView extends Disposable implements ITreeView {
}
return children;
}
async setParent(nodes: ITreeItem[], parentNode: ITreeItem): Promise<void> {
if (dataProvider.setParent) {
await dataProvider.setParent(nodes, parentNode);
}
}
};
if (this._dataProvider.onDidChangeEmpty) {
this._register(this._dataProvider.onDidChangeEmpty(() => this._onDidChangeWelcomeState.fire()));
@ -339,14 +339,6 @@ export class TreeView extends Disposable implements ITreeView {
this._canSelectMany = canSelectMany;
}
get canDragAndDrop(): boolean {
return this._canDragAndDrop;
}
set canDragAndDrop(canDragAndDrop: boolean) {
this._canDragAndDrop = canDragAndDrop;
}
get hasIconForParentNode(): boolean {
return this._hasIconForParentNode;
}
@ -521,7 +513,7 @@ export class TreeView extends Disposable implements ITreeView {
return e.collapsibleState !== TreeItemCollapsibleState.Expanded;
},
multipleSelectionSupport: this.canSelectMany,
dnd: this.canDragAndDrop ? this.instantiationService.createInstance(CustomTreeViewDragAndDrop, dataSource) : undefined,
dnd: this.dragAndDropController ? this.instantiationService.createInstance(CustomTreeViewDragAndDrop, this.dragAndDropController) : undefined,
overrideStyles: {
listBackground: this.viewLocation === ViewContainerLocation.Sidebar ? SIDE_BAR_BACKGROUND : PANEL_BACKGROUND
}
@ -814,18 +806,6 @@ class TreeDataSource implements IAsyncDataSource<ITreeItem, ITreeItem> {
}
return result;
}
async setParent(elements: ITreeItem[], newParentElement: ITreeItem): Promise<void> {
if (this.treeView.dataProvider && this.treeView.dataProvider.setParent) {
try {
await this.withProgress(this.treeView.dataProvider.setParent(elements, newParentElement));
} catch (e) {
if (!(<string>e.message).startsWith('Bad progress location:')) {
throw e;
}
}
}
}
}
// todo@jrieken,sandy make this proper and contributable from extensions
@ -879,12 +859,18 @@ class TreeRenderer extends Disposable implements ITreeRenderer<ITreeItem, FuzzyS
@IThemeService private readonly themeService: IThemeService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@ILabelService private readonly labelService: ILabelService,
@IHoverService private readonly hoverService: IHoverService
@IHoverService private readonly hoverService: IHoverService,
@IOpenerService openerService: IOpenerService
) {
super();
this._hoverDelegate = {
showHover: (options: IHoverDelegateOptions): IDisposable | undefined => {
return this.hoverService.showHover(options);
return this.hoverService.showHover({
...options,
linkHandler: (url: string) => {
return openerService.open(url, { allowCommands: (!isString(options.text) && options.text.isTrusted) });
}
});
},
delay: <number>this.configurationService.getValue('workbench.hover.delay')
};
@ -1216,7 +1202,7 @@ export class CustomTreeView extends TreeView {
}
export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop<ITreeItem> {
constructor(private dataSource: TreeDataSource, @ILabelService private readonly labelService: ILabelService) { }
constructor(private dndController: ITreeViewDragAndDropController, @ILabelService private readonly labelService: ILabelService) { }
onDragOver(data: IDragAndDropData, targetElement: ITreeItem, targetIndex: number, originalEvent: DragEvent): boolean | ITreeDragOverReaction {
return { accept: true, bubble: TreeDragOverBubble.Down, autoExpand: true };
@ -1238,7 +1224,7 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop<ITreeItem> {
if (data instanceof ElementsDragAndDropData) {
const elements = data.elements;
if (targetNode) {
await this.dataSource.setParent(elements, targetNode);
await this.dndController.onDrop(elements, targetNode);
}
}
}

View file

@ -7,7 +7,7 @@ import 'vs/css!./media/paneviewlet';
import * as nls from 'vs/nls';
import { Event, Emitter } from 'vs/base/common/event';
import { foreground } from 'vs/platform/theme/common/colorRegistry';
import { attachButtonStyler, attachLinkStyler, attachProgressBarStyler } from 'vs/platform/theme/common/styler';
import { attachButtonStyler, attachProgressBarStyler } from 'vs/platform/theme/common/styler';
import { PANEL_BACKGROUND, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
import { after, append, $, trackFocus, EventType, addDisposableListener, createCSSRule, asCSSUrl } from 'vs/base/browser/dom';
import { IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle';
@ -582,10 +582,9 @@ export abstract class ViewPane extends Pane implements IView {
const link = this.instantiationService.createInstance(Link, node, {});
append(p, link.el);
disposables.add(link);
disposables.add(attachLinkStyler(link, this.themeService));
if (precondition && node.href.startsWith('command:')) {
const updateEnablement = () => link.style({ disabled: !this.contextKeyService.contextMatchesRules(precondition) });
const updateEnablement = () => link.enabled = this.contextKeyService.contextMatchesRules(precondition);
updateEnablement();
const keys = new Set();

View file

@ -328,7 +328,7 @@ export class Workbench extends Layout {
// Create Parts
[
{ id: Parts.TITLEBAR_PART, role: 'contentinfo', classes: ['titlebar'] },
{ id: Parts.BANNER_PART, role: 'alert', classes: ['banner'] },
{ id: Parts.BANNER_PART, role: 'banner', classes: ['banner'] },
{ id: Parts.ACTIVITYBAR_PART, role: 'none', classes: ['activitybar', this.state.sideBar.position === Position.LEFT ? 'left' : 'right'] }, // Use role 'none' for some parts to make screen readers less chatty #114892
{ id: Parts.SIDEBAR_PART, role: 'none', classes: ['sidebar', this.state.sideBar.position === Position.LEFT ? 'left' : 'right'] },
{ id: Parts.EDITOR_PART, role: 'main', classes: ['editor'], options: { restorePreviousState: this.state.editor.restoreEditors } },

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