mirror of
https://github.com/Microsoft/vscode
synced 2024-10-05 19:02:54 +00:00
Merge remote-tracking branch 'origin' into electron-18.x.y
This commit is contained in:
commit
9f56a2f166
2
.github/classifier.json
vendored
2
.github/classifier.json
vendored
|
@ -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": []},
|
||||
|
|
3
.vscode/extensions.json
vendored
3
.vscode/extensions.json
vendored
|
@ -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"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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');
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
},
|
||||
|
|
2
extensions/git/src/api/git.d.ts
vendored
2
extensions/git/src/api/git.d.ts
vendored
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 });
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1103,7 +1103,10 @@
|
|||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "%configuration.tsserver.experimental.enableProjectDiagnostics%",
|
||||
"scope": "window"
|
||||
"scope": "window",
|
||||
"tags": [
|
||||
"experimental"
|
||||
]
|
||||
},
|
||||
"typescript.tsserver.watchOptions": {
|
||||
"type": "object",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "code-oss-dev",
|
||||
"version": "1.68.0",
|
||||
"distro": "cc3412556bc8e8717bfd449c5e767fd16dbc22c7",
|
||||
"distro": "1af1d9e4f6e2609bd0eb3dea3786ef1571338986",
|
||||
"author": {
|
||||
"name": "Microsoft Corporation"
|
||||
},
|
||||
|
|
|
@ -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 */;
|
||||
}
|
||||
|
|
|
@ -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); // · or word separator middle dot
|
||||
sb.write1(0x200C); // ZERO WIDTH NON-JOINER
|
||||
}
|
||||
|
||||
charOffsetInPart += charWidth;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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('');
|
||||
|
||||
|
|
|
@ -1218,7 +1218,6 @@ export interface IFilesConfiguration {
|
|||
autoSaveDelay: number;
|
||||
eol: string;
|
||||
enableTrash: boolean;
|
||||
excludeGitIgnore: boolean;
|
||||
hotExit: string;
|
||||
saveConflictResolution: 'askUser' | 'overwriteFileOnDisk';
|
||||
};
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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> {
|
||||
|
||||
/**
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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] = {};
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:')
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -98,6 +98,7 @@ export interface IFilesConfiguration extends PlatformIFilesConfiguration, IWorkb
|
|||
badges: boolean;
|
||||
};
|
||||
incrementalNaming: 'simple' | 'smart';
|
||||
excludeGitIgnore: boolean;
|
||||
fileNesting: {
|
||||
enabled: boolean;
|
||||
expand: boolean;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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[] {
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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'],
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
8
src/vscode-dts/vscode.d.ts
vendored
8
src/vscode-dts/vscode.d.ts
vendored
|
@ -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}.
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue