Merge remote-tracking branch 'origin' into electron-18.x.y

This commit is contained in:
deepak1556 2022-06-02 17:10:31 +09:00
commit 9f56a2f166
61 changed files with 1736 additions and 985 deletions

View file

@ -23,7 +23,7 @@
"context-keys": {"assign": []},
"css-less-scss": {"assign": ["aeschli"]},
"custom-editors": {"assign": ["mjbvz"]},
"debug": {"assign": ["roblourens"]},
"debug": {"assign": ["weinand"]},
"dialogs": {"assign": ["sbatten"]},
"diff-editor": {"assign": ["alexdima"]},
"dropdown": {"assign": []},

View file

@ -3,6 +3,7 @@
// for the documentation about the extensions.json format
"recommendations": [
"dbaeumer.vscode-eslint",
"EditorConfig.EditorConfig"
"EditorConfig.EditorConfig",
"ms-vscode.vscode-selfhost-test-provider"
]
}

View file

@ -121,7 +121,16 @@ steps:
- script: |
set -e
export npm_config_arch=$(NPM_ARCH)
if [ "$NPM_ARCH" = "armv7l" ]; then
# There is no target_arch="armv7l" supported by node_gyp,
# arm versions for compilation are decided based on the CC
# macros.
# Mapping value is based on
# https://github.com/nodejs/node/blob/0903515e126c2697042d6546c6aa4b72e1a4b33e/configure.py#L49-L50
export npm_config_arch="arm"
else
export npm_config_arch=$(NPM_ARCH)
fi
if [ -z "$CC" ] || [ -z "$CXX" ]; then
# Download clang based on chromium revision used by vscode

View file

@ -9,10 +9,13 @@ const majorNodeVersion = parseInt(nodeVersion[1]);
const minorNodeVersion = parseInt(nodeVersion[2]);
const patchNodeVersion = parseInt(nodeVersion[3]);
if (majorNodeVersion < 16 || (majorNodeVersion === 16 && minorNodeVersion < 14) || majorNodeVersion >= 17) {
if (majorNodeVersion < 16 || (majorNodeVersion === 16 && minorNodeVersion < 14)) {
console.error('\033[1;31m*** Please use node.js versions >=16.14.x and <17.\033[0;0m');
err = true;
}
if (majorNodeVersion >= 17) {
console.warn('\033[1;31m*** Warning: Versions of node.js >= 17 have not been tested.\033[0;0m')
}
const path = require('path');
const fs = require('fs');

View file

@ -309,6 +309,7 @@
},
"customizations": {
"type": "object",
"description": "Tool-specific configuration. Each tool should use a JSON object subproperty with a unique name to group its customizations.",
"properties": {
"vscode": {
"type": "object",
@ -334,128 +335,180 @@
},
"codespaces": {
"type": "object",
"description": "Customizations specific to GitHub Codespaces",
"properties": {
"repositories": {
"type": "object",
"description": "Configuration relative to a GitHub repository",
"pattern": "^[a-zA-Z0-9-_.]+[.]*\/[a-zA-Z0-9-_*]+[.]*$",
"errorMessage": "Expected format: 'owner/repo' (eg: 'microsoft/vscode'). A wildcard (*) is permitted for the repo name. (eg: 'microsoft/*').",
"oneOf": [
{
"properties": {
"permissions": {
"type": "object",
"description": "Addional repository permissions. See https://aka.ms/ghcs/multi-repo-auth for more info.",
"anyOf": [
{
"actions": {
"type": "string",
"enum": [
"read",
"write"
]
},
"checks": {
"type": "string",
"enum": [
"read",
"write"
]
},
"contents": {
"type": "string",
"enum": [
"read",
"write"
]
},
"deployments": {
"type": "string",
"enum": [
"read",
"write"
]
},
"discussions": {
"type": "string",
"enum": [
"read",
"write"
]
},
"issues": {
"type": "string",
"enum": [
"read",
"write"
]
},
"packages": {
"type": "string",
"enum": [
"read"
]
},
"pages": {
"type": "string",
"enum": [
"read",
"write"
]
},
"pull_requests": {
"type": "string",
"enum": [
"read",
"write"
]
},
"repository_projects": {
"type": "string",
"enum": [
"read",
"write"
]
},
"statuses": {
"type": "string",
"enum": [
"read",
"write"
]
},
"workflows": {
"type": "string",
"enum": [
"write"
]
}
"description": "Configuration relative to the given repositories, following the format 'owner/repo'.\n A wildcard (*) is permitted for the repo name (eg: 'microsoft/*')",
"patternProperties": {
"^[a-zA-Z0-9-_.]+[.]*\/[a-zA-Z0-9-_*]+[.]*$": {
"type": "object",
"additionalProperties": true,
"oneOf": [
{
"properties": {
"permissions": {
"type": "object",
"description": "Additional repository permissions.\n See https://aka.ms/ghcs/multi-repo-auth for more info.",
"additionalProperties": true,
"anyOf": [
{
"properties": {
"actions": {
"type": "string",
"enum": [
"read",
"write"
]
}
}
},
{
"properties": {
"checks": {
"type": "string",
"enum": [
"read",
"write"
]
}
}
},
{
"properties": {
"contents": {
"type": "string",
"enum": [
"read",
"write"
]
}
}
},
{
"properties": {
"deployments": {
"type": "string",
"enum": [
"read",
"write"
]
}
}
},
{
"properties": {
"discussions": {
"type": "string",
"enum": [
"read",
"write"
]
}
}
},
{
"properties": {
"issues": {
"type": "string",
"enum": [
"read",
"write"
]
}
}
},
{
"properties": {
"packages": {
"type": "string",
"enum": [
"read"
]
}
}
},
{
"properties": {
"pages": {
"type": "string",
"enum": [
"read",
"write"
]
}
}
},
{
"properties": {
"pull_requests": {
"type": "string",
"enum": [
"read",
"write"
]
}
}
},
{
"properties": {
"repository_projects": {
"type": "string",
"enum": [
"read",
"write"
]
}
}
},
{
"properties": {
"statuses": {
"type": "string",
"enum": [
"read",
"write"
]
}
}
},
{
"properties": {
"workflows": {
"type": "string",
"enum": [
"write"
]
}
}
}
]
}
]
}
},
{
"properties": {
"permissions": {
"type": "string",
"description": "Additional repository permissions.\n See https://aka.ms/ghcs/multi-repo-auth for more info.",
"enum": [
"read-all",
"write-all"
]
}
}
}
}
},
{
"properties": {
"permissions": {
"type": "string",
"enum": [
"read-all",
"write-all"
]
}
}
]
}
]
}
}
}
}
},
"additionalProperties": {
"type": "object",
"additionalProperties": true
},
"description": "Tool-specific configuration. Each tool should use a JSON object subproperty with a unique name to group its customizations."
}
},
"additionalProperties": {
"type": "object",
"additionalProperties": true
}
}
},

View file

@ -1249,17 +1249,17 @@
},
{
"command": "git.revealFileInOS.linux",
"when": "scmProvider == git && scmResourceGroup == merge && isLinux",
"when": "scmProvider == git && scmResourceGroup == merge && resourceScheme == file && isLinux",
"group": "2_view@2"
},
{
"command": "git.revealFileInOS.mac",
"when": "scmProvider == git && scmResourceGroup == merge && isMac",
"when": "scmProvider == git && scmResourceGroup == merge && resourceScheme == file && isMac",
"group": "2_view@2"
},
{
"command": "git.revealFileInOS.windows",
"when": "scmProvider == git && scmResourceGroup == merge && isWindows",
"when": "scmProvider == git && scmResourceGroup == merge && resourceScheme == file && isWindows",
"group": "2_view@2"
},
{
@ -1304,17 +1304,17 @@
},
{
"command": "git.revealFileInOS.linux",
"when": "scmProvider == git && scmResourceGroup == index && isLinux",
"when": "scmProvider == git && scmResourceGroup == index && resourceScheme == file && isLinux",
"group": "2_view@2"
},
{
"command": "git.revealFileInOS.mac",
"when": "scmProvider == git && scmResourceGroup == index && isMac",
"when": "scmProvider == git && scmResourceGroup == index && resourceScheme == file && isMac",
"group": "2_view@2"
},
{
"command": "git.revealFileInOS.windows",
"when": "scmProvider == git && scmResourceGroup == index && isWindows",
"when": "scmProvider == git && scmResourceGroup == index && resourceScheme == file && isWindows",
"group": "2_view@2"
},
{
@ -1384,17 +1384,17 @@
},
{
"command": "git.revealFileInOS.linux",
"when": "scmProvider == git && scmResourceGroup == workingTree && isLinux",
"when": "scmProvider == git && scmResourceGroup == workingTree && resourceScheme == file && isLinux",
"group": "2_view@2"
},
{
"command": "git.revealFileInOS.mac",
"when": "scmProvider == git && scmResourceGroup == workingTree && isMac",
"when": "scmProvider == git && scmResourceGroup == workingTree && resourceScheme == file && isMac",
"group": "2_view@2"
},
{
"command": "git.revealFileInOS.windows",
"when": "scmProvider == git && scmResourceGroup == workingTree && isWindows",
"when": "scmProvider == git && scmResourceGroup == workingTree && resourceScheme == file && isWindows",
"group": "2_view@2"
},
{
@ -2450,7 +2450,10 @@
"type": "boolean",
"default": false,
"markdownDescription": "%config.experimental.mergeEditor%",
"scope": "window"
"scope": "window",
"tags": [
"experimental"
]
}
}
},
@ -2461,7 +2464,7 @@
"defaults": {
"light": "#587c0c",
"dark": "#81b88b",
"highContrast": "#1b5225",
"highContrast": "#a1e3ad",
"highContrastLight": "#374e06"
}
},

View file

@ -290,7 +290,7 @@ export interface GitExtension {
/**
* Returns a specific API version.
*
* Throws error if git extension is disabled. You can listed to the
* Throws error if git extension is disabled. You can listen to the
* [GitExtension.onDidChangeEnablement](#GitExtension.onDidChangeEnablement) event
* to know when the extension becomes enabled/disabled.
*

View file

@ -153,8 +153,7 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid
const scopesSeen = new Set<string>();
const sessionPromises = sessionData.map(async (session: SessionData) => {
// For GitHub scope list, order doesn't matter so we immediately sort the scopes
const sortedScopes = session.scopes.sort();
const scopesStr = sortedScopes.join(' ');
const scopesStr = [...session.scopes].sort().join(' ');
if (scopesSeen.has(scopesStr)) {
return undefined;
}
@ -181,7 +180,9 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid
: userInfo?.accountName ?? '<unknown>',
id: session.account?.id ?? userInfo?.id ?? '<unknown>'
},
scopes: sortedScopes,
// we set this to session.scopes to maintain the original order of the scopes requested
// by the extension that called getSession()
scopes: session.scopes,
accessToken: session.accessToken
};
});
@ -208,8 +209,9 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid
public async createSession(scopes: string[]): Promise<vscode.AuthenticationSession> {
try {
// For GitHub scope list, order doesn't matter so we immediately sort the scopes
const sortedScopes = scopes.sort();
// For GitHub scope list, order doesn't matter so we use a sorted scope to determine
// if we've got a session already.
const sortedScopes = [...scopes].sort();
/* __GDPR__
"login" : {
@ -219,13 +221,13 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid
}
*/
this._telemetryReporter?.sendTelemetryEvent('login', {
scopes: JSON.stringify(sortedScopes),
scopes: JSON.stringify(scopes),
});
const scopeString = sortedScopes.join(' ');
const token = await this._githubServer.login(scopeString);
const session = await this.tokenToSession(token, sortedScopes);
const session = await this.tokenToSession(token, scopes);
this.afterSessionLoad(session);
const sessions = await this._sessionsPromise;

View file

@ -24,7 +24,7 @@ function getAgent(url: string | undefined = process.env.HTTPS_PROXY): Agent {
}
}
const scopes = ['repo', 'workflow'];
const scopes = ['repo', 'workflow', 'user:email', 'read:user'];
export async function getSession(): Promise<AuthenticationSession> {
return await authentication.getSession('github', scopes, { createIfNone: true });

View file

@ -220,7 +220,7 @@ export class GithubPushErrorHandler implements PushErrorHandler {
return false;
}
const match = /^(?:https:\/\/github\.com\/|git@github\.com:)([^/]+)\/([^/.]+)(?:\.git)?$/i.exec(remoteUrl);
const match = /^(?:https:\/\/github\.com\/|git@github\.com:)([^\/]+)\/([^\/.]+)/i.exec(remoteUrl);
if (!match) {
return false;
}

View file

@ -417,15 +417,21 @@
},
"markdown.experimental.editor.pasteLinks.enabled": {
"type": "boolean",
"default": false,
"scope": "resource",
"markdownDescription": "%configuration.markdown.editor.pasteLinks.enabled%",
"scope": "resource"
"default": false,
"tags": [
"experimental"
]
},
"markdown.experimental.validate.enabled": {
"type": "boolean",
"scope": "resource",
"description": "%configuration.markdown.experimental.validate.enabled.description%",
"default": false
"default": false,
"tags": [
"experimental"
]
},
"markdown.experimental.validate.referenceLinks.enabled": {
"type": "string",
@ -436,6 +442,9 @@
"ignore",
"warning",
"error"
],
"tags": [
"experimental"
]
},
"markdown.experimental.validate.headerLinks.enabled": {
@ -447,6 +456,9 @@
"ignore",
"warning",
"error"
],
"tags": [
"experimental"
]
},
"markdown.experimental.validate.fileLinks.enabled": {
@ -458,6 +470,9 @@
"ignore",
"warning",
"error"
],
"tags": [
"experimental"
]
},
"markdown.experimental.validate.ignoreLinks": {
@ -466,7 +481,10 @@
"markdownDescription": "%configuration.markdown.experimental.validate.ignoreLinks.description%",
"items": {
"type": "string"
}
},
"tags": [
"experimental"
]
}
}
},

View file

@ -448,7 +448,7 @@ export class DiagnosticComputer {
if (link.href.kind === 'reference' && !definitionSet.lookup(link.href.ref)) {
yield new vscode.Diagnostic(
link.source.hrefRange,
localize('invalidReferenceLink', 'No link reference found: \'{0}\'', link.href.ref),
localize('invalidReferenceLink', 'No link definition found: \'{0}\'', link.href.ref),
severity);
}
}

View file

@ -271,9 +271,11 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
case 'reference': {
const def = definitionSet.lookup(link.href.ref);
if (def) {
return new vscode.DocumentLink(
const documentLink = new vscode.DocumentLink(
link.source.hrefRange,
vscode.Uri.parse(`command:_markdown.moveCursorToPosition?${encodeURIComponent(JSON.stringify([def.source.hrefRange.start.line, def.source.hrefRange.start.character]))}`));
documentLink.tooltip = localize('documentLink.referenceTooltip', 'Go to link definition');
return documentLink;
} else {
return undefined;
}
@ -353,6 +355,12 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider {
reference = match[5];
const offset = ((match.index ?? 0) + match[1].length) + 1;
linkStart = document.positionAt(offset);
const line = document.lineAt(linkStart.line);
// See if link looks like a checkbox
const checkboxMatch = line.text.match(/^\s*[\-\*]\s*\[x\]/i);
if (checkboxMatch && linkStart.character <= checkboxMatch[0].length) {
continue;
}
linkEnd = document.positionAt(offset + reference.length);
} else {
continue;

View file

@ -268,4 +268,17 @@ suite('markdown: Diagnostics', () => {
const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken);
assert.deepStrictEqual(diagnostics.length, 0);
});
test('Should not detect checkboxes as invalid links', async () => {
const doc1 = new InMemoryDocument(workspacePath('doc1.md'), joinLines(
`- [x]`,
`- [X]`,
`- [ ]`,
));
const contents = new InMemoryWorkspaceMarkdownDocuments([doc1]);
const manager = createDiagnosticsManager(contents, new MemoryDiagnosticConfiguration(true, ['/doc2.md']));
const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken);
assert.deepStrictEqual(diagnostics.length, 0);
});
});

View file

@ -313,4 +313,36 @@ suite('markdown.DocumentLinkProvider', () => {
));
assert.strictEqual(links.length, 0);
});
test('Should not mark checkboxes as links', async () => {
const links = await getLinksForFile(joinLines(
'- [x]',
'- [X]',
'- [ ]',
'* [x]',
'* [X]',
'* [ ]',
``,
`[x]: http://example.com`
));
assert.strictEqual(links.length, 1);
assertRangeEqual(links[0].range, new vscode.Range(7, 5, 7, 23));
});
test('Should still find links on line with checkbox', async () => {
const links = await getLinksForFile(joinLines(
'- [x] [x]',
'- [X] [x]',
'- [] [x]',
``,
`[x]: http://example.com`
));
assert.strictEqual(links.length, 4);
assertRangeEqual(links[0].range, new vscode.Range(0, 7, 0, 8));
assertRangeEqual(links[1].range, new vscode.Range(1, 7, 1, 8));
assertRangeEqual(links[2].range, new vscode.Range(2, 6, 2, 7));
assertRangeEqual(links[3].range, new vscode.Range(4, 5, 4, 23));
});
});

View file

@ -577,5 +577,19 @@ suite('markdown: find all references', () => {
{ uri: docUri, line: 2 },
);
});
test('Should not consider checkboxes as reference links', async () => {
const docUri = workspacePath('doc.md');
const doc = new InMemoryDocument(docUri, joinLines(
`- [x]`,
`- [X]`,
`- [ ]`,
``,
`[x]: https://example.com`
));
const refs = await getReferences(doc, new vscode.Position(0, 4), new InMemoryWorkspaceMarkdownDocuments([doc]));
assert.strictEqual(refs?.length!, 0);
});
});
});

View file

@ -1103,7 +1103,10 @@
"type": "boolean",
"default": false,
"description": "%configuration.tsserver.experimental.enableProjectDiagnostics%",
"scope": "window"
"scope": "window",
"tags": [
"experimental"
]
},
"typescript.tsserver.watchOptions": {
"type": "object",

View file

@ -1,7 +1,7 @@
{
"name": "code-oss-dev",
"version": "1.68.0",
"distro": "cc3412556bc8e8717bfd449c5e767fd16dbc22c7",
"distro": "1af1d9e4f6e2609bd0eb3dea3786ef1571338986",
"author": {
"name": "Microsoft Corporation"
},

View file

@ -98,6 +98,7 @@ export class Toggle extends Widget {
readonly onKeyDown: Event<IKeyboardEvent> = this._onKeyDown.event;
private readonly _opts: IToggleOpts;
private _icon: CSSIcon | undefined;
readonly domNode: HTMLElement;
private _checked: boolean;
@ -110,7 +111,8 @@ export class Toggle extends Widget {
const classes = ['monaco-custom-toggle'];
if (this._opts.icon) {
classes.push(...CSSIcon.asClassNameArray(this._opts.icon));
this._icon = this._opts.icon;
classes.push(...CSSIcon.asClassNameArray(this._icon));
}
if (this._opts.actionClassName) {
classes.push(...this._opts.actionClassName.split(' '));
@ -174,6 +176,16 @@ export class Toggle extends Widget {
this.applyStyles();
}
setIcon(icon: CSSIcon | undefined): void {
if (this._icon) {
this.domNode.classList.remove(...CSSIcon.asClassNameArray(this._icon));
}
this._icon = icon;
if (this._icon) {
this.domNode.classList.add(...CSSIcon.asClassNameArray(this._icon));
}
}
width(): number {
return 2 /*margin left*/ + 2 /*border*/ + 2 /*padding*/ + 16 /* icon width */;
}

View file

@ -991,7 +991,9 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render
} else { // must be CharCode.Space
charWidth = 1;
sb.write1(0x200C); // ZERO WIDTH NON-JOINER
sb.write1(renderSpaceCharCode); // &middot; or word separator middle dot
sb.write1(0x200C); // ZERO WIDTH NON-JOINER
}
charOffsetInPart += charWidth;

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { distinct } from 'vs/base/common/arrays';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { CancellationToken } from 'vs/base/common/cancellation';
import { createStringDataTransferItem, VSDataTransfer } from 'vs/base/common/dataTransfer';
import { Disposable } from 'vs/base/common/lifecycle';
import { Mimes } from 'vs/base/common/mime';
@ -19,6 +19,7 @@ import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { DocumentOnDropEditProvider, SnippetTextEdit } from 'vs/editor/common/languages';
import { ITextModel } from 'vs/editor/common/model';
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
import { CodeEditorStateFlag, EditorStateCancellationTokenSource } from 'vs/editor/contrib/editorState/browser/editorState';
import { performSnippetEdit } from 'vs/editor/contrib/snippet/browser/snippetController2';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { extractEditorsDropData } from 'vs/platform/dnd/browser/dnd';
@ -76,21 +77,22 @@ export class DropIntoEditorController extends Disposable implements IEditorContr
return;
}
const cts = new CancellationTokenSource();
editor.onDidDispose(() => cts.cancel());
model.onDidChangeContent(() => cts.cancel());
const tokenSource = new EditorStateCancellationTokenSource(editor, CodeEditorStateFlag.Value);
try {
const providers = this._languageFeaturesService.documentOnDropEditProvider.ordered(model);
for (const provider of providers) {
const edit = await provider.provideDocumentOnDropEdits(model, position, ourDataTransfer, tokenSource.token);
if (tokenSource.token.isCancellationRequested || editor.getModel().getVersionId() !== modelVersionNow) {
return;
}
const providers = this._languageFeaturesService.documentOnDropEditProvider.ordered(model);
for (const provider of providers) {
const edit = await provider.provideDocumentOnDropEdits(model, position, ourDataTransfer, cts.token);
if (cts.token.isCancellationRequested || editor.getModel().getVersionId() !== modelVersionNow) {
return;
}
if (edit) {
performSnippetEdit(editor, edit);
return;
if (edit) {
performSnippetEdit(editor, edit);
return;
}
}
} finally {
tokenSource.dispose();
}
}
@ -100,15 +102,13 @@ export class DropIntoEditorController extends Disposable implements IEditorContr
}
const textEditorDataTransfer = toVSDataTransfer(dragEvent.dataTransfer);
if (!textEditorDataTransfer.has(Mimes.uriList)) {
const editorData = (await this._instantiationService.invokeFunction(extractEditorsDropData, dragEvent))
.filter(input => input.resource)
.map(input => input.resource!.toString());
const editorData = (await this._instantiationService.invokeFunction(extractEditorsDropData, dragEvent))
.filter(input => input.resource)
.map(input => input.resource!.toString());
if (editorData.length) {
const str = distinct(editorData).join('\n');
textEditorDataTransfer.replace(Mimes.uriList, createStringDataTransferItem(str));
}
if (editorData.length) {
const str = distinct(editorData).join('\n');
textEditorDataTransfer.replace(Mimes.uriList, createStringDataTransferItem(str));
}
return textEditorDataTransfer;

View file

@ -201,7 +201,7 @@ suite('viewLineRenderer.renderLine', () => {
]);
const expectedOutput = [
'<span class="mtkz" style="width:40px">\u2192\u00a0\u00a0\u00a0</span>',
'<span class="mtkz" style="width:40px">\u00b7\u00b7\u00b7\u00b7</span>',
'<span class="mtkz" style="width:40px">\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c</span>',
'<span class="mtk2">export</span>',
'<span class="mtk3">\u00a0</span>',
'<span class="mtk4">class</span>',
@ -212,8 +212,8 @@ suite('viewLineRenderer.renderLine', () => {
'<span class="mtk9">\u00a0</span>',
'<span class="mtk10">//\u00a0</span>',
'<span class="mtk11">http://test.com</span>',
'<span class="mtkz" style="width:20px">\u00b7\u00b7</span>',
'<span class="mtkz" style="width:30px">\u00b7\u00b7\u00b7</span>'
'<span class="mtkz" style="width:20px">\u200c\u00b7\u200c\u200c\u00b7\u200c</span>',
'<span class="mtkz" style="width:30px">\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c</span>'
].join('');
const info: CharacterMappingInfo[] = [
@ -565,7 +565,7 @@ suite('viewLineRenderer.renderLine', () => {
]);
const expectedOutput = [
'<span class="mtkw">\u00b7\u00b7\u00b7\u00b7</span>',
'<span class="mtkw">\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c</span>',
'<span class="mtk2">[</span>',
'<span style="unicode-bidi:isolate" class="mtk3">"🖨️\u00a0چاپ\u00a0فاکتور"</span>',
'<span class="mtk2">,</span>',
@ -1108,10 +1108,10 @@ suite('viewLineRenderer.renderLine 2', () => {
null,
[
'<span>',
'<span class="mtkz" style="width:40px">\u00b7\u00b7\u00b7\u00b7</span>',
'<span class="mtkz" style="width:40px">\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c</span>',
'<span class="mtk2">He</span>',
'<span class="mtk3">llo\u00a0world!</span>',
'<span class="mtkz" style="width:40px">\u00b7\u00b7\u00b7\u00b7</span>',
'<span class="mtkz" style="width:40px">\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c</span>',
'</span>',
].join('')
);
@ -1130,12 +1130,12 @@ suite('viewLineRenderer.renderLine 2', () => {
null,
[
'<span>',
'<span class="mtkz" style="width:40px">\u00b7\u00b7\u00b7\u00b7</span>',
'<span class="mtkz" style="width:40px">\u00b7\u00b7\u00b7\u00b7</span>',
'<span class="mtkz" style="width:40px">\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c</span>',
'<span class="mtkz" style="width:40px">\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c</span>',
'<span class="mtk2">He</span>',
'<span class="mtk3">llo\u00a0world!</span>',
'<span class="mtkz" style="width:40px">\u00b7\u00b7\u00b7\u00b7</span>',
'<span class="mtkz" style="width:40px">\u00b7\u00b7\u00b7\u00b7</span>',
'<span class="mtkz" style="width:40px">\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c</span>',
'<span class="mtkz" style="width:40px">\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c</span>',
'</span>',
].join('')
);
@ -1177,15 +1177,15 @@ suite('viewLineRenderer.renderLine 2', () => {
null,
[
'<span>',
'<span class="mtkz" style="width:40px">\u00b7\u00b7\u2192\u00a0</span>',
'<span class="mtkz" style="width:40px">\u200c\u00b7\u200c\u200c\u00b7\u200c\u2192\u00a0</span>',
'<span class="mtkz" style="width:40px">\u2192\u00a0\u00a0\u00a0</span>',
'<span class="mtkz" style="width:20px">\u00b7\u00b7</span>',
'<span class="mtkz" style="width:20px">\u200c\u00b7\u200c\u200c\u00b7\u200c</span>',
'<span class="mtk2">He</span>',
'<span class="mtk3">llo\u00a0world!</span>',
'<span class="mtkz" style="width:20px">\u00b7\uffeb</span>',
'<span class="mtkz" style="width:40px">\u00b7\u00b7\u2192\u00a0</span>',
'<span class="mtkz" style="width:40px">\u00b7\u00b7\u00b7\uffeb</span>',
'<span class="mtkz" style="width:40px">\u00b7\u00b7\u00b7\u00b7</span>',
'<span class="mtkz" style="width:20px">\u200c\u00b7\u200c\uffeb</span>',
'<span class="mtkz" style="width:40px">\u200c\u00b7\u200c\u200c\u00b7\u200c\u2192\u00a0</span>',
'<span class="mtkz" style="width:40px">\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\uffeb</span>',
'<span class="mtkz" style="width:40px">\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c</span>',
'</span>',
].join('')
);
@ -1206,13 +1206,13 @@ suite('viewLineRenderer.renderLine 2', () => {
[
'<span>',
'<span class="">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0</span>',
'<span class="mtkz" style="width:20px">\u00b7\u00b7</span>',
'<span class="mtkz" style="width:20px">\u200c\u00b7\u200c\u200c\u00b7\u200c</span>',
'<span class="mtk2">He</span>',
'<span class="mtk3">llo\u00a0world!</span>',
'<span class="mtkz" style="width:20px">\u00b7\uffeb</span>',
'<span class="mtkz" style="width:40px">\u00b7\u00b7\u2192\u00a0</span>',
'<span class="mtkz" style="width:40px">\u00b7\u00b7\u00b7\uffeb</span>',
'<span class="mtkz" style="width:40px">\u00b7\u00b7\u00b7\u00b7</span>',
'<span class="mtkz" style="width:20px">\u200c\u00b7\u200c\uffeb</span>',
'<span class="mtkz" style="width:40px">\u200c\u00b7\u200c\u200c\u00b7\u200c\u2192\u00a0</span>',
'<span class="mtkz" style="width:40px">\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\uffeb</span>',
'<span class="mtkz" style="width:40px">\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c</span>',
'</span>',
].join('')
);
@ -1233,10 +1233,10 @@ suite('viewLineRenderer.renderLine 2', () => {
[
'<span>',
'<span class="">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0</span>',
'<span class="mtkw">\u00b7\u00b7</span>',
'<span class="mtkw">\u200c\u00b7\u200c\u200c\u00b7\u200c</span>',
'<span class="mtk2">He</span>',
'<span class="mtk3">llo\u00a0world!</span>',
'<span class="mtkw">\u00b7\uffeb\u00b7\u00b7\u2192\u00a0\u00b7\u00b7\u00b7\uffeb\u00b7\u00b7\u00b7\u00b7</span>',
'<span class="mtkw">\u200c\u00b7\u200c\uffeb\u200c\u00b7\u200c\u200c\u00b7\u200c\u2192\u00a0\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\uffeb\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c</span>',
'</span>',
].join('')
);
@ -1257,11 +1257,11 @@ suite('viewLineRenderer.renderLine 2', () => {
[
'<span>',
'<span class="mtk1">it</span>',
'<span class="mtkz" style="width:20px">\u00b7\u00b7</span>',
'<span class="mtkz" style="width:20px">\u200c\u00b7\u200c\u200c\u00b7\u200c</span>',
'<span class="mtk1">it</span>',
'<span class="mtk2">\u00a0</span>',
'<span class="mtk3">it</span>',
'<span class="mtkz" style="width:20px">\u00b7\u00b7</span>',
'<span class="mtkz" style="width:20px">\u200c\u00b7\u200c\u200c\u00b7\u200c</span>',
'<span class="mtk3">it</span>',
'</span>',
].join('')
@ -1282,10 +1282,10 @@ suite('viewLineRenderer.renderLine 2', () => {
null,
[
'<span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'<span class="mtkz" style="width:10px">\u200c\u00b7\u200c</span>',
'<span class="mtk0">Hel</span>',
'<span class="mtk1">lo</span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'<span class="mtkz" style="width:10px">\u200c\u00b7\u200c</span>',
'<span class="mtk2">world!</span>',
'<span class="mtkz" style="width:30px">\u2192\u00a0\u00a0</span>',
'</span>',
@ -1329,10 +1329,10 @@ suite('viewLineRenderer.renderLine 2', () => {
[new LineRange(0, 14)],
[
'<span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'<span class="mtkz" style="width:10px">\u200c\u00b7\u200c</span>',
'<span class="mtk0">Hel</span>',
'<span class="mtk1">lo</span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'<span class="mtkz" style="width:10px">\u200c\u00b7\u200c</span>',
'<span class="mtk2">world!</span>',
'<span class="mtkz" style="width:30px">\u2192\u00a0\u00a0</span>',
'</span>',
@ -1354,7 +1354,7 @@ suite('viewLineRenderer.renderLine 2', () => {
[new LineRange(0, 5)],
[
'<span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'<span class="mtkz" style="width:10px">\u200c\u00b7\u200c</span>',
'<span class="mtk0">Hel</span>',
'<span class="mtk1">lo</span>',
'<span class="mtk2">\u00a0world!\u00a0\u00a0\u00a0</span>',
@ -1378,7 +1378,7 @@ suite('viewLineRenderer.renderLine 2', () => {
[new LineRange(0, 5), new LineRange(9, 14)],
[
'<span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'<span class="mtkz" style="width:10px">\u200c\u00b7\u200c</span>',
'<span class="mtk0">Hel</span>',
'<span class="mtk1">lo</span>',
'<span class="mtk2">\u00a0world!</span>',
@ -1403,7 +1403,7 @@ suite('viewLineRenderer.renderLine 2', () => {
[new LineRange(9, 14), new LineRange(0, 5)],
[
'<span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'<span class="mtkz" style="width:10px">\u200c\u00b7\u200c</span>',
'<span class="mtk0">Hel</span>',
'<span class="mtk1">lo</span>',
'<span class="mtk2">\u00a0world!</span>',
@ -1425,9 +1425,9 @@ suite('viewLineRenderer.renderLine 2', () => {
[new LineRange(0, 1), new LineRange(1, 2), new LineRange(2, 3)],
[
'<span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'<span class="mtkz" style="width:10px">\u200c\u00b7\u200c</span>',
'<span class="mtk0">*</span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'<span class="mtkz" style="width:10px">\u200c\u00b7\u200c</span>',
'<span class="mtk0">S</span>',
'</span>',
].join('')
@ -1473,7 +1473,7 @@ suite('viewLineRenderer.renderLine 2', () => {
'<span class="mtk0">\u00a0Hel</span>',
'<span class="mtk1">lo</span>',
'<span class="mtk2">\u00a0world!</span>',
'<span class="mtkz" style="width:30px">\u00b7\u2192\u00a0</span>',
'<span class="mtkz" style="width:30px">\u200c\u00b7\u200c\u2192\u00a0</span>',
'</span>',
].join('')
);
@ -1496,8 +1496,8 @@ suite('viewLineRenderer.renderLine 2', () => {
'<span class="mtk1">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0</span>',
'<span class="mtk2">He</span>',
'<span class="mtk3">llo\u00a0world!</span>',
'<span class="mtkz" style="width:40px">\u00b7\u00b7\u00b7\u00b7</span>',
'<span class="mtkz" style="width:40px">\u00b7\u00b7\u00b7\u00b7</span>',
'<span class="mtkz" style="width:40px">\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c</span>',
'<span class="mtkz" style="width:40px">\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c</span>',
'</span>',
].join('')
);
@ -1516,8 +1516,8 @@ suite('viewLineRenderer.renderLine 2', () => {
null,
[
'<span>',
'<span class="mtkz" style="width:40px">\u00b7\u2192\u00a0\u00a0</span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'<span class="mtkz" style="width:40px">\u200c\u00b7\u200c\u2192\u00a0\u00a0</span>',
'<span class="mtkz" style="width:10px">\u200c\u00b7\u200c</span>',
'</span>',
].join('')
);
@ -1925,9 +1925,9 @@ suite('viewLineRenderer.renderLine 2', () => {
const expected = [
'<span>',
'<span class="mtk3">asd</span>',
'<span class="mtkw">\u00b7</span>',
'<span class="mtkw">\u200c\u00b7\u200c</span>',
'<span class="mtk3">=</span>',
'<span class="mtkw">\u00b7</span>',
'<span class="mtkw">\u200c\u00b7\u200c</span>',
'<span class="mtk3">"擦"</span>',
'<span class="mtkw">\u2192\u00a0\u2192\u00a0\u00a0\u00a0</span>',
'<span class="mtk3">#asd</span>',
@ -2184,40 +2184,40 @@ suite('viewLineRenderer.renderLine 2', () => {
const expected = [
'<span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'<span class="mtkz" style="width:10px">\u200c\u00b7\u200c</span>',
'<span class="mtkz" style="width:10px">\u200c\u00b7\u200c</span>',
'<span class="mtkz" style="width:10px">\u200c\u00b7\u200c</span>',
'<span class="mtkz" style="width:10px">\u200c\u00b7\u200c</span>',
'<span class="mtkz" style="width:10px">\u200c\u00b7\u200c</span>',
'<span class="mtkz" style="width:10px">\u200c\u00b7\u200c</span>',
'<span class="mtkz" style="width:10px">\u200c\u00b7\u200c</span>',
'<span class="mtkz" style="width:10px">\u200c\u00b7\u200c</span>',
'<span class="mtkz" style="width:10px">\u200c\u00b7\u200c</span>',
'<span class="mtkz" style="width:10px">\u200c\u00b7\u200c</span>',
'<span class="mtkz" style="width:10px">\u200c\u00b7\u200c</span>',
'<span class="mtkz" style="width:10px">\u200c\u00b7\u200c</span>',
'<span class="mtkz" style="width:10px">\u200c\u00b7\u200c</span>',
'<span class="mtkz" style="width:10px">\u200c\u00b7\u200c</span>',
'<span class="mtkz" style="width:10px">\u200c\u00b7\u200c</span>',
'<span class="mtkz" style="width:10px">\u200c\u00b7\u200c</span>',
'<span class="mtkz" style="width:10px">\u200c\u00b7\u200c</span>',
'<span class="mtkz" style="width:10px">\u200c\u00b7\u200c</span>',
'<span class="mtkz" style="width:10px">\u200c\u00b7\u200c</span>',
'<span class="mtkz" style="width:10px">\u200c\u00b7\u200c</span>',
'<span class="mtk15">else</span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'<span class="mtkz" style="width:10px">\u200c\u00b7\u200c</span>',
'<span class="mtk15">if</span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'<span class="mtkz" style="width:10px">\u200c\u00b7\u200c</span>',
'<span class="mtk1">(</span>',
'<span class="mtk16">$s</span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'<span class="mtkz" style="width:10px">\u200c\u00b7\u200c</span>',
'<span class="mtk1">=</span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'<span class="mtkz" style="width:10px">\u200c\u00b7\u200c</span>',
'<span class="mtk6">08</span>',
'<span class="mtk1">)</span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'<span class="mtkz" style="width:10px">\u200c\u00b7\u200c</span>',
'<span class="mtk15">then</span>',
'<span class="mtkz" style="width:10px">\u00b7</span>',
'<span class="mtkz" style="width:10px">\u200c\u00b7\u200c</span>',
'<span class="mtk11">\'\\b\'</span>',
'</span>'
].join('');
@ -2318,7 +2318,7 @@ suite('viewLineRenderer.renderLine 2', () => {
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 class="mtkw">\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c\u200c\u00b7\u200c</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('');

View file

@ -1218,7 +1218,6 @@ export interface IFilesConfiguration {
autoSaveDelay: number;
eol: string;
enableTrash: boolean;
excludeGitIgnore: boolean;
hotExit: string;
saveConflictResolution: 'askUser' | 'overwriteFileOnDisk';
};

View file

@ -213,10 +213,6 @@ export class NativeHostMainService extends Disposable implements INativeHostMain
}
async updateTitleBarOverlay(windowId: number | undefined, backgroundColor: string, foregroundColor: string): Promise<void> {
if (!isWindows || !this.environmentMainService.isBuilt) {
return; // Non-OSS, Windows only
}
const window = this.windowById(windowId);
if (window?.win) {
window.win.setTitleBarOverlay({

View file

@ -5,12 +5,13 @@
import { IStringDictionary } from 'vs/base/common/collections';
import { PerformanceMark } from 'vs/base/common/performance';
import { isLinux, isMacintosh, isNative, isWeb } from 'vs/base/common/platform';
import { isLinux, isMacintosh, isNative, isWeb, isWindows } from 'vs/base/common/platform';
import { URI, UriComponents } from 'vs/base/common/uri';
import { ISandboxConfiguration } from 'vs/base/parts/sandbox/common/sandboxTypes';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IEditorOptions } from 'vs/platform/editor/common/editor';
import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { FileType } from 'vs/platform/files/common/files';
import { LogLevel } from 'vs/platform/log/common/log';
import { PolicyDefinition, PolicyValue } from 'vs/platform/policy/common/policy';
@ -170,6 +171,19 @@ export function getTitleBarStyle(configurationService: IConfigurationService): '
return isLinux ? 'native' : 'custom'; // default to custom on all macOS and Windows
}
export function useWindowControlsOverlay(configurationService: IConfigurationService, environmentService: IEnvironmentService): boolean {
// Window Controls Overlay are only configurable on Windows
if (!isWindows || isWeb || !environmentService.isBuilt) {
return false;
}
if (getTitleBarStyle(configurationService) === 'native') {
return false;
}
return configurationService.getValue<boolean>('window.experimental.windowControlsOverlay.enabled');
}
export interface IPath<T = IEditorOptions> extends IPathData<T> {
/**

View file

@ -33,7 +33,7 @@ import { IGlobalStorageMainService, IStorageMainService } from 'vs/platform/stor
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainService';
import { getMenuBarVisibility, getTitleBarStyle, IFolderToOpen, INativeWindowConfiguration, IWindowSettings, IWorkspaceToOpen, MenuBarVisibility, WindowMinimumSize, zoomLevelToZoomFactor } from 'vs/platform/window/common/window';
import { getMenuBarVisibility, getTitleBarStyle, IFolderToOpen, INativeWindowConfiguration, IWindowSettings, IWorkspaceToOpen, MenuBarVisibility, useWindowControlsOverlay, WindowMinimumSize, zoomLevelToZoomFactor } from 'vs/platform/window/common/window';
import { IWindowsMainService, OpenContext } from 'vs/platform/windows/electron-main/windows';
import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace';
import { IWorkspacesManagementMainService } from 'vs/platform/workspaces/electron-main/workspacesManagementMainService';
@ -249,7 +249,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
options.frame = false;
}
if (isWindows && this.environmentMainService.isBuilt) {
if (useWindowControlsOverlay(this.configurationService, this.environmentMainService)) {
// This logic will not perfectly guess the right colors to use on initialization,
// but prefer to keep things simple as it is temporary and not noticeable
const titleBarColor = this.themeMainService.getWindowSplash()?.colorInfo.titleBarBackground ?? this.themeMainService.getBackgroundColor();

View file

@ -192,7 +192,7 @@ export class RemoteExtensionHostAgentServer extends Disposable implements IServe
}
}
if (req.headers['upgrade'] !== 'websocket') {
if (req.headers['upgrade'] === undefined || req.headers['upgrade'].toLowerCase() !== 'websocket') {
socket.end('HTTP/1.1 400 Bad Request');
return;
}

View file

@ -872,7 +872,16 @@ export class CommentController implements IEditorContribution {
let pendingComment = zone.getPendingComment();
let providerCacheStore = this._pendingCommentCache[zone.owner];
if (pendingComment) {
let lastCommentBody;
if (zone.commentThread.comments && zone.commentThread.comments.length) {
const lastComment = zone.commentThread.comments[zone.commentThread.comments.length - 1];
if (typeof lastComment.body === 'string') {
lastCommentBody = lastComment.body;
} else {
lastCommentBody = lastComment.body.value;
}
}
if (pendingComment && (pendingComment !== lastCommentBody)) {
if (!providerCacheStore) {
this._pendingCommentCache[zone.owner] = {};
}

View file

@ -161,7 +161,7 @@ export function prepareCommand(shell: string, args: string[], cwd?: string, env?
case ShellType.bash: {
quote = (s: string) => {
s = s.replace(/(["'\\\$!><#()\[\]*&^| ;])/g, '\\$1');
s = s.replace(/(["'\\\$!><#()\[\]*&^| ;{}`])/g, '\\$1');
return s.length === 0 ? `""` : s;
};

View file

@ -11,7 +11,7 @@ suite('Debug - prepareCommand', () => {
test('bash', () => {
assert.strictEqual(
prepareCommand('bash', ['{$} (']).trim(),
'{\\$}\\ \\(');
'\\{\\$\\}\\ \\(');
assert.strictEqual(
prepareCommand('bash', ['hello', 'world', '--flag=true']).trim(),
'hello world --flag=true');

View file

@ -29,7 +29,7 @@ import {
UpdateAction, ReloadAction, EnableDropDownAction, DisableDropDownAction, ExtensionStatusLabelAction, SetFileIconThemeAction, SetColorThemeAction,
RemoteInstallAction, ExtensionStatusAction, LocalInstallAction, ToggleSyncExtensionAction, SetProductIconThemeAction,
ActionWithDropDownAction, InstallDropdownAction, InstallingLabelAction, UninstallAction, ExtensionActionWithDropdownActionViewItem, ExtensionDropDownAction,
InstallAnotherVersionAction, ExtensionEditorManageExtensionAction, WebInstallAction, SwitchToPreReleaseVersionAction, SwitchToReleasedVersionAction, MigrateDeprecatedExtension, SponsorExtensionAction, SponsorExtensionActionViewItem
InstallAnotherVersionAction, ExtensionEditorManageExtensionAction, WebInstallAction, SwitchToPreReleaseVersionAction, SwitchToReleasedVersionAction, MigrateDeprecatedExtensionAction, SponsorExtensionAction, SponsorExtensionActionViewItem
} from 'vs/workbench/contrib/extensions/browser/extensionsActions';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
@ -338,7 +338,7 @@ export class ExtensionEditor extends EditorPane {
this.instantiationService.createInstance(InstallingLabelAction),
this.instantiationService.createInstance(ActionWithDropDownAction, 'extensions.uninstall', UninstallAction.UninstallLabel, [
[
this.instantiationService.createInstance(MigrateDeprecatedExtension, false),
this.instantiationService.createInstance(MigrateDeprecatedExtensionAction, false),
this.instantiationService.createInstance(UninstallAction),
this.instantiationService.createInstance(InstallAnotherVersionAction),
]
@ -518,6 +518,7 @@ export class ExtensionEditor extends EditorPane {
template.name.textContent = extension.displayName;
template.name.classList.toggle('clickable', !!extension.url);
template.name.classList.toggle('deprecated', !!extension.deprecationInfo);
template.preview.style.display = extension.preview ? 'inherit' : 'none';
template.builtin.style.display = extension.isBuiltin ? 'inherit' : 'none';

View file

@ -69,6 +69,7 @@ import { IPreferencesService } from 'vs/workbench/services/preferences/common/pr
import { renderIcon } from 'vs/base/browser/ui/iconLabel/iconLabels';
import { Codicon } from 'vs/base/common/codicons';
import { assertType } from 'vs/base/common/types';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
export class PromptExtensionInstallFailureAction extends Action {
@ -281,7 +282,7 @@ export abstract class AbstractInstallAction extends ExtensionAction {
}
if (this.extension.deprecationInfo) {
let detail = localize('deprecated message', "This extension is no longer being maintained and is deprecated.");
let detail = localize('deprecated message', "This extension is deprecated as it is no longer being maintained");
let action: () => Promise<any> = async () => undefined;
const buttons = [
localize('install anyway', "Install Anyway"),
@ -289,13 +290,13 @@ export abstract class AbstractInstallAction extends ExtensionAction {
];
if (this.extension.deprecationInfo.extension) {
detail = localize('deprecated with alternate extension message', "This extension has been deprecated. Use {0} instead.", this.extension.deprecationInfo.extension.displayName);
detail = localize('deprecated with alternate extension message', "This extension is deprecated. Use the {0} extension instead.", this.extension.deprecationInfo.extension.displayName);
buttons.splice(1, 0, localize('Show alternate extension', "Open {0}", this.extension.deprecationInfo.extension.displayName));
const alternateExtension = this.extension.deprecationInfo.extension;
action = () => this.extensionsWorkbenchService.getExtensions([{ id: alternateExtension.id, preRelease: alternateExtension.preRelease }], CancellationToken.None)
.then(([extension]) => this.extensionsWorkbenchService.open(extension));
} else if (this.extension.deprecationInfo.settings) {
detail = localize('deprecated with alternate settings message', "This extension is deprecated and has become a native feature in VS Code.");
detail = localize('deprecated with alternate settings message', "This extension is deprecated as this functionality is now built-in to VS Code.");
buttons.splice(1, 0, localize('configure in settings', "Configure Settings"));
const settings = this.extension.deprecationInfo.settings;
action = () => this.preferencesService.openSettings({ query: settings.map(setting => `@id:${setting}`).join(' ') });
@ -834,22 +835,22 @@ export class UpdateAction extends ExtensionAction {
}
}
export class MigrateDeprecatedExtension extends ExtensionAction {
export class MigrateDeprecatedExtensionAction extends ExtensionAction {
private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} prominent migrate`;
private static readonly DisabledClass = `${MigrateDeprecatedExtension.EnabledClass} disabled`;
private static readonly DisabledClass = `${MigrateDeprecatedExtensionAction.EnabledClass} disabled`;
constructor(
private readonly small: boolean,
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService
) {
super('extensions.uninstall', localize('migrateExtension', "Migrate"), MigrateDeprecatedExtension.DisabledClass, false);
super('extensionsAction.migrateDeprecatedExtension', localize('migrateExtension', "Migrate"), MigrateDeprecatedExtensionAction.DisabledClass, false);
this.update();
}
update(): void {
this.enabled = false;
this.class = MigrateDeprecatedExtension.DisabledClass;
this.class = MigrateDeprecatedExtensionAction.DisabledClass;
if (!this.extension?.local) {
return;
}
@ -864,7 +865,7 @@ export class MigrateDeprecatedExtension extends ExtensionAction {
return;
}
this.enabled = true;
this.class = MigrateDeprecatedExtension.EnabledClass;
this.class = MigrateDeprecatedExtensionAction.EnabledClass;
this.tooltip = localize('migrate to', "Migrate to {0}", this.extension.deprecationInfo.extension.displayName);
this.label = this.small ? localize('migrate', "Migrate") : this.tooltip;
}
@ -887,8 +888,9 @@ export class SponsorExtensionAction extends ExtensionAction {
constructor(
@IOpenerService private openerService: IOpenerService,
@ITelemetryService private telemetryService: ITelemetryService,
) {
super('extensions.sponsor', localize('sponsor', "Sponsor"), SponsorExtensionAction.DisabledClass, false);
super('extensionsAction.sponsorExtension', localize('sponsor', "Sponsor"), SponsorExtensionAction.DisabledClass, false);
this.update();
}
@ -905,6 +907,15 @@ export class SponsorExtensionAction extends ExtensionAction {
override async run(): Promise<any> {
if (this.extension?.publisherSponsorLink) {
type SponsorExtensionClassification = {
owner: 'sandy081';
comment: 'Reporting when sponosor extension action is executed';
'extensionId': { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Id of the extension to be sponsored' };
};
type SponsorExtensionEvent = {
'extensionId': string;
};
this.telemetryService.publicLog2<SponsorExtensionEvent, SponsorExtensionClassification>('extensionsAction.sponsorExtension', { extensionId: this.extension.identifier.id });
return this.openerService.open(this.extension.publisherSponsorLink);
}
}
@ -2295,12 +2306,12 @@ export class ExtensionStatusAction extends ExtensionAction {
if (this.extension.deprecationInfo) {
if (this.extension.deprecationInfo.extension) {
const link = `[${this.extension.deprecationInfo.extension.displayName}](${URI.parse(`command:extension.open?${encodeURIComponent(JSON.stringify([this.extension.deprecationInfo.extension.id]))}`)})`;
this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('deprecated with alternate extension tooltip', "This extension has been deprecated. Use {0} instead.", link)) }, true);
this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('deprecated with alternate extension tooltip', "This extension is deprecated. Use the {0} extension instead.", link)) }, true);
} else if (this.extension.deprecationInfo.settings) {
const link = `[${localize('settings', "settings")}](${URI.parse(`command:workbench.action.openSettings?${encodeURIComponent(JSON.stringify([this.extension.deprecationInfo.settings.map(setting => `@id:${setting}`).join(' ')]))}`)})`;
this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('deprecated with alternate settings tooltip', "This extension is deprecated and has become a native feature in VS Code. Configure these {0} instead.", link)) }, true);
this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('deprecated with alternate settings tooltip', "This extension is deprecated as this functionality is now built-in to VS Code. Configure these {0} to use this functionality.", link)) }, true);
} else {
this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('deprecated tooltip', "This extension is no longer being maintained and is deprecated.")) }, true);
this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('deprecated tooltip', "This extension is deprecated as it is no longer being maintained")) }, true);
}
return;
}
@ -2886,8 +2897,8 @@ export const extensionButtonProminentHoverBackground = registerColor('extensionB
hcLight: null
}, localize('extensionButtonProminentHoverBackground', "Button background hover color for actions extension that stand out (e.g. install button)."));
registerColor('extensionSponsorButton.background', { light: '#E94AAA', dark: '#E90A91', hcDark: null, hcLight: '#E94AAA' }, localize('extensionSponsorButton.background', "Background color for extension sponsor button."), true);
registerColor('extensionSponsorButton.hoverBackground', { light: '#E90A91', dark: '#E94AAA', hcDark: null, hcLight: '#E94AAA' }, localize('extensionSponsorButton.hoverBackground', "Background hover color for extension sponsor button."), true);
registerColor('extensionSponsorButton.background', { light: '#B51E78', dark: '#B51E78', hcDark: null, hcLight: '#B51E78' }, localize('extensionSponsorButton.background', "Background color for extension sponsor button."), true);
registerColor('extensionSponsorButton.hoverBackground', { light: '#D61B8C', dark: '#D61B8C', hcDark: null, hcLight: '#D61B8C' }, localize('extensionSponsorButton.hoverBackground', "Background hover color for extension sponsor button."), true);
registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => {
const foregroundColor = theme.getColor(foreground);

View file

@ -13,7 +13,7 @@ import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
import { IPagedRenderer } from 'vs/base/browser/ui/list/listPaging';
import { Event } from 'vs/base/common/event';
import { IExtension, ExtensionContainers, ExtensionState, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions';
import { UpdateAction, ManageExtensionAction, ReloadAction, ExtensionStatusLabelAction, RemoteInstallAction, ExtensionStatusAction, LocalInstallAction, ActionWithDropDownAction, InstallDropdownAction, InstallingLabelAction, ExtensionActionWithDropdownActionViewItem, ExtensionDropDownAction, WebInstallAction, SwitchToPreReleaseVersionAction, SwitchToReleasedVersionAction, MigrateDeprecatedExtension } from 'vs/workbench/contrib/extensions/browser/extensionsActions';
import { UpdateAction, ManageExtensionAction, ReloadAction, ExtensionStatusLabelAction, RemoteInstallAction, ExtensionStatusAction, LocalInstallAction, ActionWithDropDownAction, InstallDropdownAction, InstallingLabelAction, ExtensionActionWithDropdownActionViewItem, ExtensionDropDownAction, WebInstallAction, SwitchToPreReleaseVersionAction, SwitchToReleasedVersionAction, MigrateDeprecatedExtensionAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions';
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { RatingsWidget, InstallCountWidget, RecommendationWidget, RemoteBadgeWidget, ExtensionPackCountWidget as ExtensionPackBadgeWidget, SyncIgnoredWidget, ExtensionHoverWidget, ExtensionActivationStatusWidget, PreReleaseBookmarkWidget, extensionVerifiedPublisherIconColor } from 'vs/workbench/contrib/extensions/browser/extensionsWidgets';
import { IExtensionService, toExtension } from 'vs/workbench/services/extensions/common/extensions';
@ -118,7 +118,7 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
const reloadAction = this.instantiationService.createInstance(ReloadAction);
const actions = [
this.instantiationService.createInstance(ExtensionStatusLabelAction),
this.instantiationService.createInstance(MigrateDeprecatedExtension, true),
this.instantiationService.createInstance(MigrateDeprecatedExtensionAction, true),
this.instantiationService.createInstance(UpdateAction),
reloadAction,
this.instantiationService.createInstance(InstallDropdownAction),
@ -187,15 +187,17 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
data.extensionDisposables = dispose(data.extensionDisposables);
const updateEnablement = async () => {
let isDisabled = false;
let disabled = false;
let deprecated = !!extension.deprecationInfo;
if (extension.state === ExtensionState.Uninstalled) {
isDisabled = !!extension.deprecationInfo || !(await this.extensionsWorkbenchService.canInstall(extension));
disabled = deprecated || !(await this.extensionsWorkbenchService.canInstall(extension));
} else if (extension.local && !isLanguagePackExtension(extension.local.manifest)) {
const runningExtensions = await this.extensionService.getExtensions();
const runningExtension = runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, extension.identifier))[0];
isDisabled = !(runningExtension && extension.server === this.extensionManagementServerService.getExtensionManagementServer(toExtension(runningExtension)));
disabled = !(runningExtension && extension.server === this.extensionManagementServerService.getExtensionManagementServer(toExtension(runningExtension)));
}
data.root.classList.toggle('disabled', isDisabled);
data.element.classList.toggle('deprecated', deprecated);
data.root.classList.toggle('disabled', disabled);
};
updateEnablement();
this.extensionService.onDidChangeExtensions(() => updateEnablement(), this, data.extensionDisposables);

View file

@ -607,6 +607,7 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE
private normalizedQuery(): string {
return this.searchBox
? this.searchBox.getValue()
.trim()
.replace(/@category/g, 'category')
.replace(/@tag:/g, 'tag:')
.replace(/@ext:/g, 'ext:')

View file

@ -82,6 +82,10 @@
overflow: hidden;
}
.extension-list-item.deprecated > .details > .header-container > .header > .name {
text-decoration: line-through;
}
.extension-list-item > .details > .header-container > .header > .activation-status,
.extension-list-item > .details > .header-container > .header > .install-count,
.extension-list-item > .details > .header-container > .header > .ratings {

View file

@ -75,6 +75,10 @@
white-space: nowrap;
}
.extension-editor > .header > .details > .title > .name.deprecated {
text-decoration: line-through;
}
.extension-editor > .header > .details > .title > .version {
margin-left: 10px;
user-select: text;

View file

@ -186,12 +186,6 @@ configurationRegistry.registerConfiguration({
'markdownDescription': nls.localize('autoGuessEncoding', "When enabled, the editor will attempt to guess the character set encoding when opening files. This setting can also be configured per language. Note, this setting is not respected by text search. Only `#files.encoding#` is respected."),
'scope': ConfigurationScope.LANGUAGE_OVERRIDABLE
},
'files.excludeGitIgnore': {
type: 'boolean',
markdownDescription: nls.localize('excludeGitignore', "Controls whether entries in .gitignore should be parsed and excluded from the explorer. Similar to `#files.exclude#`."),
default: false,
scope: ConfigurationScope.RESOURCE
},
'files.eol': {
'type': 'string',
'enum': [
@ -477,6 +471,12 @@ configurationRegistry.registerConfiguration({
'description': nls.localize('copyRelativePathSeparator', "The path separation character used when copying relative file paths."),
'default': 'auto'
},
'explorer.excludeGitIgnore': {
type: 'boolean',
markdownDescription: nls.localize('excludeGitignore', "Controls whether entries in .gitignore should be parsed and excluded from the explorer. Similar to `#files.exclude#`."),
default: false,
scope: ConfigurationScope.RESOURCE
},
'explorer.fileNesting.enabled': {
'type': 'boolean',
scope: ConfigurationScope.RESOURCE,

View file

@ -610,6 +610,8 @@ export class FilesFilter implements ITreeFilter<ExplorerItem, FuzzyScore> {
// List of ignoreFile resources. Used to detect changes to the ignoreFiles.
private ignoreFileResourcesPerRoot = new Map<string, ResourceSet>();
// Ignore tree per root. Similar to `hiddenExpressionPerRoot`
// Note: URI in the ternary search tree is the URI of the folder containing the ignore file
// It is not the ignore file itself. This is because of the way the IgnoreFile works and nested paths
private ignoreTreesPerRoot = new Map<string, TernarySearchTree<URI, IgnoreFile>>();
constructor(
@ -622,7 +624,7 @@ export class FilesFilter implements ITreeFilter<ExplorerItem, FuzzyScore> {
) {
this.toDispose.push(this.contextService.onDidChangeWorkspaceFolders(() => this.updateConfiguration()));
this.toDispose.push(this.configurationService.onDidChangeConfiguration((e) => {
if (e.affectsConfiguration('files.exclude') || e.affectsConfiguration('files.excludeGitIgnore')) {
if (e.affectsConfiguration('files.exclude') || e.affectsConfiguration('explorer.excludeGitIgnore')) {
this.updateConfiguration();
}
}));
@ -634,8 +636,9 @@ export class FilesFilter implements ITreeFilter<ExplorerItem, FuzzyScore> {
await this.processIgnoreFile(root, ignoreResource, true);
}
if (e.contains(ignoreResource, FileChangeType.DELETED)) {
this.ignoreTreesPerRoot.get(root)?.delete(ignoreResource);
this.ignoreTreesPerRoot.get(root)?.delete(dirname(ignoreResource));
ignoreFileResourceSet.delete(ignoreResource);
this._onDidChange.fire();
}
});
}
@ -683,7 +686,7 @@ export class FilesFilter implements ITreeFilter<ExplorerItem, FuzzyScore> {
this.contextService.getWorkspace().folders.forEach(folder => {
const configuration = this.configurationService.getValue<IFilesConfiguration>({ resource: folder.uri });
const excludesConfig: glob.IExpression = configuration?.files?.exclude || Object.create(null);
const parseIgnoreFile: boolean = configuration.files.excludeGitIgnore;
const parseIgnoreFile: boolean = configuration.explorer.excludeGitIgnore;
// If we should be parsing ignoreFiles for this workspace and don't have an ignore tree initialize one
if (parseIgnoreFile && !this.ignoreTreesPerRoot.has(folder.uri.toString())) {
@ -728,23 +731,29 @@ export class FilesFilter implements ITreeFilter<ExplorerItem, FuzzyScore> {
if (!ignoreTree) {
return;
}
// If it's an update we remove the stale ignore file as we will be adding a new one
if (update) {
ignoreTree.delete(dirUri);
}
// Don't process a directory if we already have it in the tree
if (ignoreTree.has(dirUri)) {
if (!update && ignoreTree.has(dirUri)) {
return;
}
// Maybe we need a cancellation token here in case it's super long?
const content = await this.fileService.readFile(ignoreFileResource);
const ignoreParent = ignoreTree.findSubstr(dirUri);
const ignoreFile = new IgnoreFile(content.value.toString(), dirUri.path, ignoreParent);
ignoreTree.set(dirUri, ignoreFile);
// If we haven't seen this resource before then we need to add it to the list of resources we're tracking
if (!this.ignoreFileResourcesPerRoot.get(root)?.has(ignoreFileResource)) {
this.ignoreFileResourcesPerRoot.get(root)?.add(ignoreFileResource);
// If it's just an update we update the contents keeping all references the same
if (update) {
const ignoreFile = ignoreTree.get(dirUri);
ignoreFile?.updateContents(content.value.toString());
} else {
// Otherwise we create a new ignorefile and add it to the tree
const ignoreParent = ignoreTree.findSubstr(dirUri);
const ignoreFile = new IgnoreFile(content.value.toString(), dirUri.path, ignoreParent);
ignoreTree.set(dirUri, ignoreFile);
// If we haven't seen this resource before then we need to add it to the list of resources we're tracking
if (!this.ignoreFileResourcesPerRoot.get(root)?.has(ignoreFileResource)) {
this.ignoreFileResourcesPerRoot.get(root)?.add(ignoreFileResource);
}
}
// Notify the explorer of the change so we may ignore these files
this._onDidChange.fire();
}

View file

@ -98,6 +98,7 @@ export interface IFilesConfiguration extends PlatformIFilesConfiguration, IWorkb
badges: boolean;
};
incrementalNaming: 'simple' | 'smart';
excludeGitIgnore: boolean;
fileNesting: {
enabled: boolean;
expand: boolean;

View file

@ -12,7 +12,7 @@ import { Disposable } from 'vs/base/common/lifecycle';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IOpenEvent, IWorkbenchTableOptions, WorkbenchTable } from 'vs/platform/list/browser/listService';
import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel';
import { Marker, MarkerTableItem, ResourceMarkers } from 'vs/workbench/contrib/markers/browser/markersModel';
import { compareMarkersByUri, Marker, MarkerTableItem, ResourceMarkers } from 'vs/workbench/contrib/markers/browser/markersModel';
import { MarkerSeverity } from 'vs/platform/markers/common/markers';
import { SeverityIcon } from 'vs/platform/severityIcon/common/severityIcon';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
@ -28,6 +28,7 @@ import Messages from 'vs/workbench/contrib/markers/browser/messages';
import { isUndefinedOrNull } from 'vs/base/common/types';
import { IProblemsWidget } from 'vs/workbench/contrib/markers/browser/markersView';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { Range } from 'vs/editor/common/core/range';
const $ = DOM.$;
@ -133,6 +134,7 @@ class MarkerCodeColumnRenderer implements ITableRenderer<MarkerTableItem, IMarke
renderElement(element: MarkerTableItem, index: number, templateData: IMarkerCodeColumnTemplateData, height: number | undefined): void {
if (element.marker.source && element.marker.code) {
templateData.codeColumn.classList.toggle('code-link', typeof element.marker.code !== 'string');
DOM.show(templateData.codeLabel.element);
if (typeof element.marker.code === 'string') {
templateData.sourceLabel.set(element.marker.source, element.sourceMatches);
@ -149,6 +151,9 @@ class MarkerCodeColumnRenderer implements ITableRenderer<MarkerTableItem, IMarke
label: codeLinkLabel.element,
};
}
} else {
templateData.sourceLabel.set('-');
DOM.hide(templateData.codeLabel.element);
}
}
@ -437,7 +442,19 @@ export class MarkersTable extends Disposable implements IProblemsWidget {
}
}
this._itemCount = items.length;
this.table.splice(0, Number.POSITIVE_INFINITY, items.sort((a, b) => MarkerSeverity.compare(a.marker.severity, b.marker.severity)));
this.table.splice(0, Number.POSITIVE_INFINITY, items.sort((a, b) => {
let result = MarkerSeverity.compare(a.marker.severity, b.marker.severity);
if (result === 0) {
result = compareMarkersByUri(a.marker, b.marker);
}
if (result === 0) {
result = Range.compareRangesUsingStarts(a.marker, b.marker);
}
return result;
}));
}
revealMarkers(activeResource: ResourceMarkers | null, focus: boolean, lastSelectedRelativeTop: number): void {

View file

@ -607,6 +607,7 @@ export class MarkersView extends ViewPane implements IMarkersView {
// Restore selection
if (selection && selection.length > 0 && (selection[0] instanceof Marker || selection[0] instanceof MarkerTableItem)) {
this.widget.domFocus();
this.widget.setMarkerSelection(selection[0]);
}
}

View file

@ -94,9 +94,11 @@ export class EditorGutter<
scrollTop +
lineHeight;
const bottom =
this._editor.getTopForLineNumber(gutterItem.range.endLineNumberExclusive) -
scrollTop;
const bottom = (
gutterItem.range.endLineNumberExclusive <= this._editor.getModel()!.getLineCount()
? this._editor.getTopForLineNumber(gutterItem.range.endLineNumberExclusive)
: this._editor.getTopForLineNumber(gutterItem.range.endLineNumberExclusive - 1) + lineHeight
) - scrollTop;
const height = bottom - top;

View file

@ -71,8 +71,19 @@
justify-content: center;
}
.merge-accept-gutter-marker .checkbox .accept-conflict-group.monaco-custom-toggle.monaco-checkbox {
margin: 0;
padding: 0;
.accept-conflict-group.monaco-custom-toggle {
height: 18px;
width: 18px;
border: 1px solid transparent;
border-radius: 3px;
margin-right: 0px;
margin-left: 0px;
padding: 0px;
opacity: 1;
background-size: 16px !important;
background-color: var(--vscode-checkbox-border);
}
.checkbox-background {
background: var(--vscode-editor-background);
}

View file

@ -106,48 +106,40 @@ export class MergeEditor extends EditorPane {
this._register(keepAlive(input1ResultMapping));
this._register(keepAlive(input2ResultMapping));
this._store.add(this.input1View.editor.onDidScrollChange(c => {
if (c.scrollTopChanged) {
reentrancyBarrier.runExclusively(() => {
const mapping = input1ResultMapping.get();
if (!mapping) {
return;
this._store.add(
this.input1View.editor.onDidScrollChange(
reentrancyBarrier.makeExclusive((c) => {
if (c.scrollTopChanged) {
const mapping = input1ResultMapping.get();
synchronizeScrolling(this.input1View.editor, this.inputResultView.editor, mapping, 1);
this.input2View.editor.setScrollTop(c.scrollTop, ScrollType.Immediate);
}
synchronizeScrolling(this.input1View.editor, this.inputResultView.editor, mapping, 1);
this.input2View.editor.setScrollTop(c.scrollTop, ScrollType.Immediate);
});
}
}));
this._store.add(this.input2View.editor.onDidScrollChange(c => {
if (c.scrollTopChanged) {
reentrancyBarrier.runExclusively(() => {
const mapping = input2ResultMapping.get();
if (!mapping) {
return;
})
)
);
this._store.add(
this.input2View.editor.onDidScrollChange(
reentrancyBarrier.makeExclusive((c) => {
if (c.scrollTopChanged) {
const mapping = input2ResultMapping.get();
synchronizeScrolling(this.input2View.editor, this.inputResultView.editor, mapping, 1);
this.input1View.editor.setScrollTop(c.scrollTop, ScrollType.Immediate);
}
synchronizeScrolling(this.input2View.editor, this.inputResultView.editor, mapping, 1);
this.input1View.editor.setScrollTop(c.scrollTop, ScrollType.Immediate);
});
}
}));
this._store.add(this.inputResultView.editor.onDidScrollChange(c => {
if (c.scrollTopChanged) {
reentrancyBarrier.runExclusively(() => {
const mapping = input1ResultMapping.get();
if (!mapping) {
return;
})
)
);
this._store.add(
this.inputResultView.editor.onDidScrollChange(
reentrancyBarrier.makeExclusive((c) => {
if (c.scrollTopChanged) {
const mapping1 = input1ResultMapping.get();
synchronizeScrolling(this.inputResultView.editor, this.input1View.editor, mapping1, 2);
const mapping2 = input2ResultMapping.get();
synchronizeScrolling(this.inputResultView.editor, this.input2View.editor, mapping2, 2);
}
synchronizeScrolling(this.inputResultView.editor, this.input1View.editor, mapping, 2);
const mapping2 = input2ResultMapping.get();
if (!mapping2) {
return;
}
synchronizeScrolling(this.inputResultView.editor, this.input2View.editor, mapping2, 2);
});
}
}));
})
)
);
// TODO@jrieken make this proper: add menu id and allow extensions to contribute
@ -307,11 +299,11 @@ export class MergeEditor extends EditorPane {
}
}
function flip(value: 1 | 2): 1 | 2 {
return value === 1 ? 2 : 1;
}
function synchronizeScrolling(scrollingEditor: CodeEditorWidget, targetEditor: CodeEditorWidget, mapping: ModifiedBaseRange[] | undefined, sourceNumber: 1 | 2) {
if (!mapping) {
return;
}
function synchronizeScrolling(scrollingEditor: CodeEditorWidget, targetEditor: CodeEditorWidget, mapping: ModifiedBaseRange[], sourceNumber: 1 | 2) {
const visibleRanges = scrollingEditor.getVisibleRanges();
if (visibleRanges.length === 0) {
return;
@ -322,21 +314,24 @@ function synchronizeScrolling(scrollingEditor: CodeEditorWidget, targetEditor: C
let sourceRange: LineRange;
let targetRange: LineRange;
const targetNumber = flip(sourceNumber);
const targetNumber = sourceNumber === 1 ? 2 : 1;
if (firstBefore && firstBefore.getInputRange(sourceNumber).contains(topLineNumber)) {
sourceRange = firstBefore.getInputRange(sourceNumber);
targetRange = firstBefore.getInputRange(targetNumber);
} else if (firstBefore && firstBefore.getInputRange(sourceNumber).isEmpty && firstBefore.getInputRange(sourceNumber).startLineNumber === topLineNumber) {
sourceRange = firstBefore.getInputRange(sourceNumber).deltaEnd(1);
targetRange = firstBefore.getInputRange(targetNumber).deltaEnd(1);
const firstBeforeSourceRange = firstBefore?.getInputRange(sourceNumber);
const firstBeforeTargetRange = firstBefore?.getInputRange(targetNumber);
if (firstBeforeSourceRange && firstBeforeSourceRange.contains(topLineNumber)) {
sourceRange = firstBeforeSourceRange;
targetRange = firstBeforeTargetRange!;
} else if (firstBeforeSourceRange && firstBeforeSourceRange.isEmpty && firstBeforeSourceRange.startLineNumber === topLineNumber) {
sourceRange = firstBeforeSourceRange.deltaEnd(1);
targetRange = firstBeforeTargetRange!.deltaEnd(1);
} else {
const delta = firstBefore ? firstBefore.getInputRange(targetNumber).endLineNumberExclusive - firstBefore.getInputRange(sourceNumber).endLineNumberExclusive : 0;
const delta = firstBeforeSourceRange ? firstBeforeTargetRange!.endLineNumberExclusive - firstBeforeSourceRange.endLineNumberExclusive : 0;
sourceRange = new LineRange(topLineNumber, 1);
targetRange = new LineRange(topLineNumber + delta, 1);
}
// sourceRange is not empty!
// sourceRange contains topLineNumber!
const resultStartTopPx = targetEditor.getTopForLineNumber(targetRange.startLineNumber);
const resultEndPx = targetEditor.getTopForLineNumber(targetRange.endLineNumberExclusive);
@ -344,8 +339,21 @@ function synchronizeScrolling(scrollingEditor: CodeEditorWidget, targetEditor: C
const sourceStartTopPx = scrollingEditor.getTopForLineNumber(sourceRange.startLineNumber);
const sourceEndPx = scrollingEditor.getTopForLineNumber(sourceRange.endLineNumberExclusive);
const factor = (scrollingEditor.getScrollTop() - sourceStartTopPx) / (sourceEndPx - sourceStartTopPx);
const factor = Math.min((scrollingEditor.getScrollTop() - sourceStartTopPx) / (sourceEndPx - sourceStartTopPx), 1);
const resultScrollPosition = resultStartTopPx + (resultEndPx - resultStartTopPx) * factor;
/*
console.log({
topLineNumber,
sourceRange: sourceRange.toString(),
targetRange: targetRange.toString(),
// resultStartTopPx,
// resultEndPx,
// sourceStartTopPx,
// sourceEndPx,
factor,
resultScrollPosition,
top: scrollingEditor.getScrollTop(),
});*/
targetEditor.setScrollTop(resultScrollPosition, ScrollType.Immediate);
}
@ -468,7 +476,7 @@ class InputCodeEditorView extends CodeEditorView {
if (!model) { return []; }
return model.modifiedBaseRanges
.filter((r) => r.getInputDiffs(this.inputNumber).length > 0)
.map<MergeConflictData>((baseRange, idx) => ({
.map<ModifiedBaseRangeGutterItemInfo>((baseRange, idx) => ({
id: idx.toString(),
additionalHeightInPx: 0,
offsetInPx: 0,
@ -497,25 +505,31 @@ class InputCodeEditorView extends CodeEditorView {
}
}
interface MergeConflictData extends IGutterItemInfo {
interface ModifiedBaseRangeGutterItemInfo extends IGutterItemInfo {
toggleState: IObservable<boolean | undefined>;
setState(value: boolean, tx: ITransaction | undefined): void;
}
class MergeConflictGutterItemView extends Disposable implements IGutterItemView<MergeConflictData> {
constructor(private item: MergeConflictData, target: HTMLElement) {
class MergeConflictGutterItemView extends Disposable implements IGutterItemView<ModifiedBaseRangeGutterItemInfo> {
constructor(private item: ModifiedBaseRangeGutterItemInfo, private readonly target: HTMLElement) {
super();
target.classList.add('merge-accept-gutter-marker');
target.classList.add(item.range.lineCount > 1 ? 'multi-line' : 'single-line');
// TODO: Tri-State-Toggle, localized title
const checkBox = new Toggle({ isChecked: false, title: 'Accept Merge', icon: Codicon.check, actionClassName: 'monaco-checkbox' });
// TODO: localized title
const checkBox = new Toggle({ isChecked: false, title: 'Accept Merge', icon: Codicon.check });
checkBox.domNode.classList.add('accept-conflict-group');
this._register(
autorun((reader) => {
const value = this.item.toggleState.read(reader);
checkBox.setIcon(
value === true
? Codicon.check
: value === false
? undefined
: Codicon.circleFilled
);
checkBox.checked = value === true;
}, 'Update Toggle State')
);
@ -524,15 +538,19 @@ class MergeConflictGutterItemView extends Disposable implements IGutterItemView<
this.item.setState(checkBox.checked, undefined);
}));
target.appendChild($('div.background', {}, noBreakWhitespace));
target.appendChild($('div.checkbox', {}, checkBox.domNode));
target.appendChild(n('div.background', [noBreakWhitespace]).root);
target.appendChild(
n('div.checkbox', [n('div.checkbox-background', [checkBox.domNode])]).root
);
}
layout(top: number, height: number, viewTop: number, viewHeight: number): void {
this.target.classList.remove('multi-line');
this.target.classList.remove('single-line');
this.target.classList.add(height > 30 ? 'multi-line' : 'single-line');
}
update(baseRange: MergeConflictData): void {
update(baseRange: ModifiedBaseRangeGutterItemInfo): void {
this.item = baseRange;
}
}

View file

@ -113,6 +113,8 @@ export class MergeEditorModel extends EditorModel {
this.recomputeState();
});
this.recomputeState();
this.resetUnknown();
}
private recomputeState(): void {
@ -135,6 +137,16 @@ export class MergeEditorModel extends EditorModel {
});
}
public resetUnknown(): void {
transaction(tx => {
for (const range of this.modifiedBaseRanges) {
if (this.getState(range).get().conflicting) {
this.setState(range, ModifiedBaseRangeState.default, tx);
}
}
});
}
public mergeNonConflictingDiffs(): void {
transaction((tx) => {
for (const m of this.modifiedBaseRanges) {
@ -215,7 +227,7 @@ export class MergeEditorModel extends EditorModel {
if (!existingState) {
throw new BugIndicatingError('object must be from this instance');
}
existingState.set(state, transaction);
const conflictingDiffs = this.resultEdits.findTouchingDiffs(
baseRange.baseRange
@ -224,13 +236,73 @@ export class MergeEditorModel extends EditorModel {
this.resultEdits.removeDiffs(conflictingDiffs, transaction);
}
const diff = state.input1
? baseRange.input1CombinedDiff
: state.input2
? baseRange.input2CombinedDiff
: undefined;
if (diff) {
this.resultEdits.applyEditRelativeToOriginal(diff.getLineEdit(), transaction);
function getEdit(baseRange: ModifiedBaseRange, state: ModifiedBaseRangeState): { edit: LineEdit | undefined; effectiveState: ModifiedBaseRangeState } {
interface LineDiffWithInputNumber {
diff: LineDiff;
inputNumber: 1 | 2;
}
const diffs = new Array<LineDiffWithInputNumber>();
if (state.input1) {
if (baseRange.input1CombinedDiff) {
diffs.push({ diff: baseRange.input1CombinedDiff, inputNumber: 1 });
}
}
if (state.input2) {
if (baseRange.input2CombinedDiff) {
diffs.push({ diff: baseRange.input2CombinedDiff, inputNumber: 2 });
}
}
if (state.input2First) {
diffs.reverse();
}
const firstDiff: LineDiffWithInputNumber | undefined = diffs[0];
const secondDiff: LineDiffWithInputNumber | undefined = diffs[1];
diffs.sort(compareBy(d => d.diff.originalRange, LineRange.compareByStart));
if (!firstDiff) {
return { edit: undefined, effectiveState: state };
}
if (!secondDiff) {
return { edit: firstDiff.diff.getLineEdit(), effectiveState: state };
}
// Two inserts
if (
firstDiff.diff.originalRange.lineCount === 0 &&
firstDiff.diff.originalRange.equals(secondDiff.diff.originalRange)
) {
return {
edit: new LineEdit(
firstDiff.diff.originalRange,
firstDiff.diff
.getLineEdit()
.newLines.concat(secondDiff.diff.getLineEdit().newLines)
),
effectiveState: state,
};
}
// Technically non-conflicting diffs
if (diffs.length === 2 && diffs[0].diff.originalRange.endLineNumberExclusive === diffs[1].diff.originalRange.startLineNumber) {
return {
edit: new LineEdit(
LineRange.join(diffs.map(d => d.diff.originalRange))!,
diffs.flatMap(d => d.diff.getLineEdit().newLines)
),
effectiveState: state,
};
}
return { edit: firstDiff.diff.getLineEdit(), effectiveState: state };
}
const { edit, effectiveState } = getEdit(baseRange, state);
existingState.set(effectiveState, transaction);
if (edit) {
this.resultEdits.applyEditRelativeToOriginal(edit, transaction);
}
}
@ -359,11 +431,11 @@ class ResultEdits {
new LineRange(edit.range.startLineNumber + delta, edit.newLines.length)
));
}
this._diffs.set(newDiffs, transaction);
this.barrier.runExclusivelyOrThrow(() => {
new LineEdit(edit.range.delta(delta), edit.newLines).apply(this.resultTextModel);
});
this._diffs.set(newDiffs, transaction);
}
public findTouchingDiffs(baseRange: LineRange): LineDiff[] {

View file

@ -9,6 +9,10 @@ import { Range } from 'vs/editor/common/core/range';
import { ILineChange } from 'vs/editor/common/diff/diffComputer';
import { ITextModel } from 'vs/editor/common/model';
/**
* Represents an edit, expressed in whole lines:
* At {@link LineRange.startLineNumber}, delete {@link LineRange.lineCount} many lines and insert {@link newLines}.
*/
export class LineEdit {
constructor(
public readonly range: LineRange,
@ -30,10 +34,26 @@ export class LineEdits {
public apply(model: ITextModel): void {
model.pushEditOperations(
null,
this.edits.map((e) => ({
range: new Range(e.range.startLineNumber, 1, e.range.endLineNumberExclusive, 1),
text: e.newLines.map(l => l + '\n').join(''),
})),
this.edits.map((e) => {
if (e.range.endLineNumberExclusive <= model.getLineCount()) {
return {
range: new Range(e.range.startLineNumber, 1, e.range.endLineNumberExclusive, 1),
text: e.newLines.map(s => s + '\n').join(''),
};
}
if (e.range.startLineNumber === 1) {
return {
range: new Range(1, 1, model.getLineCount(), Number.MAX_SAFE_INTEGER),
text: e.newLines.join('\n'),
};
}
return {
range: new Range(e.range.startLineNumber - 1, Number.MAX_SAFE_INTEGER, model.getLineCount(), Number.MAX_SAFE_INTEGER),
text: e.newLines.map(s => '\n' + s).join(''),
};
}),
() => null
);
}
@ -110,6 +130,14 @@ export class LineRange {
public deltaEnd(delta: number): LineRange {
return new LineRange(this.startLineNumber, this.lineCount + delta);
}
public getLines(model: ITextModel): string[] {
const result = new Array(this.lineCount);
for (let i = 0; i < this.lineCount; i++) {
result[i] = model.getLineContent(this.startLineNumber + i);
}
return result;
}
}
export class LineDiff {
@ -215,19 +243,11 @@ export class LineDiff {
}
private getModifiedLines(): string[] {
const result = new Array(this.modifiedRange.lineCount);
for (let i = 0; i < this.modifiedRange.lineCount; i++) {
result[i] = this.modifiedTextModel.getLineContent(this.modifiedRange.startLineNumber + i);
}
return result;
return this.modifiedRange.getLines(this.modifiedTextModel);
}
private getOriginalLines(): string[] {
const result = new Array(this.originalRange.lineCount);
for (let i = 0; i < this.originalRange.lineCount; i++) {
result[i] = this.originalTextModel.getLineContent(this.originalRange.startLineNumber + i);
}
return result;
return this.originalRange.getLines(this.originalTextModel);
}
}
@ -371,7 +391,10 @@ export class ModifiedBaseRangeState {
public readonly conflicting: boolean,
) { }
public getInput(inputNumber: 1 | 2): boolean {
public getInput(inputNumber: 1 | 2): boolean | undefined {
if (this.conflicting) {
return undefined;
}
if (inputNumber === 1) {
return this.input1;
} else {
@ -386,17 +409,17 @@ export class ModifiedBaseRangeState {
public withInput1(value: boolean): ModifiedBaseRangeState {
return new ModifiedBaseRangeState(
value,
false,
value && this.isEmpty ? false : this.input2First,
this.input2,
value !== this.input2 ? this.input2 : this.input2First,
false,
);
}
public withInput2(value: boolean): ModifiedBaseRangeState {
return new ModifiedBaseRangeState(
false,
this.input1,
value,
value && this.isEmpty ? true : this.input2First,
value !== this.input1 ? value : this.input2First,
false
);
}

View file

@ -14,6 +14,20 @@ import { IDisposable } from 'xterm';
export class ReentrancyBarrier {
private isActive = false;
public makeExclusive<TFunction extends Function>(fn: TFunction): TFunction {
return ((...args: any[]) => {
if (this.isActive) {
return;
}
this.isActive = true;
try {
return fn(...args);
} finally {
this.isActive = false;
}
}) as any;
}
public runExclusively(fn: () => void): void {
if (this.isActive) {
return;
@ -40,7 +54,7 @@ export class ReentrancyBarrier {
}
export function n<TTag extends string>(tag: TTag): never;
export function n<TTag extends string, T extends any[]>(
export function n<TTag extends string, T extends (HTMLElement | string | Record<string, HTMLElement>)[]>(
tag: TTag,
children: T
): (ArrayToObj<T> & Record<'root', TagToElement<TTag>>) extends infer Y ? { [TKey in keyof Y]: Y[TKey] } : never;
@ -48,7 +62,7 @@ export function n<TTag extends string, TId extends string>(
tag: TTag,
attributes: { $: TId }
): Record<TId, TagToElement<TTag>>;
export function n<TTag extends string, TId extends string, T extends any[]>(
export function n<TTag extends string, TId extends string, T extends (HTMLElement | string | Record<string, HTMLElement>)[]>(
tag: TTag,
attributes: { $: TId },
children: T
@ -77,6 +91,8 @@ export function n(tag: string, ...args: [] | [attributes: { $: string } | Record
for (const c of children) {
if (c instanceof HTMLElement) {
el.appendChild(c);
} else if (typeof c === 'string') {
el.append(c);
} else {
Object.assign(result, c);
el.appendChild(c.root);
@ -84,8 +100,6 @@ export function n(tag: string, ...args: [] | [attributes: { $: string } | Record
}
}
result['root'] = el;
for (const [key, value] of Object.entries(attributes)) {
if (key === '$') {
result[value] = el;
@ -94,6 +108,8 @@ export function n(tag: string, ...args: [] | [attributes: { $: string } | Record
el.setAttribute(key, value);
}
result['root'] = el;
return result;
}

View file

@ -12,7 +12,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { ITextModel } from 'vs/editor/common/model';
import { StartFindAction, StartFindReplaceAction } from 'vs/editor/contrib/find/browser/findController';
import { FindStartFocusAction, getSelectionSearchString, IFindStartOptions, StartFindAction, StartFindReplaceAction } from 'vs/editor/contrib/find/browser/findController';
import { localize } from 'vs/nls';
import { Action2, registerAction2 } from 'vs/platform/actions/common/actions';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
@ -24,6 +24,7 @@ import { registerNotebookContribution } from 'vs/workbench/contrib/notebook/brow
import { CellUri } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { KEYBINDING_CONTEXT_NOTEBOOK_FIND_WIDGET_FOCUSED, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_IS_ACTIVE_EDITOR } from 'vs/workbench/contrib/notebook/common/notebookContextKeys';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
registerNotebookContribution(NotebookFindWidget.id, NotebookFindWidget);
@ -91,6 +92,23 @@ function notebookContainsTextModel(uri: URI, textModel: ITextModel) {
return false;
}
function getSearchString(editor: ICodeEditor, opts: IFindStartOptions) {
// Get the search string result, following the same logic in _start function in 'vs/editor/contrib/find/browser/findController'
let searchString = '';
if (opts.seedSearchStringFromSelection === 'single') {
let selectionSearchString = getSelectionSearchString(editor, opts.seedSearchStringFromSelection, opts.seedSearchStringFromNonEmptySelection);
if (selectionSearchString) {
searchString = selectionSearchString;
}
} else if (opts.seedSearchStringFromSelection === 'multiple' && !opts.updateSearchScope) {
let selectionSearchString = getSelectionSearchString(editor, opts.seedSearchStringFromSelection);
if (selectionSearchString) {
searchString = selectionSearchString;
}
}
return searchString;
}
StartFindAction.addImplementation(100, (accessor: ServicesAccessor, codeEditor: ICodeEditor, args: any) => {
const editorService = accessor.get(IEditorService);
@ -112,7 +130,19 @@ StartFindAction.addImplementation(100, (accessor: ServicesAccessor, codeEditor:
}
const controller = editor.getContribution<NotebookFindWidget>(NotebookFindWidget.id);
controller.show();
const searchString = getSearchString(codeEditor, {
forceRevealReplace: false,
seedSearchStringFromSelection: codeEditor.getOption(EditorOption.find).seedSearchStringFromSelection !== 'never' ? 'single' : 'none',
seedSearchStringFromNonEmptySelection: codeEditor.getOption(EditorOption.find).seedSearchStringFromSelection === 'selection',
seedSearchStringFromGlobalClipboard: codeEditor.getOption(EditorOption.find).globalFindClipboard,
shouldFocus: FindStartFocusAction.FocusFindInput,
shouldAnimate: true,
updateSearchScope: false,
loop: codeEditor.getOption(EditorOption.find).loop
});
controller.show(searchString);
return true;
});
@ -125,8 +155,20 @@ StartFindReplaceAction.addImplementation(100, (accessor: ServicesAccessor, codeE
}
const controller = editor.getContribution<NotebookFindWidget>(NotebookFindWidget.id);
const searchString = getSearchString(codeEditor, {
forceRevealReplace: false,
seedSearchStringFromSelection: codeEditor.getOption(EditorOption.find).seedSearchStringFromSelection !== 'never' ? 'single' : 'none',
seedSearchStringFromNonEmptySelection: codeEditor.getOption(EditorOption.find).seedSearchStringFromSelection === 'selection',
seedSearchStringFromGlobalClipboard: codeEditor.getOption(EditorOption.find).globalFindClipboard,
shouldFocus: FindStartFocusAction.FocusFindInput,
shouldAnimate: true,
updateSearchScope: false,
loop: codeEditor.getOption(EditorOption.find).loop
});
if (controller) {
controller.replace();
controller.replace(searchString);
return true;
}

View file

@ -615,7 +615,7 @@ export abstract class SimpleFindReplaceWidget extends Widget {
}
public show(initialInput?: string, options?: { focus?: boolean }): void {
if (initialInput && !this._isVisible) {
if (initialInput) {
this._findInput.setValue(initialInput);
}
@ -632,11 +632,11 @@ export abstract class SimpleFindReplaceWidget extends Widget {
}
public showWithReplace(initialInput?: string, replaceInput?: string): void {
if (initialInput && !this._isVisible) {
if (initialInput) {
this._findInput.setValue(initialInput);
}
if (replaceInput && !this._isVisible) {
if (replaceInput) {
this._replaceInput.setValue(replaceInput);
}

View file

@ -1241,7 +1241,7 @@ export class NotebookCellList extends WorkbenchList<CellViewModel> implements ID
if (ignoreIfInsideViewport
&& elementTop >= scrollTop
&& elementTop < wrapperBottom) {
&& elementBottom < wrapperBottom) {
if (revealPosition === CellRevealPosition.Center
&& elementBottom > wrapperBottom

View file

@ -6,7 +6,7 @@
import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { IWorkbenchContributionsRegistry, IWorkbenchContribution, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { Registry } from 'vs/platform/registry/common/platform';
import { IWindowsConfiguration } from 'vs/platform/window/common/window';
import { IWindowsConfiguration, IWindowSettings } from 'vs/platform/window/common/window';
import { IHostService } from 'vs/workbench/services/host/browser/host';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { localize } from 'vs/nls';
@ -15,7 +15,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten
import { RunOnceScheduler } from 'vs/base/common/async';
import { URI } from 'vs/base/common/uri';
import { isEqual } from 'vs/base/common/resources';
import { isMacintosh, isNative, isLinux } from 'vs/base/common/platform';
import { isMacintosh, isNative, isLinux, isWindows } from 'vs/base/common/platform';
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
@ -26,11 +26,13 @@ interface IConfiguration extends IWindowsConfiguration {
debug?: { console?: { wordWrap?: boolean } };
editor?: { accessibilitySupport?: 'on' | 'off' | 'auto' };
security?: { workspace?: { trust?: { enabled?: boolean } } };
window: IWindowSettings & { experimental?: { windowControlsOverlay?: { enabled?: boolean } } };
}
export class SettingsChangeRelauncher extends Disposable implements IWorkbenchContribution {
private titleBarStyle: 'native' | 'custom' | undefined;
private windowControlsOverlayEnabled: boolean | undefined;
private nativeTabs: boolean | undefined;
private nativeFullScreen: boolean | undefined;
private clickThroughInactive: boolean | undefined;
@ -61,6 +63,13 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo
changed = true;
}
// Windows: Window Controls Overlay
if (isWindows && typeof config.window?.experimental?.windowControlsOverlay?.enabled === 'boolean' && config.window?.experimental?.windowControlsOverlay?.enabled !== this.windowControlsOverlayEnabled) {
this.windowControlsOverlayEnabled = config.window.experimental.windowControlsOverlay.enabled;
changed = true;
}
// macOS: Native tabs
if (isMacintosh && typeof config.window?.nativeTabs === 'boolean' && config.window.nativeTabs !== this.nativeTabs) {
this.nativeTabs = config.window.nativeTabs;

View file

@ -466,6 +466,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
// Re-establish the title after reconnect
if (this.shellLaunchConfig.attachPersistentProcess) {
this._cwd = this.shellLaunchConfig.attachPersistentProcess.cwd;
this.refreshTabLabels(this.shellLaunchConfig.attachPersistentProcess.title, this.shellLaunchConfig.attachPersistentProcess.titleSource);
this.setShellType(this.shellType);
}

View file

@ -8,7 +8,7 @@ import { localize } from 'vs/nls';
import { MenuRegistry, MenuId, registerAction2 } from 'vs/platform/actions/common/actions';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
import { isLinux, isMacintosh } from 'vs/base/common/platform';
import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform';
import { ConfigureRuntimeArgumentsAction, ToggleDevToolsAction, ToggleSharedProcessAction, ReloadWindowWithExtensionsDisabledAction } from 'vs/workbench/electron-sandbox/actions/developerActions';
import { ZoomResetAction, ZoomOutAction, ZoomInAction, CloseWindowAction, SwitchWindowAction, QuickSwitchWindowAction, NewWindowTabHandler, ShowPreviousWindowTabHandler, ShowNextWindowTabHandler, MoveWindowTabToNewWindowHandler, MergeWindowTabsHandlerHandler, ToggleWindowTabsBarHandler } from 'vs/workbench/electron-sandbox/actions/windowActions';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
@ -203,6 +203,13 @@ import { ModifierKeyEmitter } from 'vs/base/browser/dom';
'scope': ConfigurationScope.APPLICATION,
'description': localize('titleBarStyle', "Adjust the appearance of the window title bar. On Linux and Windows, this setting also affects the application and context menu appearances. Changes require a full restart to apply.")
},
'window.experimental.windowControlsOverlay.enabled': {
'type': 'boolean',
'default': false,
'scope': ConfigurationScope.APPLICATION,
'description': localize('windowControlsOverlay', "Use window controls provided by the platform instead of our HTML-based window controls. Changes require a full restart to apply."),
'included': isWindows
},
'window.dialogStyle': {
'type': 'string',
'enum': ['native', 'custom'],

View file

@ -17,7 +17,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
import { getTitleBarStyle } from 'vs/platform/window/common/window';
import { getTitleBarStyle, useWindowControlsOverlay } from 'vs/platform/window/common/window';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Codicon } from 'vs/base/common/codicons';
import { NativeMenubarControl } from 'vs/workbench/electron-sandbox/parts/titlebar/menubarControl';
@ -196,7 +196,7 @@ export class TitlebarPart extends BrowserTitleBarPart {
super.updateStyles();
// WCO styles only supported on Windows currently
if (isWindows) {
if (useWindowControlsOverlay(this.configurationService, this.environmentService)) {
if (!this.cachedWindowControlStyles ||
this.cachedWindowControlStyles.bgColor !== this.element.style.backgroundColor ||
this.cachedWindowControlStyles.fgColor !== this.element.style.color) {

View file

@ -101,9 +101,23 @@ export class NativeRemoteExtensionManagementService extends ExtensionManagementC
}
private async checkAndGetCompatible(extension: IGalleryExtension, includePreRelease: boolean): Promise<IGalleryExtension> {
const compatible = await this.galleryService.getCompatibleExtension(extension, includePreRelease, await this.getTargetPlatform());
if (compatible) {
if (includePreRelease && !compatible.properties.isPreReleaseVersion && extension.hasPreReleaseVersion) {
const targetPlatform = await this.getTargetPlatform();
let compatibleExtension: IGalleryExtension | null = null;
if (extension.hasPreReleaseVersion && extension.properties.isPreReleaseVersion !== includePreRelease) {
compatibleExtension = (await this.galleryService.getExtensions([{ ...extension.identifier, preRelease: includePreRelease }], { targetPlatform, compatible: true }, CancellationToken.None))[0] || null;
}
if (!compatibleExtension && await this.galleryService.isExtensionCompatible(extension, includePreRelease, targetPlatform)) {
compatibleExtension = extension;
}
if (!compatibleExtension) {
compatibleExtension = await this.galleryService.getCompatibleExtension(extension, includePreRelease, targetPlatform);
}
if (compatibleExtension) {
if (includePreRelease && !compatibleExtension.properties.isPreReleaseVersion && extension.hasPreReleaseVersion) {
throw new ExtensionManagementError(localize('notFoundCompatiblePrereleaseDependency', "Can't install pre-release version of '{0}' extension because it is not compatible with the current version of {1} (version {2}).", extension.identifier.id, this.productService.nameLong, this.productService.version), ExtensionManagementErrorCode.IncompatiblePreRelease);
}
} else {
@ -114,7 +128,7 @@ export class NativeRemoteExtensionManagementService extends ExtensionManagementC
throw new ExtensionManagementError(localize('notFoundCompatibleDependency', "Can't install '{0}' extension because it is not compatible with the current version of {1} (version {2}).", extension.identifier.id, this.productService.nameLong, this.productService.version), ExtensionManagementErrorCode.Incompatible);
}
return compatible;
return compatibleExtension;
}
private async installUIDependenciesAndPackedExtensions(local: ILocalExtension): Promise<void> {

View file

@ -518,8 +518,17 @@ export const schema: IJSONSchema = {
}
},
sponsor: {
description: nls.localize('vscode.extension.contributes.sponsorLink', "URL from where users can sponsor your extension. If a URL is provided a Sponsor button will get rendered in extension details page. Example value: https://github.com/sponsors/nvaccess"),
type: 'string',
description: nls.localize('vscode.extension.contributes.sponsor', "Specify the location from where users can sponsor your extension."),
type: 'object',
defaultSnippets: [
{ body: { url: '${1:https:}' } },
],
properties: {
'url': {
description: nls.localize('vscode.extension.contributes.sponsor.url', "URL from where users can sponsor your extension. It must be a valid URL with a HTTP or HTTPS protocol. Example value: https://github.com/sponsors/nvaccess"),
type: 'string',
}
}
},
scripts: {
type: 'object',

View file

@ -6,7 +6,9 @@
import { ExtensionIdentifier, IExtensionDescription, IRelaxedExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { localize } from 'vs/nls';
import { ILogService } from 'vs/platform/log/common/log';
import * as semver from 'vs/base/common/semver/semver';
// TODO: @sandy081 merge this with deduping in extensionsScannerService.ts
export function dedupExtensions(system: IExtensionDescription[], user: IExtensionDescription[], development: IExtensionDescription[], logService: ILogService): IExtensionDescription[] {
let result = new Map<string, IExtensionDescription>();
system.forEach((systemExtension) => {
@ -22,6 +24,10 @@ export function dedupExtensions(system: IExtensionDescription[], user: IExtensio
const extension = result.get(extensionKey);
if (extension) {
if (extension.isBuiltin) {
if (semver.gt(extension.version, userExtension.version)) {
logService.warn(`Skipping extension ${userExtension.extensionLocation.path} with lower version ${userExtension.version}.`);
return;
}
// Overwriting a builtin extension inherits the `isBuiltin` property and it doesn't show a warning
(<IRelaxedExtensionDescription>userExtension).isBuiltin = true;
} else {

View file

@ -10,14 +10,25 @@ export class IgnoreFile {
private isPathIgnored: (path: string, isDir: boolean, parent?: IgnoreFile) => boolean;
constructor(contents: string, location: string, parent?: IgnoreFile) {
constructor(
contents: string,
private readonly location: string,
private readonly parent?: IgnoreFile) {
if (location[location.length - 1] === '\\') {
throw Error('Unexpected path format, do not use trailing backslashes');
}
if (location[location.length - 1] !== '/') {
location += '/';
}
this.isPathIgnored = this.parseIgnoreFile(contents, location, parent);
this.isPathIgnored = this.parseIgnoreFile(contents, this.location, this.parent);
}
/**
* Updates the contents of the ignorefile. Preservering the location and parent
* @param contents The new contents of the gitignore file
*/
updateContents(contents: string) {
this.isPathIgnored = this.parseIgnoreFile(contents, this.location, this.parent);
}
/**

View file

@ -2050,8 +2050,8 @@ declare module 'vscode' {
* to the user.
*
* @param value The current value of the input box.
* @return A human-readable string which is presented as diagnostic message.
* Return `undefined`, `null`, or the empty string when 'value' is valid.
* @return Either a human-readable string which is presented as an error message or an {@link InputBoxValidationMessage}
* which can provide a specific message severity. Return `undefined`, `null`, or the empty string when 'value' is valid.
*/
validateInput?(value: string): string | InputBoxValidationMessage | undefined | null |
Thenable<string | InputBoxValidationMessage | undefined | null>;
@ -11125,6 +11125,8 @@ declare module 'vscode' {
/**
* An optional validation message indicating a problem with the current input value.
* By returning a string, the InputBox will use a default {@link InputBoxValidationSeverity} of Error.
* Returning undefined clears the validation message.
*/
validationMessage: string | InputBoxValidationMessage | undefined;
}
@ -16029,7 +16031,7 @@ declare module 'vscode' {
readonly viewColumn: ViewColumn;
/**
* The active {@link Tab tab} in the group. This is the tab which contents are currently
* The active {@link Tab tab} in the group. This is the tab whose contents are currently
* being rendered.
*
* *Note* that there can be one active tab per group but there can only be one {@link TabGroups.activeTabGroup active group}.

View file

@ -28,8 +28,9 @@ export function setup() {
await terminal.runCommandWithValue(TerminalCommandIdWithValue.NewWithProfile, process.platform === 'win32' ? 'PowerShell' : 'bash');
}
describe('Shell integration', function () {
(process.platform === 'linux' || process.platform === 'win32' ? describe.skip : describe)('Decorations', function () {
// TODO: These are currently flaky https://github.com/microsoft/vscode/issues/150478
describe.skip('Shell integration', function () {
describe('Decorations', function () {
describe('Should show default icons', function () {
it('Placeholder', async () => {
await createShellIntegrationProfile();