Merge branch 'master' into alex/python-language-configuration
This commit is contained in:
commit
c735c8b291
|
@ -5,7 +5,10 @@
|
|||
"ecmaVersion": 6,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": ["@typescript-eslint", "jsdoc"],
|
||||
"plugins": [
|
||||
"@typescript-eslint",
|
||||
"jsdoc"
|
||||
],
|
||||
"rules": {
|
||||
"constructor-super": "warn",
|
||||
"curly": "warn",
|
||||
|
@ -43,7 +46,9 @@
|
|||
"warn",
|
||||
{
|
||||
"selector": "class",
|
||||
"format": ["PascalCase"]
|
||||
"format": [
|
||||
"PascalCase"
|
||||
]
|
||||
}
|
||||
],
|
||||
"code-no-unused-expressions": [
|
||||
|
@ -60,11 +65,26 @@
|
|||
"warn",
|
||||
{
|
||||
"common": [],
|
||||
"node": ["common"],
|
||||
"browser": ["common"],
|
||||
"electron-sandbox": ["common", "browser"],
|
||||
"electron-browser": ["common", "browser", "node", "electron-sandbox"],
|
||||
"electron-main": ["common", "node"]
|
||||
"node": [
|
||||
"common"
|
||||
],
|
||||
"browser": [
|
||||
"common"
|
||||
],
|
||||
"electron-sandbox": [
|
||||
"common",
|
||||
"browser"
|
||||
],
|
||||
"electron-browser": [
|
||||
"common",
|
||||
"browser",
|
||||
"node",
|
||||
"electron-sandbox"
|
||||
],
|
||||
"electron-main": [
|
||||
"common",
|
||||
"node"
|
||||
]
|
||||
}
|
||||
],
|
||||
"code-import-patterns": [
|
||||
|
@ -74,7 +94,10 @@
|
|||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
{
|
||||
"target": "**/vs/base/common/**",
|
||||
"restrictions": ["vs/nls", "**/vs/base/common/**"]
|
||||
"restrictions": [
|
||||
"vs/nls",
|
||||
"**/vs/base/common/**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"target": "**/vs/base/test/common/**",
|
||||
|
@ -432,7 +455,11 @@
|
|||
},
|
||||
{
|
||||
"target": "**/vs/workbench/api/worker/**",
|
||||
"restrictions": ["vscode", "vs/nls", "**/vs/**/{common,worker}/**"]
|
||||
"restrictions": [
|
||||
"vscode",
|
||||
"vs/nls",
|
||||
"**/vs/**/{common,worker}/**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"target": "**/vs/workbench/electron-sandbox/**",
|
||||
|
@ -878,7 +905,13 @@
|
|||
},
|
||||
{
|
||||
"target": "**/api/**.test.ts",
|
||||
"restrictions": ["**/vs/**", "assert", "sinon", "crypto", "vscode"]
|
||||
"restrictions": [
|
||||
"**/vs/**",
|
||||
"assert",
|
||||
"sinon",
|
||||
"crypto",
|
||||
"vscode"
|
||||
]
|
||||
},
|
||||
{
|
||||
"target": "**/{node,electron-browser,electron-main}/**/*.test.ts",
|
||||
|
@ -903,28 +936,46 @@
|
|||
},
|
||||
{
|
||||
"target": "**/**.test.ts",
|
||||
"restrictions": ["**/vs/**", "assert", "sinon", "crypto", "xterm*"]
|
||||
"restrictions": [
|
||||
"**/vs/**",
|
||||
"assert",
|
||||
"sinon",
|
||||
"crypto",
|
||||
"xterm*"
|
||||
]
|
||||
},
|
||||
{
|
||||
"target": "**/test/**",
|
||||
"restrictions": ["**/vs/**", "assert", "sinon", "crypto", "xterm*"]
|
||||
"restrictions": [
|
||||
"**/vs/**",
|
||||
"assert",
|
||||
"sinon",
|
||||
"crypto",
|
||||
"xterm*"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.js"],
|
||||
"files": [
|
||||
"*.js"
|
||||
],
|
||||
"rules": {
|
||||
"jsdoc/no-types": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["**/vscode.d.ts", "**/vscode.proposed.d.ts"],
|
||||
"files": [
|
||||
"**/vscode.d.ts",
|
||||
"**/vscode.proposed.d.ts"
|
||||
],
|
||||
"rules": {
|
||||
"vscode-dts-create-func": "warn",
|
||||
"vscode-dts-literal-or-types": "warn",
|
||||
"vscode-dts-interface-naming": "warn",
|
||||
"vscode-dts-cancellation": "warn",
|
||||
"vscode-dts-use-thenable": "warn",
|
||||
"vscode-dts-provider-naming": [
|
||||
"warn",
|
||||
{
|
||||
|
@ -940,7 +991,10 @@
|
|||
"vscode-dts-event-naming": [
|
||||
"warn",
|
||||
{
|
||||
"allowed": ["onCancellationRequested", "event"],
|
||||
"allowed": [
|
||||
"onCancellationRequested",
|
||||
"event"
|
||||
],
|
||||
"verbs": [
|
||||
"accept",
|
||||
"change",
|
||||
|
|
2
.github/actions/build-chat/.gitignore
vendored
Normal file
2
.github/actions/build-chat/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
dist
|
||||
node_modules
|
10
.github/actions/build-chat/action.yml
vendored
Normal file
10
.github/actions/build-chat/action.yml
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
name: 'Build Chat'
|
||||
description: 'Notify in chat about build results.'
|
||||
author: 'Christof Marti'
|
||||
inputs:
|
||||
workflow_run_url:
|
||||
description: 'Workflow run URL of the completed build.'
|
||||
required: true
|
||||
runs:
|
||||
using: 'node12'
|
||||
main: 'dist/main.js'
|
20
.github/actions/build-chat/package.json
vendored
Normal file
20
.github/actions/build-chat/package.json
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "build-chat",
|
||||
"version": "0.0.0",
|
||||
"author": "Microsoft Corporation",
|
||||
"license": "MIT",
|
||||
"description": "A GitHub action to create a Windows Package Manager manifest file.",
|
||||
"main": "dist/main.js",
|
||||
"scripts": {
|
||||
"build": "tsc"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.2.6",
|
||||
"@octokit/rest": "^18.0.12",
|
||||
"@slack/web-api": "^6.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^14.14.22",
|
||||
"typescript": "^4.1.3"
|
||||
}
|
||||
}
|
183
.github/actions/build-chat/src/main.ts
vendored
Normal file
183
.github/actions/build-chat/src/main.ts
vendored
Normal file
|
@ -0,0 +1,183 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as core from '@actions/core';
|
||||
import { Octokit, RestEndpointMethodTypes } from '@octokit/rest';
|
||||
import { WebClient } from '@slack/web-api';
|
||||
|
||||
(async () => {
|
||||
const actionUrl = core.getInput('workflow_run_url');
|
||||
const url = actionUrl || 'https://api.github.com/repos/microsoft/vscode/actions/runs/500731641';
|
||||
console.log(url);
|
||||
const parts = url.split('/');
|
||||
const owner = parts[parts.length - 5];
|
||||
const repo = parts[parts.length - 4];
|
||||
const runId = parseInt(parts[parts.length - 1], 10);
|
||||
if (actionUrl) {
|
||||
await handleNotification(owner, repo, runId);
|
||||
} else {
|
||||
const results = await buildComplete(owner, repo, runId);
|
||||
for (const message of [...results.logMessages, ...results.messages]) {
|
||||
console.log(message);
|
||||
}
|
||||
}
|
||||
})()
|
||||
.then(null, console.error);
|
||||
|
||||
const testChannels = ['bot-log', 'bot-test-log'];
|
||||
|
||||
async function handleNotification(owner: string, repo: string, runId: number) {
|
||||
|
||||
const results = await buildComplete(owner, repo, runId);
|
||||
if (results.logMessages.length || results.messages.length) {
|
||||
|
||||
const web = new WebClient(process.env.SLACK_TOKEN);
|
||||
const memberships = await listAllMemberships(web);
|
||||
const memberTestChannels = memberships.filter(m => testChannels.indexOf(m.name) !== -1);
|
||||
|
||||
for (const message of results.logMessages) {
|
||||
for (const testChannel of memberTestChannels) {
|
||||
await web.chat.postMessage({
|
||||
text: message,
|
||||
channel: testChannel.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
for (const message of results.messages) {
|
||||
for (const channel of memberships) {
|
||||
await web.chat.postMessage({
|
||||
text: message,
|
||||
channel: channel.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function buildComplete(owner: string, repo: string, runId: number) {
|
||||
console.log(`buildComplete: https://github.com/${owner}/${repo}/actions/runs/${runId}`);
|
||||
const auth = `token ${process.env.GITHUB_TOKEN}`;
|
||||
const octokit = new Octokit({ auth });
|
||||
const buildResult = (await octokit.actions.getWorkflowRun({
|
||||
owner,
|
||||
repo,
|
||||
run_id: runId,
|
||||
})).data;
|
||||
if (buildResult.head_branch !== 'master' && !buildResult.head_branch?.startsWith('release/')) {
|
||||
console.error('Private branch. Terminating.')
|
||||
return { logMessages: [], messages: [] };
|
||||
}
|
||||
|
||||
// const buildQuery = `${buildsApiUrl}?$top=10&maxTime=${buildResult.finishTime}&definitions=${buildResult.definition.id}&branchName=${buildResult.sourceBranch}&resultFilter=${results.join(',')}&api-version=5.0-preview.4`;
|
||||
|
||||
const buildResults = (await octokit.actions.listWorkflowRuns({
|
||||
owner,
|
||||
repo,
|
||||
workflow_id: buildResult.workflow_id,
|
||||
branch: buildResult.head_branch || undefined,
|
||||
per_page: 5, // More returns 502s.
|
||||
})).data.workflow_runs
|
||||
.filter(run => run.status === 'completed');
|
||||
|
||||
const currentBuildIndex = buildResults.findIndex(build => build.id === buildResult.id);
|
||||
if (currentBuildIndex === -1) {
|
||||
console.error('Build not on first page. Terminating.')
|
||||
console.error(buildResults.map(({ id, status, conclusion }) => ({ id, status, conclusion })));
|
||||
return { logMessages: [], messages: [] };
|
||||
}
|
||||
const slicedResults = buildResults.slice(currentBuildIndex, currentBuildIndex + 2);
|
||||
const builds = slicedResults
|
||||
.map<Build>((build, i, array) => ({
|
||||
data: build,
|
||||
previousSourceVersion: i < array.length - 1 ? array[i + 1].head_sha : undefined,
|
||||
authors: [],
|
||||
buildHtmlUrl: build.html_url,
|
||||
changesHtmlUrl: '',
|
||||
}));
|
||||
const logMessages = builds.slice(0, 1)
|
||||
.map(build => `Id: ${build.data.id} | Branch: ${build.data.head_branch} | Conclusion: ${build.data.conclusion} | Created: ${build.data.created_at} | Updated: ${build.data.updated_at}`);
|
||||
const transitionedBuilds = builds.filter((build, i, array) => i < array.length - 1 && transitioned(build, array[i + 1]));
|
||||
await Promise.all(transitionedBuilds
|
||||
.map(async build => {
|
||||
if (build.previousSourceVersion) {
|
||||
const cmp = await compareCommits(octokit, owner, repo, build.previousSourceVersion, build.data.head_sha);
|
||||
const commits = cmp.data.commits;
|
||||
const authors = new Set<string>([
|
||||
...commits.map((c: any) => c.author.login),
|
||||
...commits.map((c: any) => c.committer.login),
|
||||
]);
|
||||
authors.delete('web-flow'); // GitHub Web UI committer
|
||||
build.authors = [...authors];
|
||||
build.changesHtmlUrl = `https://github.com/${owner}/${repo}/compare/${build.previousSourceVersion.substr(0, 7)}...${build.data.head_sha.substr(0, 7)}`; // Shorter than: cmp.data.html_url
|
||||
}
|
||||
}));
|
||||
const vscode = repo === 'vscode';
|
||||
const name = vscode ? `VS Code ${buildResult.name} Build` : buildResult.name;
|
||||
// TBD: `Requester: ${vstsToSlackUser(build.requester, build.degraded)}${pingBenForSmokeTests && releaseBuild && build.result === 'partiallySucceeded' ? ' | Ping: @bpasero' : ''}`
|
||||
const messages = transitionedBuilds.map(build => `${name}
|
||||
Result: ${build.data.conclusion} | Branch: ${build.data.head_branch} | Authors: ${githubToSlackUsers(build.authors, build.degraded).sort().join(', ') || `None (rebuild)`}
|
||||
Build: ${build.buildHtmlUrl}
|
||||
Changes: ${build.changesHtmlUrl}`);
|
||||
return { logMessages, messages };
|
||||
}
|
||||
|
||||
const conclusions = ['success', 'failure']
|
||||
|
||||
function transitioned(newer: Build, older: Build) {
|
||||
const newerResult = newer.data.conclusion || 'success';
|
||||
const olderResult = older.data.conclusion || 'success';
|
||||
if (newerResult === olderResult) {
|
||||
return false;
|
||||
}
|
||||
if (conclusions.indexOf(newerResult) > conclusions.indexOf(olderResult)) {
|
||||
newer.degraded = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async function compareCommits(octokit: Octokit, owner: string, repo: string, base: string, head: string) {
|
||||
return octokit.repos.compareCommits({ owner, repo, base, head });
|
||||
}
|
||||
|
||||
function githubToSlackUsers(githubUsers: string[], at?: boolean) {
|
||||
return githubUsers.map(g => githubToAccounts[g] ? `${at ? '@' : ''}${githubToAccounts[g].slack}` : g);
|
||||
}
|
||||
|
||||
const accounts = [
|
||||
{
|
||||
github: 'tbd',
|
||||
slack: 'tbd'
|
||||
}
|
||||
];
|
||||
|
||||
type Accounts = (typeof accounts)[0];
|
||||
|
||||
const githubToAccounts = accounts.reduce((m, e) => {
|
||||
m[e.github] = e;
|
||||
return m;
|
||||
}, <Record<string, Accounts>>{});
|
||||
|
||||
interface AllChannels {
|
||||
channels: {
|
||||
id: string;
|
||||
name: string;
|
||||
is_member: boolean;
|
||||
}[];
|
||||
}
|
||||
|
||||
async function listAllMemberships(web: WebClient) {
|
||||
const groups = await web.conversations.list({ types: 'public_channel,private_channel' }) as unknown as AllChannels;
|
||||
return groups.channels
|
||||
.filter(c => c.is_member);
|
||||
}
|
||||
|
||||
interface Build {
|
||||
data: RestEndpointMethodTypes['actions']['getWorkflowRun']['response']['data'];
|
||||
previousSourceVersion: string | undefined;
|
||||
authors: string[];
|
||||
buildHtmlUrl: string;
|
||||
changesHtmlUrl: string;
|
||||
degraded?: boolean;
|
||||
}
|
18
.github/actions/build-chat/tsconfig.json
vendored
Normal file
18
.github/actions/build-chat/tsconfig.json
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es2017",
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"resolveJsonModule": true,
|
||||
"lib": [
|
||||
"es2017"
|
||||
],
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
296
.github/actions/build-chat/yarn.lock
vendored
Normal file
296
.github/actions/build-chat/yarn.lock
vendored
Normal file
|
@ -0,0 +1,296 @@
|
|||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@actions/core@^1.2.6":
|
||||
version "1.2.6"
|
||||
resolved "https://registry.yarnpkg.com/@actions/core/-/core-1.2.6.tgz#a78d49f41a4def18e88ce47c2cac615d5694bf09"
|
||||
integrity sha512-ZQYitnqiyBc3D+k7LsgSBmMDVkOVidaagDG7j3fOym77jNunWRuYx7VSHa9GNfFZh+zh61xsCjRj4JxMZlDqTA==
|
||||
|
||||
"@octokit/auth-token@^2.4.4":
|
||||
version "2.4.4"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.4.4.tgz#ee31c69b01d0378c12fd3ffe406030f3d94d3b56"
|
||||
integrity sha512-LNfGu3Ro9uFAYh10MUZVaT7X2CnNm2C8IDQmabx+3DygYIQjs9FwzFAHN/0t6mu5HEPhxcb1XOuxdpY82vCg2Q==
|
||||
dependencies:
|
||||
"@octokit/types" "^6.0.0"
|
||||
|
||||
"@octokit/core@^3.2.3":
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.2.4.tgz#5791256057a962eca972e31818f02454897fd106"
|
||||
integrity sha512-d9dTsqdePBqOn7aGkyRFe7pQpCXdibSJ5SFnrTr0axevObZrpz3qkWm7t/NjYv5a66z6vhfteriaq4FRz3e0Qg==
|
||||
dependencies:
|
||||
"@octokit/auth-token" "^2.4.4"
|
||||
"@octokit/graphql" "^4.5.8"
|
||||
"@octokit/request" "^5.4.12"
|
||||
"@octokit/types" "^6.0.3"
|
||||
before-after-hook "^2.1.0"
|
||||
universal-user-agent "^6.0.0"
|
||||
|
||||
"@octokit/endpoint@^6.0.1":
|
||||
version "6.0.10"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.10.tgz#741ce1fa2f4fb77ce8ebe0c6eaf5ce63f565f8e8"
|
||||
integrity sha512-9+Xef8nT7OKZglfkOMm7IL6VwxXUQyR7DUSU0LH/F7VNqs8vyd7es5pTfz9E7DwUIx7R3pGscxu1EBhYljyu7Q==
|
||||
dependencies:
|
||||
"@octokit/types" "^6.0.0"
|
||||
is-plain-object "^5.0.0"
|
||||
universal-user-agent "^6.0.0"
|
||||
|
||||
"@octokit/graphql@^4.5.8":
|
||||
version "4.5.8"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.5.8.tgz#d42373633c3015d0eafce64a8ce196be167fdd9b"
|
||||
integrity sha512-WnCtNXWOrupfPJgXe+vSmprZJUr0VIu14G58PMlkWGj3cH+KLZEfKMmbUQ6C3Wwx6fdhzVW1CD5RTnBdUHxhhA==
|
||||
dependencies:
|
||||
"@octokit/request" "^5.3.0"
|
||||
"@octokit/types" "^6.0.0"
|
||||
universal-user-agent "^6.0.0"
|
||||
|
||||
"@octokit/openapi-types@^3.0.0":
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-3.0.0.tgz#f73d48af2d21bf4f97fbf38fae43b54699e0dbba"
|
||||
integrity sha512-jOp1CVRw+OBJaZtG9QzZggvJXvyzgDXuW948SWsDiwmyDuCjeYCiF3TDD/qvhpF580RfP7iBIos4AVU6yhgMlA==
|
||||
|
||||
"@octokit/plugin-paginate-rest@^2.6.2":
|
||||
version "2.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.8.0.tgz#2b41e12b494e895bf5fb5b12565d2c80a0ecc6ae"
|
||||
integrity sha512-HtuEQ2AYE4YFEBQN0iHmMsIvVucd5RsnwJmRKIsfAg1/ZeoMaU+jXMnTAZqIUEmcVJA27LjHUm3f1hxf8Fpdxw==
|
||||
dependencies:
|
||||
"@octokit/types" "^6.4.0"
|
||||
|
||||
"@octokit/plugin-request-log@^1.0.2":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.2.tgz#394d59ec734cd2f122431fbaf05099861ece3c44"
|
||||
integrity sha512-oTJSNAmBqyDR41uSMunLQKMX0jmEXbwD1fpz8FG27lScV3RhtGfBa1/BBLym+PxcC16IBlF7KH9vP1BUYxA+Eg==
|
||||
|
||||
"@octokit/plugin-rest-endpoint-methods@4.4.1":
|
||||
version "4.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.4.1.tgz#105cf93255432155de078c9efc33bd4e14d1cd63"
|
||||
integrity sha512-+v5PcvrUcDeFXf8hv1gnNvNLdm4C0+2EiuWt9EatjjUmfriM1pTMM+r4j1lLHxeBQ9bVDmbywb11e3KjuavieA==
|
||||
dependencies:
|
||||
"@octokit/types" "^6.1.0"
|
||||
deprecation "^2.3.1"
|
||||
|
||||
"@octokit/request-error@^2.0.0":
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.0.4.tgz#07dd5c0521d2ee975201274c472a127917741262"
|
||||
integrity sha512-LjkSiTbsxIErBiRh5wSZvpZqT4t0/c9+4dOe0PII+6jXR+oj/h66s7E4a/MghV7iT8W9ffoQ5Skoxzs96+gBPA==
|
||||
dependencies:
|
||||
"@octokit/types" "^6.0.0"
|
||||
deprecation "^2.0.0"
|
||||
once "^1.4.0"
|
||||
|
||||
"@octokit/request@^5.3.0", "@octokit/request@^5.4.12":
|
||||
version "5.4.12"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.4.12.tgz#b04826fa934670c56b135a81447be2c1723a2ffc"
|
||||
integrity sha512-MvWYdxengUWTGFpfpefBBpVmmEYfkwMoxonIB3sUGp5rhdgwjXL1ejo6JbgzG/QD9B/NYt/9cJX1pxXeSIUCkg==
|
||||
dependencies:
|
||||
"@octokit/endpoint" "^6.0.1"
|
||||
"@octokit/request-error" "^2.0.0"
|
||||
"@octokit/types" "^6.0.3"
|
||||
deprecation "^2.0.0"
|
||||
is-plain-object "^5.0.0"
|
||||
node-fetch "^2.6.1"
|
||||
once "^1.4.0"
|
||||
universal-user-agent "^6.0.0"
|
||||
|
||||
"@octokit/rest@^18.0.12":
|
||||
version "18.0.12"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.0.12.tgz#278bd41358c56d87c201e787e8adc0cac132503a"
|
||||
integrity sha512-hNRCZfKPpeaIjOVuNJzkEL6zacfZlBPV8vw8ReNeyUkVvbuCvvrrx8K8Gw2eyHHsmd4dPlAxIXIZ9oHhJfkJpw==
|
||||
dependencies:
|
||||
"@octokit/core" "^3.2.3"
|
||||
"@octokit/plugin-paginate-rest" "^2.6.2"
|
||||
"@octokit/plugin-request-log" "^1.0.2"
|
||||
"@octokit/plugin-rest-endpoint-methods" "4.4.1"
|
||||
|
||||
"@octokit/types@^6.0.0", "@octokit/types@^6.0.3", "@octokit/types@^6.1.0", "@octokit/types@^6.4.0":
|
||||
version "6.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.4.0.tgz#f3f47be70bcdb3c26f2c2619f3dd0ced466a265c"
|
||||
integrity sha512-1FEmuVppZE2zG0rBdQlviRz5cp0udyI63zyhBVPrm0FRNAsQkAXU7IYWQg1XvhChFut8YbFYN1usQpk54D6/4w==
|
||||
dependencies:
|
||||
"@octokit/openapi-types" "^3.0.0"
|
||||
"@types/node" ">= 8"
|
||||
|
||||
"@slack/logger@>=1.0.0 <3.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@slack/logger/-/logger-2.0.0.tgz#6a4e1c755849bc0f66dac08a8be54ce790ec0e6b"
|
||||
integrity sha512-OkIJpiU2fz6HOJujhlhfIGrc8hB4ibqtf7nnbJQDerG0BqwZCfmgtK5sWzZ0TkXVRBKD5MpLrTmCYyMxoMCgPw==
|
||||
dependencies:
|
||||
"@types/node" ">=8.9.0"
|
||||
|
||||
"@slack/types@^1.7.0":
|
||||
version "1.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@slack/types/-/types-1.10.0.tgz#cbf7d83e1027f4cbfd13d6b429f120c7fb09127a"
|
||||
integrity sha512-tA7GG7Tj479vojfV3AoxbckalA48aK6giGjNtgH6ihpLwTyHE3fIgRrvt8TWfLwW8X8dyu7vgmAsGLRG7hWWOg==
|
||||
|
||||
"@slack/web-api@^6.0.0":
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@slack/web-api/-/web-api-6.0.0.tgz#14c65ed73c66a187e5f20e12c3898dfd8d5cbf7c"
|
||||
integrity sha512-YD1wqWuzrYPf4RQyD7OnYS5lImUmNWn+G5V6Qt0N97fPYxqhT72YJtRdSnsTc3VkH5R5imKOhYxb+wqI9hiHnA==
|
||||
dependencies:
|
||||
"@slack/logger" ">=1.0.0 <3.0.0"
|
||||
"@slack/types" "^1.7.0"
|
||||
"@types/is-stream" "^1.1.0"
|
||||
"@types/node" ">=12.0.0"
|
||||
axios "^0.21.1"
|
||||
eventemitter3 "^3.1.0"
|
||||
form-data "^2.5.0"
|
||||
is-stream "^1.1.0"
|
||||
p-queue "^6.6.1"
|
||||
p-retry "^4.0.0"
|
||||
|
||||
"@types/is-stream@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/is-stream/-/is-stream-1.1.0.tgz#b84d7bb207a210f2af9bed431dc0fbe9c4143be1"
|
||||
integrity sha512-jkZatu4QVbR60mpIzjINmtS1ZF4a/FqdTUTBeQDVOQ2PYyidtwFKr0B5G6ERukKwliq+7mIXvxyppwzG5EgRYg==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/node@*", "@types/node@>= 8", "@types/node@>=12.0.0", "@types/node@>=8.9.0", "@types/node@^14.14.22":
|
||||
version "14.14.22"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.22.tgz#0d29f382472c4ccf3bd96ff0ce47daf5b7b84b18"
|
||||
integrity sha512-g+f/qj/cNcqKkc3tFqlXOYjrmZA+jNBiDzbP3kH+B+otKFqAdPgVTGP1IeKRdMml/aE69as5S4FqtxAbl+LaMw==
|
||||
|
||||
"@types/retry@^0.12.0":
|
||||
version "0.12.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d"
|
||||
integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==
|
||||
|
||||
asynckit@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
|
||||
integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
|
||||
|
||||
axios@^0.21.1:
|
||||
version "0.21.1"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
|
||||
integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==
|
||||
dependencies:
|
||||
follow-redirects "^1.10.0"
|
||||
|
||||
before-after-hook@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.1.0.tgz#b6c03487f44e24200dd30ca5e6a1979c5d2fb635"
|
||||
integrity sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A==
|
||||
|
||||
combined-stream@^1.0.6:
|
||||
version "1.0.8"
|
||||
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
|
||||
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
|
||||
dependencies:
|
||||
delayed-stream "~1.0.0"
|
||||
|
||||
delayed-stream@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
|
||||
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
|
||||
|
||||
deprecation@^2.0.0, deprecation@^2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919"
|
||||
integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==
|
||||
|
||||
eventemitter3@^3.1.0:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7"
|
||||
integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==
|
||||
|
||||
eventemitter3@^4.0.4:
|
||||
version "4.0.7"
|
||||
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
|
||||
integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
|
||||
|
||||
follow-redirects@^1.10.0:
|
||||
version "1.13.1"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.1.tgz#5f69b813376cee4fd0474a3aba835df04ab763b7"
|
||||
integrity sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg==
|
||||
|
||||
form-data@^2.5.0:
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4"
|
||||
integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==
|
||||
dependencies:
|
||||
asynckit "^0.4.0"
|
||||
combined-stream "^1.0.6"
|
||||
mime-types "^2.1.12"
|
||||
|
||||
is-plain-object@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344"
|
||||
integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==
|
||||
|
||||
is-stream@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
|
||||
integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
|
||||
|
||||
mime-db@1.45.0:
|
||||
version "1.45.0"
|
||||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.45.0.tgz#cceeda21ccd7c3a745eba2decd55d4b73e7879ea"
|
||||
integrity sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==
|
||||
|
||||
mime-types@^2.1.12:
|
||||
version "2.1.28"
|
||||
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.28.tgz#1160c4757eab2c5363888e005273ecf79d2a0ecd"
|
||||
integrity sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ==
|
||||
dependencies:
|
||||
mime-db "1.45.0"
|
||||
|
||||
node-fetch@^2.6.1:
|
||||
version "2.6.1"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
||||
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
|
||||
|
||||
once@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
|
||||
dependencies:
|
||||
wrappy "1"
|
||||
|
||||
p-finally@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
|
||||
integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=
|
||||
|
||||
p-queue@^6.6.1:
|
||||
version "6.6.2"
|
||||
resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-6.6.2.tgz#2068a9dcf8e67dd0ec3e7a2bcb76810faa85e426"
|
||||
integrity sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==
|
||||
dependencies:
|
||||
eventemitter3 "^4.0.4"
|
||||
p-timeout "^3.2.0"
|
||||
|
||||
p-retry@^4.0.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.2.0.tgz#ea9066c6b44f23cab4cd42f6147cdbbc6604da5d"
|
||||
integrity sha512-jPH38/MRh263KKcq0wBNOGFJbm+U6784RilTmHjB/HM9kH9V8WlCpVUcdOmip9cjXOh6MxZ5yk1z2SjDUJfWmA==
|
||||
dependencies:
|
||||
"@types/retry" "^0.12.0"
|
||||
retry "^0.12.0"
|
||||
|
||||
p-timeout@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe"
|
||||
integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==
|
||||
dependencies:
|
||||
p-finally "^1.0.0"
|
||||
|
||||
retry@^0.12.0:
|
||||
version "0.12.0"
|
||||
resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b"
|
||||
integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=
|
||||
|
||||
typescript@^4.1.3:
|
||||
version "4.1.3"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.3.tgz#519d582bd94cba0cf8934c7d8e8467e473f53bb7"
|
||||
integrity sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==
|
||||
|
||||
universal-user-agent@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee"
|
||||
integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==
|
||||
|
||||
wrappy@1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
|
35
.github/workflows/build-chat.yml
vendored
Normal file
35
.github/workflows/build-chat.yml
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
name: "Build Chat"
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows:
|
||||
- CI
|
||||
types:
|
||||
- completed
|
||||
branches:
|
||||
- master
|
||||
- release/*
|
||||
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup Node.js 12.x
|
||||
uses: actions/setup-node@v1.4.4
|
||||
with:
|
||||
node-version: "12.x"
|
||||
|
||||
- name: Build
|
||||
run: yarn install && yarn run build
|
||||
working-directory: .github/actions/build-chat
|
||||
|
||||
- name: Build Chat
|
||||
uses: ./.github/actions/build-chat
|
||||
with:
|
||||
workflow_run_url: ${{ github.event.workflow_run.url }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SLACK_TOKEN: ${{ secrets.SLACK_TOKEN }}
|
29
.github/workflows/ci.yml
vendored
29
.github/workflows/ci.yml
vendored
|
@ -32,13 +32,15 @@ jobs:
|
|||
- name: Compute node modules cache key
|
||||
id: nodeModulesCacheKey
|
||||
run: echo "::set-output name=value::$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)"
|
||||
- name: Cache node modules
|
||||
- name: Cache node_modules archive
|
||||
id: cacheNodeModules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.os }}-cacheNodeModules10-${{ steps.nodeModulesCacheKey.outputs.value }}
|
||||
restore-keys: ${{ runner.os }}-cacheNodeModules10-
|
||||
path: ".build/node_modules_cache"
|
||||
key: "${{ runner.os }}-cacheNodeModulesArchive-${{ steps.nodeModulesCacheKey.outputs.value }}"
|
||||
- name: Extract node_modules archive
|
||||
if: ${{ steps.cacheNodeModules.outputs.cache-hit == 'true' }}
|
||||
run: 7z.exe x .build/node_modules_cache/cache.7z -aos
|
||||
- name: Get yarn cache directory path
|
||||
id: yarnCacheDirPath
|
||||
if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }}
|
||||
|
@ -56,6 +58,13 @@ jobs:
|
|||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
|
||||
ELECTRON_SKIP_BINARY_DOWNLOAD: 1
|
||||
run: yarn --frozen-lockfile --network-timeout 180000
|
||||
- name: Create node_modules archive
|
||||
if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }}
|
||||
run: |
|
||||
mkdir -Force .build
|
||||
node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt
|
||||
mkdir -Force .build/node_modules_cache
|
||||
7z.exe a .build/node_modules_cache/cache.7z -mx3 `@.build/node_modules_list.txt
|
||||
|
||||
- name: Compile and Download
|
||||
run: yarn npm-run-all --max_old_space_size=4095 -lp compile "electron x64" playwright-install download-builtin-extensions
|
||||
|
@ -100,8 +109,8 @@ jobs:
|
|||
uses: actions/cache@v2
|
||||
with:
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.os }}-cacheNodeModules10-${{ steps.nodeModulesCacheKey.outputs.value }}
|
||||
restore-keys: ${{ runner.os }}-cacheNodeModules10-
|
||||
key: ${{ runner.os }}-cacheNodeModules11-${{ steps.nodeModulesCacheKey.outputs.value }}
|
||||
restore-keys: ${{ runner.os }}-cacheNodeModules11-
|
||||
- name: Get yarn cache directory path
|
||||
id: yarnCacheDirPath
|
||||
if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }}
|
||||
|
@ -156,8 +165,8 @@ jobs:
|
|||
uses: actions/cache@v2
|
||||
with:
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.os }}-cacheNodeModules10-${{ steps.nodeModulesCacheKey.outputs.value }}
|
||||
restore-keys: ${{ runner.os }}-cacheNodeModules10-
|
||||
key: ${{ runner.os }}-cacheNodeModules11-${{ steps.nodeModulesCacheKey.outputs.value }}
|
||||
restore-keys: ${{ runner.os }}-cacheNodeModules11-
|
||||
- name: Get yarn cache directory path
|
||||
id: yarnCacheDirPath
|
||||
if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }}
|
||||
|
@ -209,8 +218,8 @@ jobs:
|
|||
uses: actions/cache@v2
|
||||
with:
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.os }}-cacheNodeModules10-${{ steps.nodeModulesCacheKey.outputs.value }}
|
||||
restore-keys: ${{ runner.os }}-cacheNodeModules10-
|
||||
key: ${{ runner.os }}-cacheNodeModules11-${{ steps.nodeModulesCacheKey.outputs.value }}
|
||||
restore-keys: ${{ runner.os }}-cacheNodeModules11-
|
||||
- name: Get yarn cache directory path
|
||||
id: yarnCacheDirPath
|
||||
if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }}
|
||||
|
|
14
.vscode/notebooks/notebook-papercuts.github-issues
vendored
Normal file
14
.vscode/notebooks/notebook-papercuts.github-issues
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
[
|
||||
{
|
||||
"kind": 1,
|
||||
"language": "markdown",
|
||||
"value": "## Native Notebook Paper Cuts\n\nThis notebook serves as an ongoing collection of paper cut issues that we encounter while dogfooding native notebooks. With that in mind only promote issues that really turn you off when using notebooks, e.g issues that make you wanna stop using native notebooks. To mark an issue (bug, feature-request, etc) as paper cut add the labels: `notebook` _and_ `papercut :drop_of_blood:`",
|
||||
"editable": true
|
||||
},
|
||||
{
|
||||
"kind": 2,
|
||||
"language": "github-issues",
|
||||
"value": "repo:microsoft/vscode is:open label:notebook label:\"papercut :drop_of_blood:\"",
|
||||
"editable": true
|
||||
}
|
||||
]
|
|
@ -9,7 +9,7 @@ const es = require("event-stream");
|
|||
const vfs = require("vinyl-fs");
|
||||
const util = require("../lib/util");
|
||||
// @ts-ignore
|
||||
const deps = require("../dependencies");
|
||||
const deps = require("../lib/dependencies");
|
||||
const azure = require('gulp-azure-storage');
|
||||
const root = path.dirname(path.dirname(__dirname));
|
||||
const commit = util.getVersion(root);
|
||||
|
|
|
@ -11,7 +11,7 @@ import * as Vinyl from 'vinyl';
|
|||
import * as vfs from 'vinyl-fs';
|
||||
import * as util from '../lib/util';
|
||||
// @ts-ignore
|
||||
import * as deps from '../dependencies';
|
||||
import * as deps from '../lib/dependencies';
|
||||
const azure = require('gulp-azure-storage');
|
||||
|
||||
const root = path.dirname(path.dirname(__dirname));
|
||||
|
|
|
@ -26,7 +26,7 @@ const packageJson = require('../package.json');
|
|||
const product = require('../product.json');
|
||||
const crypto = require('crypto');
|
||||
const i18n = require('./lib/i18n');
|
||||
const { getProductionDependencies } = require('./dependencies');
|
||||
const { getProductionDependencies } = require('./lib/dependencies');
|
||||
const { config } = require('./lib/electron');
|
||||
const createAsar = require('./lib/asar').createAsar;
|
||||
const minimist = require('minimist');
|
||||
|
|
60
build/lib/dependencies.js
Normal file
60
build/lib/dependencies.js
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getProductionDependencies = void 0;
|
||||
const path = require("path");
|
||||
const cp = require("child_process");
|
||||
const _ = require("underscore");
|
||||
const parseSemver = require('parse-semver');
|
||||
function asYarnDependency(prefix, tree) {
|
||||
let parseResult;
|
||||
try {
|
||||
parseResult = parseSemver(tree.name);
|
||||
}
|
||||
catch (err) {
|
||||
err.message += `: ${tree.name}`;
|
||||
console.warn(`Could not parse semver: ${tree.name}`);
|
||||
return null;
|
||||
}
|
||||
// not an actual dependency in disk
|
||||
if (parseResult.version !== parseResult.range) {
|
||||
return null;
|
||||
}
|
||||
const name = parseResult.name;
|
||||
const version = parseResult.version;
|
||||
const dependencyPath = path.join(prefix, name);
|
||||
const children = [];
|
||||
for (const child of (tree.children || [])) {
|
||||
const dep = asYarnDependency(path.join(prefix, name, 'node_modules'), child);
|
||||
if (dep) {
|
||||
children.push(dep);
|
||||
}
|
||||
}
|
||||
return { name, version, path: dependencyPath, children };
|
||||
}
|
||||
function getYarnProductionDependencies(cwd) {
|
||||
const raw = cp.execSync('yarn list --json', { cwd, encoding: 'utf8', env: Object.assign(Object.assign({}, process.env), { NODE_ENV: 'production' }), stdio: [null, null, 'inherit'] });
|
||||
const match = /^{"type":"tree".*$/m.exec(raw);
|
||||
if (!match || match.length !== 1) {
|
||||
throw new Error('Could not parse result of `yarn list --json`');
|
||||
}
|
||||
const trees = JSON.parse(match[0]).data.trees;
|
||||
return trees
|
||||
.map(tree => asYarnDependency(path.join(cwd, 'node_modules'), tree))
|
||||
.filter((dep) => !!dep);
|
||||
}
|
||||
function getProductionDependencies(cwd) {
|
||||
const result = [];
|
||||
const deps = getYarnProductionDependencies(cwd);
|
||||
const flatten = (dep) => { result.push({ name: dep.name, version: dep.version, path: dep.path }); dep.children.forEach(flatten); };
|
||||
deps.forEach(flatten);
|
||||
return _.uniq(result);
|
||||
}
|
||||
exports.getProductionDependencies = getProductionDependencies;
|
||||
if (require.main === module) {
|
||||
const root = path.dirname(path.dirname(__dirname));
|
||||
console.log(JSON.stringify(getProductionDependencies(root), null, ' '));
|
||||
}
|
|
@ -5,12 +5,27 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
import * as path from 'path';
|
||||
import * as cp from 'child_process';
|
||||
import * as _ from 'underscore';
|
||||
const parseSemver = require('parse-semver');
|
||||
const cp = require('child_process');
|
||||
const _ = require('underscore');
|
||||
|
||||
function asYarnDependency(prefix, tree) {
|
||||
interface Tree {
|
||||
readonly name: string;
|
||||
readonly children?: Tree[];
|
||||
}
|
||||
|
||||
interface FlatDependency {
|
||||
readonly name: string;
|
||||
readonly version: string;
|
||||
readonly path: string;
|
||||
}
|
||||
|
||||
interface Dependency extends FlatDependency {
|
||||
readonly children: Dependency[];
|
||||
}
|
||||
|
||||
function asYarnDependency(prefix: string, tree: Tree): Dependency | null {
|
||||
let parseResult;
|
||||
|
||||
try {
|
||||
|
@ -42,7 +57,7 @@ function asYarnDependency(prefix, tree) {
|
|||
return { name, version, path: dependencyPath, children };
|
||||
}
|
||||
|
||||
function getYarnProductionDependencies(cwd) {
|
||||
function getYarnProductionDependencies(cwd: string): Dependency[] {
|
||||
const raw = cp.execSync('yarn list --json', { cwd, encoding: 'utf8', env: { ...process.env, NODE_ENV: 'production' }, stdio: [null, null, 'inherit'] });
|
||||
const match = /^{"type":"tree".*$/m.exec(raw);
|
||||
|
||||
|
@ -50,25 +65,22 @@ function getYarnProductionDependencies(cwd) {
|
|||
throw new Error('Could not parse result of `yarn list --json`');
|
||||
}
|
||||
|
||||
const trees = JSON.parse(match[0]).data.trees;
|
||||
const trees = JSON.parse(match[0]).data.trees as Tree[];
|
||||
|
||||
return trees
|
||||
.map(tree => asYarnDependency(path.join(cwd, 'node_modules'), tree))
|
||||
.filter(dep => !!dep);
|
||||
.filter<Dependency>((dep): dep is Dependency => !!dep);
|
||||
}
|
||||
|
||||
function getProductionDependencies(cwd) {
|
||||
const result = [];
|
||||
export function getProductionDependencies(cwd: string): FlatDependency[] {
|
||||
const result: FlatDependency[] = [];
|
||||
const deps = getYarnProductionDependencies(cwd);
|
||||
const flatten = dep => { result.push({ name: dep.name, version: dep.version, path: dep.path }); dep.children.forEach(flatten); };
|
||||
const flatten = (dep: Dependency) => { result.push({ name: dep.name, version: dep.version, path: dep.path }); dep.children.forEach(flatten); };
|
||||
deps.forEach(flatten);
|
||||
|
||||
return _.uniq(result);
|
||||
}
|
||||
|
||||
module.exports.getProductionDependencies = getProductionDependencies;
|
||||
|
||||
if (require.main === module) {
|
||||
const root = path.dirname(__dirname);
|
||||
const root = path.dirname(path.dirname(__dirname));
|
||||
console.log(JSON.stringify(getProductionDependencies(root), null, ' '));
|
||||
}
|
24
build/lib/eslint/vscode-dts-use-thenable.js
Normal file
24
build/lib/eslint/vscode-dts-use-thenable.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
"use strict";
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
module.exports = new class ApiEventNaming {
|
||||
constructor() {
|
||||
this.meta = {
|
||||
messages: {
|
||||
usage: 'Use the Thenable-type instead of the Promise type',
|
||||
}
|
||||
};
|
||||
}
|
||||
create(context) {
|
||||
return {
|
||||
['TSTypeAnnotation TSTypeReference Identifier[name="Promise"]']: (node) => {
|
||||
context.report({
|
||||
node,
|
||||
messageId: 'usage',
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
30
build/lib/eslint/vscode-dts-use-thenable.ts
Normal file
30
build/lib/eslint/vscode-dts-use-thenable.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as eslint from 'eslint';
|
||||
|
||||
export = new class ApiEventNaming implements eslint.Rule.RuleModule {
|
||||
|
||||
readonly meta: eslint.Rule.RuleMetaData = {
|
||||
messages: {
|
||||
usage: 'Use the Thenable-type instead of the Promise type',
|
||||
}
|
||||
};
|
||||
|
||||
create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener {
|
||||
|
||||
|
||||
|
||||
return {
|
||||
['TSTypeAnnotation TSTypeReference Identifier[name="Promise"]']: (node: any) => {
|
||||
|
||||
context.report({
|
||||
node,
|
||||
messageId: 'usage',
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
|
@ -269,6 +269,11 @@ export class ApiImpl implements API {
|
|||
return this.getRepository(root) || null;
|
||||
}
|
||||
|
||||
async openRepository(root: Uri): Promise<Repository | null> {
|
||||
await this._model.openRepository(root.fsPath);
|
||||
return this.getRepository(root) || null;
|
||||
}
|
||||
|
||||
registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable {
|
||||
return this._model.registerRemoteSourceProvider(provider);
|
||||
}
|
||||
|
|
1
extensions/git/src/api/git.d.ts
vendored
1
extensions/git/src/api/git.d.ts
vendored
|
@ -254,6 +254,7 @@ export interface API {
|
|||
toGitUri(uri: Uri, ref: string): Uri;
|
||||
getRepository(uri: Uri): Repository | null;
|
||||
init(root: Uri): Promise<Repository | null>;
|
||||
openRepository(root: Uri): Promise<Repository | null>
|
||||
|
||||
registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable;
|
||||
registerCredentialsProvider(provider: CredentialsProvider): Disposable;
|
||||
|
|
|
@ -467,7 +467,7 @@ export class Git {
|
|||
|
||||
try {
|
||||
const networkPath = await new Promise<string | undefined>(resolve =>
|
||||
realpath.native(`${letter}:`, { encoding: 'utf8' }, (err, resolvedPath) =>
|
||||
realpath.native(`${letter}:\\`, { encoding: 'utf8' }, (err, resolvedPath) =>
|
||||
resolve(err !== null ? undefined : resolvedPath),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -1369,7 +1369,7 @@ export class Repository implements Disposable {
|
|||
await this.repository.fetch({ all: true });
|
||||
}
|
||||
|
||||
if (await this.checkIfMaybeRebased(branch)) {
|
||||
if (await this.checkIfMaybeRebased(this.HEAD?.name)) {
|
||||
await this.repository.pull(rebase, remote, branch, { unshallow, tags });
|
||||
}
|
||||
});
|
||||
|
@ -1440,7 +1440,7 @@ export class Repository implements Disposable {
|
|||
await this.repository.fetch({ all: true, cancellationToken });
|
||||
}
|
||||
|
||||
if (await this.checkIfMaybeRebased(pullBranch)) {
|
||||
if (await this.checkIfMaybeRebased(this.HEAD?.name)) {
|
||||
await this.repository.pull(rebase, remoteName, pullBranch, { tags, cancellationToken });
|
||||
}
|
||||
};
|
||||
|
@ -1472,7 +1472,7 @@ export class Repository implements Disposable {
|
|||
});
|
||||
}
|
||||
|
||||
private async checkIfMaybeRebased(branch?: string) {
|
||||
private async checkIfMaybeRebased(currentBranch?: string) {
|
||||
const config = workspace.getConfiguration('git');
|
||||
const shouldIgnore = config.get<boolean>('ignoreRebaseWarning') === true;
|
||||
|
||||
|
@ -1481,12 +1481,16 @@ export class Repository implements Disposable {
|
|||
}
|
||||
|
||||
const maybeRebased = await this.run(Operation.Log, async () => {
|
||||
const result = await this.repository.run(['log', '--oneline', '--cherry', `${branch ?? ''}...${branch ?? ''}@{upstream}`, '--']);
|
||||
if (result.exitCode) {
|
||||
try {
|
||||
const result = await this.repository.run(['log', '--oneline', '--cherry', `${currentBranch ?? ''}...${currentBranch ?? ''}@{upstream}`, '--']);
|
||||
if (result.exitCode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return /^=/.test(result.stdout);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
|
||||
return /^=/.test(result.stdout);
|
||||
});
|
||||
|
||||
if (!maybeRebased) {
|
||||
|
@ -1497,9 +1501,9 @@ export class Repository implements Disposable {
|
|||
const pull = { title: localize('pull', "Pull") };
|
||||
const cancel = { title: localize('dont pull', "Don't Pull") };
|
||||
const result = await window.showWarningMessage(
|
||||
branch
|
||||
? localize('pull branch maybe rebased', "It looks like branch \'{0}\' might have been rebased. Are you sure you still want to pull?", branch)
|
||||
: localize('pull maybe rebased', "It looks like the current branch might have been rebased. Are you sure you still want to pull?"),
|
||||
currentBranch
|
||||
? localize('pull branch maybe rebased', "It looks like the current branch \'{0}\' might have been rebased. Are you sure you still want to pull into it?", currentBranch)
|
||||
: localize('pull maybe rebased', "It looks like the current branch might have been rebased. Are you sure you still want to pull into it?"),
|
||||
always, pull, cancel
|
||||
);
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ export class Keychain {
|
|||
constructor(private context: vscode.ExtensionContext) { }
|
||||
async setToken(token: string): Promise<void> {
|
||||
try {
|
||||
return await this.context.secrets.set(SERVICE_ID, token);
|
||||
return await this.context.secrets.store(SERVICE_ID, token);
|
||||
} catch (e) {
|
||||
// Ignore
|
||||
Logger.error(`Setting token failed: ${e}`);
|
||||
|
|
|
@ -48,7 +48,7 @@ export class Keychain {
|
|||
async setToken(token: string): Promise<void> {
|
||||
|
||||
try {
|
||||
return await this.context.secrets.set(SERVICE_ID, token);
|
||||
return await this.context.secrets.store(SERVICE_ID, token);
|
||||
} catch (e) {
|
||||
Logger.error(`Setting token failed: ${e}`);
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ const jsTsLanguageConfiguration: vscode.LanguageConfiguration = {
|
|||
}, {
|
||||
// e.g. * ...|
|
||||
beforeText: /^(\t|[ ])*[ ]\*([ ]([^\*]|\*(?!\/))*)?$/,
|
||||
oneLineAboveText: /(?=^(\s*(\/\*\*|\*)).*)(?=(?!(\s*\*\/)))/,
|
||||
previousLineText: /(?=^(\s*(\/\*\*|\*)).*)(?=(?!(\s*\*\/)))/,
|
||||
action: { indentAction: vscode.IndentAction.None, appendText: '* ' },
|
||||
}, {
|
||||
// e.g. */|
|
||||
|
|
|
@ -215,7 +215,8 @@ export function activate(context: vscode.ExtensionContext) {
|
|||
}, (progress) => doResolve(_authority, progress));
|
||||
},
|
||||
tunnelFactory,
|
||||
tunnelFeatures: { elevation: true, public: false }
|
||||
tunnelFeatures: { elevation: true, public: false },
|
||||
showCandidatePort: async (_host, port) => port === 100
|
||||
});
|
||||
context.subscriptions.push(authorityResolverDisposable);
|
||||
|
||||
|
@ -316,18 +317,47 @@ function getConfiguration<T>(id: string): T | undefined {
|
|||
}
|
||||
|
||||
function tunnelFactory(tunnelOptions: vscode.TunnelOptions): Promise<vscode.Tunnel> | undefined {
|
||||
const onDidDispose: vscode.EventEmitter<void> = new vscode.EventEmitter();
|
||||
let isDisposed = false;
|
||||
return Promise.resolve({
|
||||
localAddress: { host: 'localhost', port: (tunnelOptions.localAddressPort === undefined ? tunnelOptions.remoteAddress.port : tunnelOptions.localAddressPort) + 1 },
|
||||
remoteAddress: tunnelOptions.remoteAddress,
|
||||
public: tunnelOptions.public,
|
||||
onDidDispose: onDidDispose.event,
|
||||
dispose: () => {
|
||||
if (!isDisposed) {
|
||||
isDisposed = true;
|
||||
onDidDispose.fire();
|
||||
let remotePort = tunnelOptions.remoteAddress.port;
|
||||
if (remotePort === 100) {
|
||||
return createTunnelServer();
|
||||
} else {
|
||||
const port: number = (tunnelOptions.localAddressPort || remotePort) + 1;
|
||||
const dummyTunnel = newTunnel({ host: 'localhost', port });
|
||||
return Promise.resolve(dummyTunnel);
|
||||
|
||||
}
|
||||
|
||||
function newTunnel(localAddress: { host: string, port: number }) {
|
||||
const onDidDispose: vscode.EventEmitter<void> = new vscode.EventEmitter();
|
||||
let isDisposed = false;
|
||||
return {
|
||||
localAddress,
|
||||
remoteAddress: tunnelOptions.remoteAddress,
|
||||
public: tunnelOptions.public,
|
||||
onDidDispose: onDidDispose.event,
|
||||
dispose: () => {
|
||||
if (!isDisposed) {
|
||||
isDisposed = true;
|
||||
onDidDispose.fire();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function createTunnelServer(): Promise<vscode.Tunnel> {
|
||||
return new Promise<vscode.Tunnel>((res, _rej) => {
|
||||
const proxyServer = net.createServer(proxySocket => {
|
||||
outputChannel.appendLine(`Connection accepted`);
|
||||
const remoteSocket = net.createConnection({ host: tunnelOptions.remoteAddress.host, port: tunnelOptions.remoteAddress.port });
|
||||
remoteSocket.pipe(proxySocket);
|
||||
proxySocket.pipe(remoteSocket);
|
||||
});
|
||||
proxyServer.listen(tunnelOptions.localAddressPort === undefined ? 0 : tunnelOptions.localAddressPort, () => {
|
||||
const localPort = (<net.AddressInfo>proxyServer.address()).port;
|
||||
console.log(`New tunnel server: Remote ${tunnelOptions.remoteAddress.port} -> local ${localPort}`);
|
||||
const tunnel = newTunnel({ host: 'localhost', port: localPort });
|
||||
res(tunnel);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "code-oss-dev",
|
||||
"version": "1.53.0",
|
||||
"distro": "d714854350ee61de7f105af6bb54b64404be26e2",
|
||||
"distro": "9743d4c538f650da5636ce1b2994c719dfa8e953",
|
||||
"author": {
|
||||
"name": "Microsoft Corporation"
|
||||
},
|
||||
|
|
|
@ -21,6 +21,7 @@ const vfs = require('vinyl-fs');
|
|||
const uuid = require('uuid');
|
||||
|
||||
const extensions = require('../../build/lib/extensions');
|
||||
const { getBuiltInExtensions } = require('../../build/lib/builtInExtensions');
|
||||
|
||||
const APP_ROOT = path.join(__dirname, '..', '..');
|
||||
const BUILTIN_EXTENSIONS_ROOT = path.join(APP_ROOT, 'extensions');
|
||||
|
@ -90,6 +91,8 @@ const exists = (path) => util.promisify(fs.exists)(path);
|
|||
const readFile = (path) => util.promisify(fs.readFile)(path);
|
||||
|
||||
async function getBuiltInExtensionInfos() {
|
||||
await getBuiltInExtensions();
|
||||
|
||||
const allExtensions = [];
|
||||
/** @type {Object.<string, string>} */
|
||||
const locations = {};
|
||||
|
@ -400,8 +403,8 @@ async function handleRoot(req, res) {
|
|||
|
||||
const secondaryHost = (
|
||||
req.headers['host']
|
||||
? req.headers['host'].replace(':' + PORT, ':' + SECONDARY_PORT)
|
||||
: `${HOST}:${SECONDARY_PORT}`
|
||||
? req.headers['host'].replace(':' + PORT, ':' + SECONDARY_PORT)
|
||||
: `${HOST}:${SECONDARY_PORT}`
|
||||
);
|
||||
const webConfigJSON = {
|
||||
folderUri: folderUri,
|
||||
|
|
|
@ -1013,3 +1013,61 @@ export class IntervalCounter {
|
|||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
export type ValueCallback<T = any> = (value: T | Promise<T>) => void;
|
||||
|
||||
/**
|
||||
* Creates a promise whose resolution or rejection can be controlled imperatively.
|
||||
*/
|
||||
export class DeferredPromise<T> {
|
||||
|
||||
private completeCallback!: ValueCallback<T>;
|
||||
private errorCallback!: (err: any) => void;
|
||||
private rejected = false;
|
||||
private resolved = false;
|
||||
|
||||
public get isRejected() {
|
||||
return this.rejected;
|
||||
}
|
||||
|
||||
public get isResolved() {
|
||||
return this.resolved;
|
||||
}
|
||||
|
||||
public get isSettled() {
|
||||
return this.rejected || this.resolved;
|
||||
}
|
||||
|
||||
public p: Promise<T>;
|
||||
|
||||
constructor() {
|
||||
this.p = new Promise<T>((c, e) => {
|
||||
this.completeCallback = c;
|
||||
this.errorCallback = e;
|
||||
});
|
||||
}
|
||||
|
||||
public complete(value: T) {
|
||||
return new Promise<void>(resolve => {
|
||||
this.completeCallback(value);
|
||||
this.resolved = true;
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
public error(err: any) {
|
||||
return new Promise<void>(resolve => {
|
||||
this.errorCallback(err);
|
||||
this.rejected = true;
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
public cancel() {
|
||||
new Promise<void>(resolve => {
|
||||
this.errorCallback(errors.canceled());
|
||||
this.rejected = true;
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,11 @@ export class Client extends MessagePortClient implements IDisposable {
|
|||
*/
|
||||
export async function connect(window: BrowserWindow): Promise<MessagePortMain> {
|
||||
|
||||
// Assert healthy window to talk to
|
||||
if (window.webContents.isDestroyed()) {
|
||||
throw new Error('ipc.mp#connect: Cannot talk to window because it is closed or destroyed');
|
||||
}
|
||||
|
||||
// Ask to create message channel inside the window
|
||||
// and send over a UUID to correlate the response
|
||||
const nonce = generateUuid();
|
||||
|
|
|
@ -780,4 +780,31 @@ suite('Async', () => {
|
|||
assert.equal(ct1!.isCancellationRequested, true, 'should cancel a');
|
||||
assert.equal(ct2!.isCancellationRequested, true, 'should cancel b');
|
||||
});
|
||||
|
||||
suite('DeferredPromise', () => {
|
||||
test('resolves', async () => {
|
||||
const deferred = new async.DeferredPromise<number>();
|
||||
assert.strictEqual(deferred.isResolved, false);
|
||||
deferred.complete(42);
|
||||
assert.strictEqual(await deferred.p, 42);
|
||||
assert.strictEqual(deferred.isResolved, true);
|
||||
});
|
||||
|
||||
test('rejects', async () => {
|
||||
const deferred = new async.DeferredPromise<number>();
|
||||
assert.strictEqual(deferred.isRejected, false);
|
||||
const err = new Error('oh no!');
|
||||
deferred.error(err);
|
||||
assert.strictEqual(await deferred.p.catch(e => e), err);
|
||||
assert.strictEqual(deferred.isRejected, true);
|
||||
});
|
||||
|
||||
test('cancels', async () => {
|
||||
const deferred = new async.DeferredPromise<number>();
|
||||
assert.strictEqual(deferred.isRejected, false);
|
||||
deferred.cancel();
|
||||
assert.strictEqual((await deferred.p.catch(e => e)).name, 'Canceled');
|
||||
assert.strictEqual(deferred.isRejected, true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,47 +5,10 @@
|
|||
|
||||
import { join } from 'vs/base/common/path';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { canceled } from 'vs/base/common/errors';
|
||||
import { isWindows } from 'vs/base/common/platform';
|
||||
|
||||
export type ValueCallback<T = any> = (value: T | Promise<T>) => void;
|
||||
|
||||
export class DeferredPromise<T> {
|
||||
|
||||
private completeCallback!: ValueCallback<T>;
|
||||
private errorCallback!: (err: any) => void;
|
||||
|
||||
public p: Promise<any>;
|
||||
|
||||
constructor() {
|
||||
this.p = new Promise<any>((c, e) => {
|
||||
this.completeCallback = c;
|
||||
this.errorCallback = e;
|
||||
});
|
||||
}
|
||||
|
||||
public complete(value: T) {
|
||||
return new Promise<void>(resolve => {
|
||||
this.completeCallback(value);
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
public error(err: any) {
|
||||
return new Promise<void>(resolve => {
|
||||
this.errorCallback(err);
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
public cancel() {
|
||||
new Promise<void>(resolve => {
|
||||
this.errorCallback(canceled());
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function toResource(this: any, path: string) {
|
||||
if (isWindows) {
|
||||
return URI.file(join('C:\\', btoa(this.test.fullTitle()), path));
|
||||
|
|
|
@ -14,6 +14,7 @@ import { browserCodeLoadingCacheStrategy } from 'vs/base/common/platform';
|
|||
import { ISharedProcess, ISharedProcessConfiguration } from 'vs/platform/sharedProcess/node/sharedProcess';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { connect as connectMessagePort } from 'vs/base/parts/ipc/electron-main/ipc.mp';
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
|
||||
export class SharedProcess extends Disposable implements ISharedProcess {
|
||||
|
||||
|
@ -49,8 +50,18 @@ export class SharedProcess extends Disposable implements ISharedProcess {
|
|||
// workbench window will communicate directly
|
||||
await this.whenReady();
|
||||
|
||||
// connect to the shared process window
|
||||
const port = await this.connect();
|
||||
|
||||
// Check back if the requesting window meanwhile closed
|
||||
// Since shared process is delayed on startup there is
|
||||
// a chance that the window close before the shared process
|
||||
// was ready for a connection.
|
||||
if (e.sender.isDestroyed()) {
|
||||
return port.close();
|
||||
}
|
||||
|
||||
// send the port back to the requesting window
|
||||
e.sender.postMessage('vscode:createSharedProcessMessageChannelResult', nonce, [port]);
|
||||
});
|
||||
}
|
||||
|
@ -208,13 +219,9 @@ export class SharedProcess extends Disposable implements ISharedProcess {
|
|||
// Wait for shared process being ready to accept connection
|
||||
await this.whenIpcReady;
|
||||
|
||||
// Assert healthy shared process window
|
||||
if (!this.window || this.window.webContents.isDestroyed()) {
|
||||
throw new Error('Cannot connect to shared process window because the window is closed or destroyed');
|
||||
}
|
||||
|
||||
// Connect and return message port
|
||||
return connectMessagePort(this.window);
|
||||
const window = assertIsDefined(this.window);
|
||||
return connectMessagePort(window);
|
||||
}
|
||||
|
||||
async toggle(): Promise<void> {
|
||||
|
|
|
@ -424,11 +424,12 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
|||
this.dispose();
|
||||
});
|
||||
|
||||
// Prevent loading of svgs
|
||||
this._win.webContents.session.webRequest.onBeforeRequest(null!, (details, callback) => {
|
||||
if (details.url.indexOf('.svg') > 0) {
|
||||
const svgFileSchemes = new Set([Schemas.file, Schemas.vscodeFileResource, 'devtools']);
|
||||
this._win.webContents.session.webRequest.onBeforeRequest((details, callback) => {
|
||||
// Prevent loading of remote svgs
|
||||
if (details.url.endsWith('.svg')) {
|
||||
const uri = URI.parse(details.url);
|
||||
if (uri && !uri.scheme.match(/file/i) && uri.path.endsWith('.svg')) {
|
||||
if (uri && !svgFileSchemes.has(uri.scheme)) {
|
||||
return callback({ cancel: true });
|
||||
}
|
||||
}
|
||||
|
@ -436,12 +437,24 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
|||
return callback({});
|
||||
});
|
||||
|
||||
this._win.webContents.session.webRequest.onHeadersReceived(null!, (details, callback) => {
|
||||
this._win.webContents.session.webRequest.onHeadersReceived((details, callback) => {
|
||||
const responseHeaders = details.responseHeaders as Record<string, (string) | (string[])>;
|
||||
|
||||
const contentType = (responseHeaders['content-type'] || responseHeaders['Content-Type']);
|
||||
if (contentType && Array.isArray(contentType) && contentType.some(x => x.toLowerCase().indexOf('image/svg') >= 0)) {
|
||||
return callback({ cancel: true });
|
||||
|
||||
if (contentType && Array.isArray(contentType)) {
|
||||
// https://github.com/microsoft/vscode/issues/97564
|
||||
// ensure local svg files have Content-Type image/svg+xml
|
||||
if (details.url.endsWith('.svg')) {
|
||||
const uri = URI.parse(details.url);
|
||||
if (uri && svgFileSchemes.has(uri.scheme)) {
|
||||
responseHeaders['Content-Type'] = ['image/svg+xml'];
|
||||
return callback({ cancel: false, responseHeaders });
|
||||
}
|
||||
}
|
||||
|
||||
if (contentType.some(x => x.toLowerCase().includes('image/svg'))) {
|
||||
return callback({ cancel: true });
|
||||
}
|
||||
}
|
||||
|
||||
return callback({ cancel: false });
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { release } from 'os';
|
||||
import * as fs from 'fs';
|
||||
import { gracefulify } from 'graceful-fs';
|
||||
import { isAbsolute, join } from 'vs/base/common/path';
|
||||
import { raceTimeout } from 'vs/base/common/async';
|
||||
import product from 'vs/platform/product/common/product';
|
||||
|
@ -36,108 +38,119 @@ import { buildTelemetryMessage } from 'vs/platform/telemetry/node/telemetry';
|
|||
import { FileService } from 'vs/platform/files/common/fileService';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { ExtensionManagementCLIService } from 'vs/platform/extensionManagement/common/extensionManagementCLIService';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { LocalizationsService } from 'vs/platform/localizations/node/localizations';
|
||||
import { ILocalizationsService } from 'vs/platform/localizations/common/localizations';
|
||||
import { setUnexpectedErrorHandler } from 'vs/base/common/errors';
|
||||
import { toErrorMessage } from 'vs/base/common/errorMessage';
|
||||
|
||||
export class Main {
|
||||
export class CliMain extends Disposable {
|
||||
|
||||
constructor(
|
||||
@INativeEnvironmentService private readonly environmentService: INativeEnvironmentService,
|
||||
@IExtensionManagementCLIService private readonly extensionManagementCLIService: IExtensionManagementCLIService
|
||||
) { }
|
||||
private argv: NativeParsedArgs
|
||||
) {
|
||||
super();
|
||||
|
||||
async run(argv: NativeParsedArgs): Promise<void> {
|
||||
if (argv['install-source']) {
|
||||
await this.setInstallSource(argv['install-source']);
|
||||
return;
|
||||
// Enable gracefulFs
|
||||
gracefulify(fs);
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
|
||||
// Dispose on exit
|
||||
process.once('exit', () => this.dispose());
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
|
||||
// Services
|
||||
const [instantiationService, appenders] = await this.initServices();
|
||||
|
||||
return instantiationService.invokeFunction(async accessor => {
|
||||
const logService = accessor.get(ILogService);
|
||||
const environmentService = accessor.get(INativeEnvironmentService);
|
||||
const extensionManagementCLIService = accessor.get(IExtensionManagementCLIService);
|
||||
|
||||
// Log info
|
||||
logService.info('CLI main', this.argv);
|
||||
|
||||
// Error handler
|
||||
this.registerErrorHandler(logService);
|
||||
|
||||
// Run based on argv
|
||||
await this.doRun(environmentService, extensionManagementCLIService);
|
||||
|
||||
// Flush the remaining data in AI adapter (with 1s timeout)
|
||||
return raceTimeout(combinedAppender(...appenders).flush(), 1000);
|
||||
});
|
||||
}
|
||||
|
||||
private async initServices(): Promise<[IInstantiationService, AppInsightsAppender[]]> {
|
||||
const services = new ServiceCollection();
|
||||
|
||||
// Environment
|
||||
const environmentService = new NativeEnvironmentService(this.argv);
|
||||
services.set(IEnvironmentService, environmentService);
|
||||
services.set(INativeEnvironmentService, environmentService);
|
||||
|
||||
// Init folders
|
||||
await Promise.all([environmentService.appSettingsHome.fsPath, environmentService.extensionsPath].map(path => path ? mkdirp(path) : undefined));
|
||||
|
||||
// Log
|
||||
const logLevel = getLogLevel(environmentService);
|
||||
const loggers: ILogService[] = [];
|
||||
loggers.push(new SpdLogService('cli', environmentService.logsPath, logLevel));
|
||||
if (logLevel === LogLevel.Trace) {
|
||||
loggers.push(new ConsoleLogService(logLevel));
|
||||
}
|
||||
|
||||
if (argv['list-extensions']) {
|
||||
await this.extensionManagementCLIService.listExtensions(!!argv['show-versions'], argv['category']);
|
||||
} else if (argv['install-extension'] || argv['install-builtin-extension']) {
|
||||
await this.extensionManagementCLIService.installExtensions(this.asExtensionIdOrVSIX(argv['install-extension'] || []), argv['install-builtin-extension'] || [], !!argv['do-not-sync'], !!argv['force']);
|
||||
} else if (argv['uninstall-extension']) {
|
||||
await this.extensionManagementCLIService.uninstallExtensions(this.asExtensionIdOrVSIX(argv['uninstall-extension']), !!argv['force']);
|
||||
} else if (argv['locate-extension']) {
|
||||
await this.extensionManagementCLIService.locateExtension(argv['locate-extension']);
|
||||
} else if (argv['telemetry']) {
|
||||
console.log(buildTelemetryMessage(this.environmentService.appRoot, this.environmentService.extensionsPath));
|
||||
}
|
||||
}
|
||||
const logService = this._register(new MultiplexLogService(loggers));
|
||||
services.set(ILogService, logService);
|
||||
|
||||
private asExtensionIdOrVSIX(inputs: string[]): (string | URI)[] {
|
||||
return inputs.map(input => /\.vsix$/i.test(input) ? URI.file(isAbsolute(input) ? input : join(process.cwd(), input)) : input);
|
||||
}
|
||||
// Files
|
||||
const fileService = this._register(new FileService(logService));
|
||||
services.set(IFileService, fileService);
|
||||
|
||||
private setInstallSource(installSource: string): Promise<void> {
|
||||
return writeFile(this.environmentService.installSourcePath, installSource.slice(0, 30));
|
||||
}
|
||||
const diskFileSystemProvider = this._register(new DiskFileSystemProvider(logService));
|
||||
fileService.registerProvider(Schemas.file, diskFileSystemProvider);
|
||||
|
||||
}
|
||||
// Configuration
|
||||
const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService));
|
||||
services.set(IConfigurationService, configurationService);
|
||||
|
||||
const eventPrefix = 'monacoworkbench';
|
||||
// Init config
|
||||
await configurationService.initialize();
|
||||
|
||||
export async function main(argv: NativeParsedArgs): Promise<void> {
|
||||
const services = new ServiceCollection();
|
||||
const disposables = new DisposableStore();
|
||||
// State
|
||||
const stateService = new StateService(environmentService, logService);
|
||||
services.set(IStateService, stateService);
|
||||
|
||||
const environmentService = new NativeEnvironmentService(argv);
|
||||
const logLevel = getLogLevel(environmentService);
|
||||
const loggers: ILogService[] = [];
|
||||
loggers.push(new SpdLogService('cli', environmentService.logsPath, logLevel));
|
||||
if (logLevel === LogLevel.Trace) {
|
||||
loggers.push(new ConsoleLogService(logLevel));
|
||||
}
|
||||
const logService = new MultiplexLogService(loggers);
|
||||
process.once('exit', () => logService.dispose());
|
||||
logService.info('main', argv);
|
||||
|
||||
await Promise.all<void | undefined>([environmentService.appSettingsHome.fsPath, environmentService.extensionsPath]
|
||||
.map((path): undefined | Promise<void> => path ? mkdirp(path) : undefined));
|
||||
|
||||
// Files
|
||||
const fileService = new FileService(logService);
|
||||
disposables.add(fileService);
|
||||
services.set(IFileService, fileService);
|
||||
|
||||
const diskFileSystemProvider = new DiskFileSystemProvider(logService);
|
||||
disposables.add(diskFileSystemProvider);
|
||||
fileService.registerProvider(Schemas.file, diskFileSystemProvider);
|
||||
|
||||
const configurationService = new ConfigurationService(environmentService.settingsResource, fileService);
|
||||
disposables.add(configurationService);
|
||||
await configurationService.initialize();
|
||||
|
||||
services.set(IEnvironmentService, environmentService);
|
||||
services.set(INativeEnvironmentService, environmentService);
|
||||
|
||||
services.set(ILogService, logService);
|
||||
services.set(IConfigurationService, configurationService);
|
||||
services.set(IStateService, new SyncDescriptor(StateService));
|
||||
services.set(IProductService, { _serviceBrand: undefined, ...product });
|
||||
|
||||
const instantiationService: IInstantiationService = new InstantiationService(services);
|
||||
|
||||
return instantiationService.invokeFunction(async accessor => {
|
||||
const stateService = accessor.get(IStateService);
|
||||
// Product
|
||||
services.set(IProductService, { _serviceBrand: undefined, ...product });
|
||||
|
||||
const { appRoot, extensionsPath, extensionDevelopmentLocationURI, isBuilt, installSourcePath } = environmentService;
|
||||
|
||||
const services = new ServiceCollection();
|
||||
// Request
|
||||
services.set(IRequestService, new SyncDescriptor(RequestService));
|
||||
|
||||
// Extensions
|
||||
services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService));
|
||||
services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService));
|
||||
services.set(IExtensionManagementCLIService, new SyncDescriptor(ExtensionManagementCLIService));
|
||||
|
||||
// Localizations
|
||||
services.set(ILocalizationsService, new SyncDescriptor(LocalizationsService));
|
||||
|
||||
// Telemetry
|
||||
const appenders: AppInsightsAppender[] = [];
|
||||
if (isBuilt && !extensionDevelopmentLocationURI && !environmentService.disableTelemetry && product.enableTelemetry) {
|
||||
if (product.aiConfig && product.aiConfig.asimovKey) {
|
||||
appenders.push(new AppInsightsAppender(eventPrefix, null, product.aiConfig.asimovKey));
|
||||
appenders.push(new AppInsightsAppender('monacoworkbench', null, product.aiConfig.asimovKey));
|
||||
}
|
||||
|
||||
const config: ITelemetryServiceConfig = {
|
||||
|
@ -153,17 +166,70 @@ export async function main(argv: NativeParsedArgs): Promise<void> {
|
|||
services.set(ITelemetryService, NullTelemetryService);
|
||||
}
|
||||
|
||||
const instantiationService2 = instantiationService.createChild(services);
|
||||
const main = instantiationService2.createInstance(Main);
|
||||
return [new InstantiationService(services), appenders];
|
||||
}
|
||||
|
||||
try {
|
||||
await main.run(argv);
|
||||
private registerErrorHandler(logService: ILogService): void {
|
||||
|
||||
// Flush the remaining data in AI adapter.
|
||||
// If it does not complete in 1 second, exit the process.
|
||||
await raceTimeout(combinedAppender(...appenders).flush(), 1000);
|
||||
} finally {
|
||||
disposables.dispose();
|
||||
// Install handler for unexpected errors
|
||||
setUnexpectedErrorHandler(error => {
|
||||
const message = toErrorMessage(error, true);
|
||||
if (!message) {
|
||||
return;
|
||||
}
|
||||
|
||||
logService.error(message);
|
||||
});
|
||||
}
|
||||
|
||||
private async doRun(environmentService: INativeEnvironmentService, extensionManagementCLIService: IExtensionManagementCLIService): Promise<void> {
|
||||
|
||||
// Install Source
|
||||
if (this.argv['install-source']) {
|
||||
return this.setInstallSource(environmentService, this.argv['install-source']);
|
||||
}
|
||||
});
|
||||
|
||||
// List Extensions
|
||||
if (this.argv['list-extensions']) {
|
||||
return extensionManagementCLIService.listExtensions(!!this.argv['show-versions'], this.argv['category']);
|
||||
}
|
||||
|
||||
// Install Extension
|
||||
else if (this.argv['install-extension'] || this.argv['install-builtin-extension']) {
|
||||
return extensionManagementCLIService.installExtensions(this.asExtensionIdOrVSIX(this.argv['install-extension'] || []), this.argv['install-builtin-extension'] || [], !!this.argv['do-not-sync'], !!this.argv['force']);
|
||||
}
|
||||
|
||||
// Uninstall Extension
|
||||
else if (this.argv['uninstall-extension']) {
|
||||
return extensionManagementCLIService.uninstallExtensions(this.asExtensionIdOrVSIX(this.argv['uninstall-extension']), !!this.argv['force']);
|
||||
}
|
||||
|
||||
// Locate Extension
|
||||
else if (this.argv['locate-extension']) {
|
||||
return extensionManagementCLIService.locateExtension(this.argv['locate-extension']);
|
||||
}
|
||||
|
||||
// Telemetry
|
||||
else if (this.argv['telemetry']) {
|
||||
console.log(buildTelemetryMessage(environmentService.appRoot, environmentService.extensionsPath));
|
||||
}
|
||||
}
|
||||
|
||||
private asExtensionIdOrVSIX(inputs: string[]): (string | URI)[] {
|
||||
return inputs.map(input => /\.vsix$/i.test(input) ? URI.file(isAbsolute(input) ? input : join(process.cwd(), input)) : input);
|
||||
}
|
||||
|
||||
private setInstallSource(environmentService: INativeEnvironmentService, installSource: string): Promise<void> {
|
||||
return writeFile(environmentService.installSourcePath, installSource.slice(0, 30));
|
||||
}
|
||||
}
|
||||
|
||||
export async function main(argv: NativeParsedArgs): Promise<void> {
|
||||
const cliMain = new CliMain(argv);
|
||||
|
||||
try {
|
||||
await cliMain.run();
|
||||
} finally {
|
||||
cliMain.dispose();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -150,7 +150,7 @@ export interface OnEnterRule {
|
|||
/**
|
||||
* This rule will only execute if the text above the this line matches this regular expression.
|
||||
*/
|
||||
oneLineAboveText?: RegExp;
|
||||
previousLineText?: RegExp;
|
||||
/**
|
||||
* The action to execute.
|
||||
*/
|
||||
|
|
|
@ -101,11 +101,11 @@ export class RichEditSupport {
|
|||
return this._electricCharacter;
|
||||
}
|
||||
|
||||
public onEnter(autoIndent: EditorAutoIndentStrategy, oneLineAboveText: string, beforeEnterText: string, afterEnterText: string): EnterAction | null {
|
||||
public onEnter(autoIndent: EditorAutoIndentStrategy, previousLineText: string, beforeEnterText: string, afterEnterText: string): EnterAction | null {
|
||||
if (!this._onEnterSupport) {
|
||||
return null;
|
||||
}
|
||||
return this._onEnterSupport.onEnter(autoIndent, oneLineAboveText, beforeEnterText, afterEnterText);
|
||||
return this._onEnterSupport.onEnter(autoIndent, previousLineText, beforeEnterText, afterEnterText);
|
||||
}
|
||||
|
||||
private static _mergeConf(prev: LanguageConfiguration | null, current: LanguageConfiguration): LanguageConfiguration {
|
||||
|
@ -700,17 +700,17 @@ export class LanguageConfigurationRegistryImpl {
|
|||
afterEnterText = endScopedLineTokens.getLineContent().substr(range.endColumn - 1 - scopedLineTokens.firstCharOffset);
|
||||
}
|
||||
|
||||
let oneLineAboveText = '';
|
||||
let previousLineText = '';
|
||||
if (range.startLineNumber > 1 && scopedLineTokens.firstCharOffset === 0) {
|
||||
// This is not the first line and the entire line belongs to this mode
|
||||
const oneLineAboveScopedLineTokens = this.getScopedLineTokens(model, range.startLineNumber - 1);
|
||||
if (oneLineAboveScopedLineTokens.languageId === scopedLineTokens.languageId) {
|
||||
// The line above ends with text belonging to the same mode
|
||||
oneLineAboveText = oneLineAboveScopedLineTokens.getLineContent();
|
||||
previousLineText = oneLineAboveScopedLineTokens.getLineContent();
|
||||
}
|
||||
}
|
||||
|
||||
const enterResult = richEditSupport.onEnter(autoIndent, oneLineAboveText, beforeEnterText, afterEnterText);
|
||||
const enterResult = richEditSupport.onEnter(autoIndent, previousLineText, beforeEnterText, afterEnterText);
|
||||
if (!enterResult) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ export class OnEnterSupport {
|
|||
this._regExpRules = opts.onEnterRules || [];
|
||||
}
|
||||
|
||||
public onEnter(autoIndent: EditorAutoIndentStrategy, oneLineAboveText: string, beforeEnterText: string, afterEnterText: string): EnterAction | null {
|
||||
public onEnter(autoIndent: EditorAutoIndentStrategy, previousLineText: string, beforeEnterText: string, afterEnterText: string): EnterAction | null {
|
||||
// (1): `regExpRules`
|
||||
if (autoIndent >= EditorAutoIndentStrategy.Advanced) {
|
||||
for (let i = 0, len = this._regExpRules.length; i < len; i++) {
|
||||
|
@ -61,8 +61,8 @@ export class OnEnterSupport {
|
|||
reg: rule.afterText,
|
||||
text: afterEnterText
|
||||
}, {
|
||||
reg: rule.oneLineAboveText,
|
||||
text: oneLineAboveText
|
||||
reg: rule.previousLineText,
|
||||
text: previousLineText
|
||||
}].every((obj): boolean => {
|
||||
return obj.reg ? obj.reg.test(obj.text) : true;
|
||||
});
|
||||
|
|
|
@ -299,13 +299,13 @@ export class MoveLinesCommand implements ICommand {
|
|||
}
|
||||
}
|
||||
|
||||
private matchEnterRule(model: ITextModel, indentConverter: IIndentConverter, tabSize: number, line: number, oneLineAbove: number, oneLineAboveText?: string) {
|
||||
private matchEnterRule(model: ITextModel, indentConverter: IIndentConverter, tabSize: number, line: number, oneLineAbove: number, previousLineText?: string) {
|
||||
let validPrecedingLine = oneLineAbove;
|
||||
while (validPrecedingLine >= 1) {
|
||||
// ship empty lines as empty lines just inherit indentation
|
||||
let lineContent;
|
||||
if (validPrecedingLine === oneLineAbove && oneLineAboveText !== undefined) {
|
||||
lineContent = oneLineAboveText;
|
||||
if (validPrecedingLine === oneLineAbove && previousLineText !== undefined) {
|
||||
lineContent = previousLineText;
|
||||
} else {
|
||||
lineContent = model.getLineContent(validPrecedingLine);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { first } from 'vs/base/common/async';
|
||||
import { onUnexpectedExternalError } from 'vs/base/common/errors';
|
||||
import { IPosition, Position } from 'vs/editor/common/core/position';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
|
@ -20,19 +19,26 @@ export const Context = {
|
|||
MultipleSignatures: new RawContextKey<boolean>('parameterHintsMultipleSignatures', false),
|
||||
};
|
||||
|
||||
export function provideSignatureHelp(
|
||||
export async function provideSignatureHelp(
|
||||
model: ITextModel,
|
||||
position: Position,
|
||||
context: modes.SignatureHelpContext,
|
||||
token: CancellationToken
|
||||
): Promise<modes.SignatureHelpResult | null | undefined> {
|
||||
): Promise<modes.SignatureHelpResult | undefined> {
|
||||
|
||||
const supports = modes.SignatureHelpProviderRegistry.ordered(model);
|
||||
|
||||
return first(supports.map(support => () => {
|
||||
return Promise.resolve(support.provideSignatureHelp(model, position, token, context))
|
||||
.catch<modes.SignatureHelpResult | undefined>(e => onUnexpectedExternalError(e));
|
||||
}));
|
||||
for (const support of supports) {
|
||||
try {
|
||||
const result = await support.provideSignatureHelp(model, position, token, context);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
} catch (err) {
|
||||
onUnexpectedExternalError(err);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
CommandsRegistry.registerCommand('_executeSignatureHelpProvider', async (accessor, ...args: [URI, IPosition, string?]) => {
|
||||
|
|
|
@ -62,34 +62,34 @@ suite('SnippetController', () => {
|
|||
editor.setPosition({ lineNumber: 4, column: 2 });
|
||||
|
||||
snippetController.insert(template);
|
||||
assert.equal(editor.getModel()!.getLineContent(4), '\tfor (var index; index < array.length; index++) {');
|
||||
assert.equal(editor.getModel()!.getLineContent(5), '\t\tvar element = array[index];');
|
||||
assert.equal(editor.getModel()!.getLineContent(6), '\t\t');
|
||||
assert.equal(editor.getModel()!.getLineContent(7), '\t}');
|
||||
assert.strictEqual(editor.getModel()!.getLineContent(4), '\tfor (var index; index < array.length; index++) {');
|
||||
assert.strictEqual(editor.getModel()!.getLineContent(5), '\t\tvar element = array[index];');
|
||||
assert.strictEqual(editor.getModel()!.getLineContent(6), '\t\t');
|
||||
assert.strictEqual(editor.getModel()!.getLineContent(7), '\t}');
|
||||
|
||||
editor.trigger('test', 'type', { text: 'i' });
|
||||
assert.equal(editor.getModel()!.getLineContent(4), '\tfor (var i; i < array.length; i++) {');
|
||||
assert.equal(editor.getModel()!.getLineContent(5), '\t\tvar element = array[i];');
|
||||
assert.equal(editor.getModel()!.getLineContent(6), '\t\t');
|
||||
assert.equal(editor.getModel()!.getLineContent(7), '\t}');
|
||||
assert.strictEqual(editor.getModel()!.getLineContent(4), '\tfor (var i; i < array.length; i++) {');
|
||||
assert.strictEqual(editor.getModel()!.getLineContent(5), '\t\tvar element = array[i];');
|
||||
assert.strictEqual(editor.getModel()!.getLineContent(6), '\t\t');
|
||||
assert.strictEqual(editor.getModel()!.getLineContent(7), '\t}');
|
||||
|
||||
snippetController.next();
|
||||
editor.trigger('test', 'type', { text: 'arr' });
|
||||
assert.equal(editor.getModel()!.getLineContent(4), '\tfor (var i; i < arr.length; i++) {');
|
||||
assert.equal(editor.getModel()!.getLineContent(5), '\t\tvar element = arr[i];');
|
||||
assert.equal(editor.getModel()!.getLineContent(6), '\t\t');
|
||||
assert.equal(editor.getModel()!.getLineContent(7), '\t}');
|
||||
assert.strictEqual(editor.getModel()!.getLineContent(4), '\tfor (var i; i < arr.length; i++) {');
|
||||
assert.strictEqual(editor.getModel()!.getLineContent(5), '\t\tvar element = arr[i];');
|
||||
assert.strictEqual(editor.getModel()!.getLineContent(6), '\t\t');
|
||||
assert.strictEqual(editor.getModel()!.getLineContent(7), '\t}');
|
||||
|
||||
snippetController.prev();
|
||||
editor.trigger('test', 'type', { text: 'j' });
|
||||
assert.equal(editor.getModel()!.getLineContent(4), '\tfor (var j; j < arr.length; j++) {');
|
||||
assert.equal(editor.getModel()!.getLineContent(5), '\t\tvar element = arr[j];');
|
||||
assert.equal(editor.getModel()!.getLineContent(6), '\t\t');
|
||||
assert.equal(editor.getModel()!.getLineContent(7), '\t}');
|
||||
assert.strictEqual(editor.getModel()!.getLineContent(4), '\tfor (var j; j < arr.length; j++) {');
|
||||
assert.strictEqual(editor.getModel()!.getLineContent(5), '\t\tvar element = arr[j];');
|
||||
assert.strictEqual(editor.getModel()!.getLineContent(6), '\t\t');
|
||||
assert.strictEqual(editor.getModel()!.getLineContent(7), '\t}');
|
||||
|
||||
snippetController.next();
|
||||
snippetController.next();
|
||||
assert.deepEqual(editor.getPosition(), new Position(6, 3));
|
||||
assert.deepStrictEqual(editor.getPosition(), new Position(6, 3));
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -98,13 +98,13 @@ suite('SnippetController', () => {
|
|||
editor.setPosition({ lineNumber: 4, column: 2 });
|
||||
|
||||
snippetController.insert(template);
|
||||
assert.equal(editor.getModel()!.getLineContent(4), '\tfor (var index; index < array.length; index++) {');
|
||||
assert.equal(editor.getModel()!.getLineContent(5), '\t\tvar element = array[index];');
|
||||
assert.equal(editor.getModel()!.getLineContent(6), '\t\t');
|
||||
assert.equal(editor.getModel()!.getLineContent(7), '\t}');
|
||||
assert.strictEqual(editor.getModel()!.getLineContent(4), '\tfor (var index; index < array.length; index++) {');
|
||||
assert.strictEqual(editor.getModel()!.getLineContent(5), '\t\tvar element = array[index];');
|
||||
assert.strictEqual(editor.getModel()!.getLineContent(6), '\t\t');
|
||||
assert.strictEqual(editor.getModel()!.getLineContent(7), '\t}');
|
||||
|
||||
snippetController.cancel();
|
||||
assert.deepEqual(editor.getPosition(), new Position(4, 16));
|
||||
assert.deepStrictEqual(editor.getPosition(), new Position(4, 16));
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -121,7 +121,7 @@ suite('SnippetController', () => {
|
|||
// text: null
|
||||
// }]);
|
||||
|
||||
// assert.equal(snippetController.isInSnippetMode(), false);
|
||||
// assert.strictEqual(snippetController.isInSnippetMode(), false);
|
||||
// });
|
||||
// });
|
||||
|
||||
|
@ -138,7 +138,7 @@ suite('SnippetController', () => {
|
|||
// text: null
|
||||
// }]);
|
||||
|
||||
// assert.equal(snippetController.isInSnippetMode(), false);
|
||||
// assert.strictEqual(snippetController.isInSnippetMode(), false);
|
||||
// });
|
||||
// });
|
||||
|
||||
|
@ -155,7 +155,7 @@ suite('SnippetController', () => {
|
|||
// text: '\nHello'
|
||||
// }]);
|
||||
|
||||
// assert.equal(snippetController.isInSnippetMode(), false);
|
||||
// assert.strictEqual(snippetController.isInSnippetMode(), false);
|
||||
// });
|
||||
// });
|
||||
|
||||
|
@ -172,7 +172,7 @@ suite('SnippetController', () => {
|
|||
// text: '\nHello'
|
||||
// }]);
|
||||
|
||||
// assert.equal(snippetController.isInSnippetMode(), false);
|
||||
// assert.strictEqual(snippetController.isInSnippetMode(), false);
|
||||
// });
|
||||
// });
|
||||
|
||||
|
@ -183,7 +183,7 @@ suite('SnippetController', () => {
|
|||
|
||||
editor.getModel()!.setValue('goodbye');
|
||||
|
||||
assert.equal(snippetController.isInSnippetMode(), false);
|
||||
assert.strictEqual(snippetController.isInSnippetMode(), false);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -194,7 +194,7 @@ suite('SnippetController', () => {
|
|||
|
||||
editor.getModel()!.undo();
|
||||
|
||||
assert.equal(snippetController.isInSnippetMode(), false);
|
||||
assert.strictEqual(snippetController.isInSnippetMode(), false);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -205,7 +205,7 @@ suite('SnippetController', () => {
|
|||
|
||||
editor.setPosition({ lineNumber: 1, column: 1 });
|
||||
|
||||
assert.equal(snippetController.isInSnippetMode(), false);
|
||||
assert.strictEqual(snippetController.isInSnippetMode(), false);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -216,7 +216,7 @@ suite('SnippetController', () => {
|
|||
|
||||
editor.setModel(null);
|
||||
|
||||
assert.equal(snippetController.isInSnippetMode(), false);
|
||||
assert.strictEqual(snippetController.isInSnippetMode(), false);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -227,7 +227,7 @@ suite('SnippetController', () => {
|
|||
|
||||
snippetController.dispose();
|
||||
|
||||
assert.equal(snippetController.isInSnippetMode(), false);
|
||||
assert.strictEqual(snippetController.isInSnippetMode(), false);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -241,7 +241,7 @@ suite('SnippetController', () => {
|
|||
codeSnippet = 'foo$0';
|
||||
snippetController.insert(codeSnippet);
|
||||
|
||||
assert.equal(editor.getSelections()!.length, 2);
|
||||
assert.strictEqual(editor.getSelections()!.length, 2);
|
||||
const [first, second] = editor.getSelections()!;
|
||||
assert.ok(first.equalsRange({ startLineNumber: 1, startColumn: 4, endLineNumber: 1, endColumn: 4 }), first.toString());
|
||||
assert.ok(second.equalsRange({ startLineNumber: 2, startColumn: 4, endLineNumber: 2, endColumn: 4 }), second.toString());
|
||||
|
@ -256,7 +256,7 @@ suite('SnippetController', () => {
|
|||
codeSnippet = 'foo$0bar';
|
||||
snippetController.insert(codeSnippet);
|
||||
|
||||
assert.equal(editor.getSelections()!.length, 2);
|
||||
assert.strictEqual(editor.getSelections()!.length, 2);
|
||||
const [first, second] = editor.getSelections()!;
|
||||
assert.ok(first.equalsRange({ startLineNumber: 1, startColumn: 4, endLineNumber: 1, endColumn: 4 }), first.toString());
|
||||
assert.ok(second.equalsRange({ startLineNumber: 2, startColumn: 4, endLineNumber: 2, endColumn: 4 }), second.toString());
|
||||
|
@ -271,7 +271,7 @@ suite('SnippetController', () => {
|
|||
codeSnippet = 'foo$0bar';
|
||||
snippetController.insert(codeSnippet);
|
||||
|
||||
assert.equal(editor.getSelections()!.length, 2);
|
||||
assert.strictEqual(editor.getSelections()!.length, 2);
|
||||
const [first, second] = editor.getSelections()!;
|
||||
assert.ok(first.equalsRange({ startLineNumber: 1, startColumn: 4, endLineNumber: 1, endColumn: 4 }), first.toString());
|
||||
assert.ok(second.equalsRange({ startLineNumber: 1, startColumn: 14, endLineNumber: 1, endColumn: 14 }), second.toString());
|
||||
|
@ -286,7 +286,7 @@ suite('SnippetController', () => {
|
|||
codeSnippet = 'foo\n$0\nbar';
|
||||
snippetController.insert(codeSnippet);
|
||||
|
||||
assert.equal(editor.getSelections()!.length, 2);
|
||||
assert.strictEqual(editor.getSelections()!.length, 2);
|
||||
const [first, second] = editor.getSelections()!;
|
||||
assert.ok(first.equalsRange({ startLineNumber: 2, startColumn: 1, endLineNumber: 2, endColumn: 1 }), first.toString());
|
||||
assert.ok(second.equalsRange({ startLineNumber: 4, startColumn: 1, endLineNumber: 4, endColumn: 1 }), second.toString());
|
||||
|
@ -301,7 +301,7 @@ suite('SnippetController', () => {
|
|||
codeSnippet = 'foo\n$0\nbar';
|
||||
snippetController.insert(codeSnippet);
|
||||
|
||||
assert.equal(editor.getSelections()!.length, 2);
|
||||
assert.strictEqual(editor.getSelections()!.length, 2);
|
||||
const [first, second] = editor.getSelections()!;
|
||||
assert.ok(first.equalsRange({ startLineNumber: 2, startColumn: 1, endLineNumber: 2, endColumn: 1 }), first.toString());
|
||||
assert.ok(second.equalsRange({ startLineNumber: 4, startColumn: 1, endLineNumber: 4, endColumn: 1 }), second.toString());
|
||||
|
@ -315,7 +315,7 @@ suite('SnippetController', () => {
|
|||
codeSnippet = 'xo$0r';
|
||||
snippetController.insert(codeSnippet, { overwriteBefore: 1 });
|
||||
|
||||
assert.equal(editor.getSelections()!.length, 1);
|
||||
assert.strictEqual(editor.getSelections()!.length, 1);
|
||||
assert.ok(editor.getSelection()!.equalsRange({ startLineNumber: 2, startColumn: 8, endColumn: 8, endLineNumber: 2 }));
|
||||
});
|
||||
});
|
||||
|
@ -328,9 +328,9 @@ suite('SnippetController', () => {
|
|||
codeSnippet = '{{% url_**$1** %}}';
|
||||
controller.insert(codeSnippet, { overwriteBefore: 2 });
|
||||
|
||||
assert.equal(editor.getSelections()!.length, 1);
|
||||
assert.strictEqual(editor.getSelections()!.length, 1);
|
||||
assert.ok(editor.getSelection()!.equalsRange({ startLineNumber: 1, startColumn: 27, endLineNumber: 1, endColumn: 27 }));
|
||||
assert.equal(editor.getModel()!.getValue(), 'example example {{% url_**** %}}');
|
||||
assert.strictEqual(editor.getModel()!.getValue(), 'example example {{% url_**** %}}');
|
||||
|
||||
}, ['example example sc']);
|
||||
|
||||
|
@ -346,9 +346,9 @@ suite('SnippetController', () => {
|
|||
|
||||
controller.insert(codeSnippet, { overwriteBefore: 2 });
|
||||
|
||||
assert.equal(editor.getSelections()!.length, 1);
|
||||
assert.strictEqual(editor.getSelections()!.length, 1);
|
||||
assert.ok(editor.getSelection()!.equalsRange({ startLineNumber: 2, startColumn: 2, endLineNumber: 2, endColumn: 2 }), editor.getSelection()!.toString());
|
||||
assert.equal(editor.getModel()!.getValue(), 'afterEach((done) => {\n\ttest\n});');
|
||||
assert.strictEqual(editor.getModel()!.getValue(), 'afterEach((done) => {\n\ttest\n});');
|
||||
|
||||
}, ['af']);
|
||||
|
||||
|
@ -364,9 +364,9 @@ suite('SnippetController', () => {
|
|||
|
||||
controller.insert(codeSnippet, { overwriteBefore: 2 });
|
||||
|
||||
assert.equal(editor.getSelections()!.length, 1);
|
||||
assert.strictEqual(editor.getSelections()!.length, 1);
|
||||
assert.ok(editor.getSelection()!.equalsRange({ startLineNumber: 2, startColumn: 1, endLineNumber: 2, endColumn: 1 }), editor.getSelection()!.toString());
|
||||
assert.equal(editor.getModel()!.getValue(), 'afterEach((done) => {\n\ttest\n});');
|
||||
assert.strictEqual(editor.getModel()!.getValue(), 'afterEach((done) => {\n\ttest\n});');
|
||||
|
||||
}, ['af']);
|
||||
|
||||
|
@ -380,8 +380,8 @@ suite('SnippetController', () => {
|
|||
|
||||
controller.insert(codeSnippet, { overwriteBefore: 8 });
|
||||
|
||||
assert.equal(editor.getModel()!.getValue(), 'after');
|
||||
assert.equal(editor.getSelections()!.length, 1);
|
||||
assert.strictEqual(editor.getModel()!.getValue(), 'after');
|
||||
assert.strictEqual(editor.getSelections()!.length, 1);
|
||||
assert.ok(editor.getSelection()!.equalsRange({ startLineNumber: 1, startColumn: 4, endLineNumber: 1, endColumn: 4 }), editor.getSelection()!.toString());
|
||||
|
||||
}, ['afterone']);
|
||||
|
@ -404,7 +404,7 @@ suite('SnippetController', () => {
|
|||
|
||||
controller.insert(codeSnippet, { overwriteBefore: 2 });
|
||||
|
||||
assert.equal(editor.getSelections()!.length, 2);
|
||||
assert.strictEqual(editor.getSelections()!.length, 2);
|
||||
const [first, second] = editor.getSelections()!;
|
||||
|
||||
assert.ok(first.equalsRange({ startLineNumber: 5, startColumn: 3, endLineNumber: 5, endColumn: 3 }), first.toString());
|
||||
|
@ -429,7 +429,7 @@ suite('SnippetController', () => {
|
|||
|
||||
controller.insert(codeSnippet, { overwriteBefore: 2 });
|
||||
|
||||
assert.equal(editor.getSelections()!.length, 1);
|
||||
assert.strictEqual(editor.getSelections()!.length, 1);
|
||||
const [first] = editor.getSelections()!;
|
||||
|
||||
assert.ok(first.equalsRange({ startLineNumber: 2, startColumn: 3, endLineNumber: 2, endColumn: 3 }), first.toString());
|
||||
|
@ -465,7 +465,7 @@ suite('SnippetController', () => {
|
|||
|
||||
codeSnippet = '_foo';
|
||||
controller.insert(codeSnippet, { overwriteBefore: 1 });
|
||||
assert.equal(editor.getModel()!.getValue(), 'this._foo\nabc_foo');
|
||||
assert.strictEqual(editor.getModel()!.getValue(), 'this._foo\nabc_foo');
|
||||
|
||||
}, ['this._', 'abc']);
|
||||
|
||||
|
@ -478,7 +478,7 @@ suite('SnippetController', () => {
|
|||
|
||||
codeSnippet = 'XX';
|
||||
controller.insert(codeSnippet, { overwriteBefore: 1 });
|
||||
assert.equal(editor.getModel()!.getValue(), 'this.XX\nabcXX');
|
||||
assert.strictEqual(editor.getModel()!.getValue(), 'this.XX\nabcXX');
|
||||
|
||||
}, ['this._', 'abc']);
|
||||
|
||||
|
@ -492,7 +492,7 @@ suite('SnippetController', () => {
|
|||
|
||||
codeSnippet = '_foo';
|
||||
controller.insert(codeSnippet, { overwriteBefore: 1 });
|
||||
assert.equal(editor.getModel()!.getValue(), 'this._foo\nabc_foo\ndef_foo');
|
||||
assert.strictEqual(editor.getModel()!.getValue(), 'this._foo\nabc_foo\ndef_foo');
|
||||
|
||||
}, ['this._', 'abc', 'def_']);
|
||||
|
||||
|
@ -506,7 +506,7 @@ suite('SnippetController', () => {
|
|||
|
||||
codeSnippet = '._foo';
|
||||
controller.insert(codeSnippet, { overwriteBefore: 2 });
|
||||
assert.equal(editor.getModel()!.getValue(), 'this._foo\nabc._foo\ndef._foo');
|
||||
assert.strictEqual(editor.getModel()!.getValue(), 'this._foo\nabc._foo\ndef._foo');
|
||||
|
||||
}, ['this._', 'abc', 'def._']);
|
||||
|
||||
|
@ -520,7 +520,7 @@ suite('SnippetController', () => {
|
|||
|
||||
codeSnippet = '._foo';
|
||||
controller.insert(codeSnippet, { overwriteBefore: 2 });
|
||||
assert.equal(editor.getModel()!.getValue(), 'this._foo\nabc._foo\ndef._foo');
|
||||
assert.strictEqual(editor.getModel()!.getValue(), 'this._foo\nabc._foo\ndef._foo');
|
||||
|
||||
}, ['this._', 'abc', 'def._']);
|
||||
|
||||
|
@ -534,7 +534,7 @@ suite('SnippetController', () => {
|
|||
|
||||
codeSnippet = '._foo';
|
||||
controller.insert(codeSnippet, { overwriteBefore: 2 });
|
||||
assert.equal(editor.getModel()!.getValue(), 'this._._foo\na._foo\ndef._._foo');
|
||||
assert.strictEqual(editor.getModel()!.getValue(), 'this._._foo\na._foo\ndef._._foo');
|
||||
|
||||
}, ['this._', 'abc', 'def._']);
|
||||
|
||||
|
@ -550,7 +550,7 @@ suite('SnippetController', () => {
|
|||
|
||||
codeSnippet = 'document';
|
||||
controller.insert(codeSnippet, { overwriteBefore: 3 });
|
||||
assert.equal(editor.getModel()!.getValue(), '{document}\n{document && true}');
|
||||
assert.strictEqual(editor.getModel()!.getValue(), '{document}\n{document && true}');
|
||||
|
||||
}, ['{foo}', '{foo && true}']);
|
||||
});
|
||||
|
@ -565,7 +565,7 @@ suite('SnippetController', () => {
|
|||
|
||||
codeSnippet = 'for (var ${1:i}=0; ${1:i}<len; ${1:i}++) { $0 }';
|
||||
controller.insert(codeSnippet);
|
||||
assert.equal(editor.getModel()!.getValue(), 'for (var i=0; i<len; i++) { }for (var i=0; i<len; i++) { }');
|
||||
assert.strictEqual(editor.getModel()!.getValue(), 'for (var i=0; i<len; i++) { }for (var i=0; i<len; i++) { }');
|
||||
|
||||
}, ['for (var i=0; i<len; i++) { }']);
|
||||
|
||||
|
@ -578,7 +578,7 @@ suite('SnippetController', () => {
|
|||
|
||||
codeSnippet = 'for (let ${1:i}=0; ${1:i}<len; ${1:i}++) { $0 }';
|
||||
controller.insert(codeSnippet);
|
||||
assert.equal(editor.getModel()!.getValue(), 'for (let i=0; i<len; i++) { }for (var i=0; i<len; i++) { }');
|
||||
assert.strictEqual(editor.getModel()!.getValue(), 'for (let i=0; i<len; i++) { }for (var i=0; i<len; i++) { }');
|
||||
|
||||
}, ['for (var i=0; i<len; i++) { }']);
|
||||
|
||||
|
|
|
@ -21,13 +21,13 @@ suite('SnippetController2', function () {
|
|||
const actual = s.shift()!;
|
||||
assert.ok(selection.equalsSelection(actual), `actual=${selection.toString()} <> expected=${actual.toString()}`);
|
||||
}
|
||||
assert.equal(s.length, 0);
|
||||
assert.strictEqual(s.length, 0);
|
||||
}
|
||||
|
||||
function assertContextKeys(service: MockContextKeyService, inSnippet: boolean, hasPrev: boolean, hasNext: boolean): void {
|
||||
assert.equal(SnippetController2.InSnippetMode.getValue(service), inSnippet, `inSnippetMode`);
|
||||
assert.equal(SnippetController2.HasPrevTabstop.getValue(service), hasPrev, `HasPrevTabstop`);
|
||||
assert.equal(SnippetController2.HasNextTabstop.getValue(service), hasNext, `HasNextTabstop`);
|
||||
assert.strictEqual(SnippetController2.InSnippetMode.getValue(service), inSnippet, `inSnippetMode`);
|
||||
assert.strictEqual(SnippetController2.HasPrevTabstop.getValue(service), hasPrev, `HasPrevTabstop`);
|
||||
assert.strictEqual(SnippetController2.HasNextTabstop.getValue(service), hasNext, `HasNextTabstop`);
|
||||
}
|
||||
|
||||
let editor: ICodeEditor;
|
||||
|
@ -40,7 +40,7 @@ suite('SnippetController2', function () {
|
|||
model = createTextModel('if\n $state\nfi');
|
||||
editor = createTestCodeEditor({ model: model });
|
||||
editor.setSelections([new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5)]);
|
||||
assert.equal(model.getEOL(), '\n');
|
||||
assert.strictEqual(model.getEOL(), '\n');
|
||||
});
|
||||
|
||||
teardown(function () {
|
||||
|
@ -78,9 +78,9 @@ suite('SnippetController2', function () {
|
|||
assertContextKeys(contextKeys, false, false, false);
|
||||
|
||||
editor.trigger('test', 'type', { text: '\t' });
|
||||
assert.equal(SnippetController2.InSnippetMode.getValue(contextKeys), false);
|
||||
assert.equal(SnippetController2.HasNextTabstop.getValue(contextKeys), false);
|
||||
assert.equal(SnippetController2.HasPrevTabstop.getValue(contextKeys), false);
|
||||
assert.strictEqual(SnippetController2.InSnippetMode.getValue(contextKeys), false);
|
||||
assert.strictEqual(SnippetController2.HasNextTabstop.getValue(contextKeys), false);
|
||||
assert.strictEqual(SnippetController2.HasPrevTabstop.getValue(contextKeys), false);
|
||||
});
|
||||
|
||||
test('insert, insert -> cursor moves out (left/right)', function () {
|
||||
|
@ -111,7 +111,7 @@ suite('SnippetController2', function () {
|
|||
const ctrl = new SnippetController2(editor, logService, contextKeys);
|
||||
|
||||
ctrl.insert('foo${1:bar}foo$0');
|
||||
assert.equal(SnippetController2.InSnippetMode.getValue(contextKeys), true);
|
||||
assert.strictEqual(SnippetController2.InSnippetMode.getValue(contextKeys), true);
|
||||
assertSelections(editor, new Selection(1, 4, 1, 7), new Selection(2, 8, 2, 11));
|
||||
|
||||
// bad selection change
|
||||
|
|
|
@ -531,8 +531,8 @@ suite('SnippetParser', () => {
|
|||
let snippet = new SnippetParser().parse('This ${1:is ${2:nested}}$0', true);
|
||||
let [first, second] = snippet.placeholders;
|
||||
|
||||
assert.deepEqual(snippet.enclosingPlaceholders(first), []);
|
||||
assert.deepEqual(snippet.enclosingPlaceholders(second), [first]);
|
||||
assert.deepStrictEqual(snippet.enclosingPlaceholders(first), []);
|
||||
assert.deepStrictEqual(snippet.enclosingPlaceholders(second), [first]);
|
||||
});
|
||||
|
||||
test('TextmateSnippet#offset', () => {
|
||||
|
|
|
@ -23,14 +23,14 @@ suite('SnippetSession', function () {
|
|||
const actual = s.shift()!;
|
||||
assert.ok(selection.equalsSelection(actual), `actual=${selection.toString()} <> expected=${actual.toString()}`);
|
||||
}
|
||||
assert.equal(s.length, 0);
|
||||
assert.strictEqual(s.length, 0);
|
||||
}
|
||||
|
||||
setup(function () {
|
||||
model = createTextModel('function foo() {\n console.log(a);\n}');
|
||||
editor = createTestCodeEditor({ model: model }) as IActiveCodeEditor;
|
||||
editor.setSelections([new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5)]);
|
||||
assert.equal(model.getEOL(), '\n');
|
||||
assert.strictEqual(model.getEOL(), '\n');
|
||||
});
|
||||
|
||||
teardown(function () {
|
||||
|
@ -43,7 +43,7 @@ suite('SnippetSession', function () {
|
|||
function assertNormalized(position: IPosition, input: string, expected: string): void {
|
||||
const snippet = new SnippetParser().parse(input);
|
||||
SnippetSession.adjustWhitespace(model, position, snippet, true, true);
|
||||
assert.equal(snippet.toTextmateString(), expected);
|
||||
assert.strictEqual(snippet.toTextmateString(), expected);
|
||||
}
|
||||
|
||||
assertNormalized(new Position(1, 1), 'foo', 'foo');
|
||||
|
@ -73,7 +73,7 @@ suite('SnippetSession', function () {
|
|||
test('text edits & selection', function () {
|
||||
const session = new SnippetSession(editor, 'foo${1:bar}foo$0');
|
||||
session.insert();
|
||||
assert.equal(editor.getModel()!.getValue(), 'foobarfoofunction foo() {\n foobarfooconsole.log(a);\n}');
|
||||
assert.strictEqual(editor.getModel()!.getValue(), 'foobarfoofunction foo() {\n foobarfooconsole.log(a);\n}');
|
||||
|
||||
assertSelections(editor, new Selection(1, 4, 1, 7), new Selection(2, 8, 2, 11));
|
||||
session.next();
|
||||
|
@ -86,7 +86,7 @@ suite('SnippetSession', function () {
|
|||
editor.setSelections([new Selection(2, 5, 2, 5), new Selection(1, 1, 1, 1)]);
|
||||
|
||||
session.insert();
|
||||
assert.equal(model.getValue(), 'barfunction foo() {\n barconsole.log(a);\n}');
|
||||
assert.strictEqual(model.getValue(), 'barfunction foo() {\n barconsole.log(a);\n}');
|
||||
assertSelections(editor, new Selection(2, 5, 2, 8), new Selection(1, 1, 1, 4));
|
||||
});
|
||||
|
||||
|
@ -107,7 +107,7 @@ suite('SnippetSession', function () {
|
|||
test('snippets, just text', function () {
|
||||
const session = new SnippetSession(editor, 'foobar');
|
||||
session.insert();
|
||||
assert.equal(model.getValue(), 'foobarfunction foo() {\n foobarconsole.log(a);\n}');
|
||||
assert.strictEqual(model.getValue(), 'foobarfunction foo() {\n foobarconsole.log(a);\n}');
|
||||
assertSelections(editor, new Selection(1, 7, 1, 7), new Selection(2, 11, 2, 11));
|
||||
});
|
||||
|
||||
|
@ -116,7 +116,7 @@ suite('SnippetSession', function () {
|
|||
const session = new SnippetSession(editor, 'foo\n\t${1:bar}\n$0');
|
||||
session.insert();
|
||||
|
||||
assert.equal(editor.getModel()!.getValue(), 'foo\n bar\nfunction foo() {\n foo\n bar\n console.log(a);\n}');
|
||||
assert.strictEqual(editor.getModel()!.getValue(), 'foo\n bar\nfunction foo() {\n foo\n bar\n console.log(a);\n}');
|
||||
|
||||
assertSelections(editor, new Selection(2, 5, 2, 8), new Selection(5, 9, 5, 12));
|
||||
|
||||
|
@ -129,7 +129,7 @@ suite('SnippetSession', function () {
|
|||
editor.setSelection(new Selection(2, 5, 2, 5));
|
||||
const session = new SnippetSession(editor, 'abc\n foo\n bar\n$0', { overwriteBefore: 0, overwriteAfter: 0, adjustWhitespace: false, clipboardText: undefined, overtypingCapturer: undefined });
|
||||
session.insert();
|
||||
assert.equal(editor.getModel()!.getValue(), 'function foo() {\n abc\n foo\n bar\nconsole.log(a);\n}');
|
||||
assert.strictEqual(editor.getModel()!.getValue(), 'function foo() {\n abc\n foo\n bar\nconsole.log(a);\n}');
|
||||
});
|
||||
|
||||
test('snippets, selections -> next/prev', () => {
|
||||
|
@ -171,7 +171,7 @@ suite('SnippetSession', function () {
|
|||
|
||||
// go to final tabstop
|
||||
session.next();
|
||||
assert.equal(model.getValue(), 'fX_bar_function foo() {\n fX_bar_console.log(a);\n}');
|
||||
assert.strictEqual(model.getValue(), 'fX_bar_function foo() {\n fX_bar_console.log(a);\n}');
|
||||
assertSelections(editor, new Selection(1, 8, 1, 8), new Selection(2, 12, 2, 12));
|
||||
});
|
||||
|
||||
|
@ -180,7 +180,7 @@ suite('SnippetSession', function () {
|
|||
editor.setSelections([new Selection(1, 1, 1, 4), new Selection(1, 9, 1, 12)]);
|
||||
|
||||
new SnippetSession(editor, 'x$0').insert();
|
||||
assert.equal(model.getValue(), 'x_bar_x');
|
||||
assert.strictEqual(model.getValue(), 'x_bar_x');
|
||||
assertSelections(editor, new Selection(1, 2, 1, 2), new Selection(1, 8, 1, 8));
|
||||
});
|
||||
|
||||
|
@ -189,7 +189,7 @@ suite('SnippetSession', function () {
|
|||
editor.setSelections([new Selection(1, 1, 1, 4), new Selection(1, 9, 1, 12)]);
|
||||
|
||||
new SnippetSession(editor, 'LONGER$0').insert();
|
||||
assert.equal(model.getValue(), 'LONGER_bar_LONGER');
|
||||
assert.strictEqual(model.getValue(), 'LONGER_bar_LONGER');
|
||||
assertSelections(editor, new Selection(1, 7, 1, 7), new Selection(1, 18, 1, 18));
|
||||
});
|
||||
|
||||
|
@ -203,11 +203,11 @@ suite('SnippetSession', function () {
|
|||
editor.trigger('test', 'type', { text: 'foo-' });
|
||||
|
||||
session.next();
|
||||
assert.equal(model.getValue(), 'foo_foo-bar_foo');
|
||||
assert.strictEqual(model.getValue(), 'foo_foo-bar_foo');
|
||||
assertSelections(editor, new Selection(1, 12, 1, 12));
|
||||
|
||||
editor.trigger('test', 'type', { text: 'XXX' });
|
||||
assert.equal(model.getValue(), 'foo_foo-barXXX_foo');
|
||||
assert.strictEqual(model.getValue(), 'foo_foo-barXXX_foo');
|
||||
session.prev();
|
||||
assertSelections(editor, new Selection(1, 5, 1, 9));
|
||||
session.next();
|
||||
|
@ -242,7 +242,7 @@ suite('SnippetSession', function () {
|
|||
editor.trigger('test', 'type', { text: '333' });
|
||||
|
||||
session.next();
|
||||
assert.equal(model.getValue(), '111222333function foo() {\n 111222333console.log(a);\n}');
|
||||
assert.strictEqual(model.getValue(), '111222333function foo() {\n 111222333console.log(a);\n}');
|
||||
assertSelections(editor, new Selection(1, 10, 1, 10), new Selection(2, 14, 2, 14));
|
||||
|
||||
session.prev();
|
||||
|
@ -269,22 +269,22 @@ suite('SnippetSession', function () {
|
|||
editor.trigger('test', 'type', { text: '333' });
|
||||
|
||||
session.next();
|
||||
assert.equal(session.isAtLastPlaceholder, true);
|
||||
assert.strictEqual(session.isAtLastPlaceholder, true);
|
||||
});
|
||||
|
||||
test('snippets, gracefully move over final tabstop', function () {
|
||||
const session = new SnippetSession(editor, '${1}bar$0');
|
||||
session.insert();
|
||||
|
||||
assert.equal(session.isAtLastPlaceholder, false);
|
||||
assert.strictEqual(session.isAtLastPlaceholder, false);
|
||||
assertSelections(editor, new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5));
|
||||
|
||||
session.next();
|
||||
assert.equal(session.isAtLastPlaceholder, true);
|
||||
assert.strictEqual(session.isAtLastPlaceholder, true);
|
||||
assertSelections(editor, new Selection(1, 4, 1, 4), new Selection(2, 8, 2, 8));
|
||||
|
||||
session.next();
|
||||
assert.equal(session.isAtLastPlaceholder, true);
|
||||
assert.strictEqual(session.isAtLastPlaceholder, true);
|
||||
assertSelections(editor, new Selection(1, 4, 1, 4), new Selection(2, 8, 2, 8));
|
||||
});
|
||||
|
||||
|
@ -294,46 +294,46 @@ suite('SnippetSession', function () {
|
|||
assertSelections(editor, new Selection(1, 5, 1, 7), new Selection(2, 9, 2, 11));
|
||||
|
||||
editor.trigger('test', 'type', { text: 'XXX' });
|
||||
assert.equal(model.getValue(), 'log(XXX);function foo() {\n log(XXX);console.log(a);\n}');
|
||||
assert.strictEqual(model.getValue(), 'log(XXX);function foo() {\n log(XXX);console.log(a);\n}');
|
||||
|
||||
session.next();
|
||||
assert.equal(session.isAtLastPlaceholder, false);
|
||||
assert.strictEqual(session.isAtLastPlaceholder, false);
|
||||
// assertSelections(editor, new Selection(1, 7, 1, 7), new Selection(2, 11, 2, 11));
|
||||
|
||||
session.next();
|
||||
assert.equal(session.isAtLastPlaceholder, true);
|
||||
assert.strictEqual(session.isAtLastPlaceholder, true);
|
||||
assertSelections(editor, new Selection(1, 10, 1, 10), new Selection(2, 14, 2, 14));
|
||||
});
|
||||
|
||||
test('snippets, selections and snippet ranges', function () {
|
||||
const session = new SnippetSession(editor, '${1:foo}farboo${2:bar}$0');
|
||||
session.insert();
|
||||
assert.equal(model.getValue(), 'foofarboobarfunction foo() {\n foofarboobarconsole.log(a);\n}');
|
||||
assert.strictEqual(model.getValue(), 'foofarboobarfunction foo() {\n foofarboobarconsole.log(a);\n}');
|
||||
assertSelections(editor, new Selection(1, 1, 1, 4), new Selection(2, 5, 2, 8));
|
||||
|
||||
assert.equal(session.isSelectionWithinPlaceholders(), true);
|
||||
assert.strictEqual(session.isSelectionWithinPlaceholders(), true);
|
||||
|
||||
editor.setSelections([new Selection(1, 1, 1, 1)]);
|
||||
assert.equal(session.isSelectionWithinPlaceholders(), false);
|
||||
assert.strictEqual(session.isSelectionWithinPlaceholders(), false);
|
||||
|
||||
editor.setSelections([new Selection(1, 6, 1, 6), new Selection(2, 10, 2, 10)]);
|
||||
assert.equal(session.isSelectionWithinPlaceholders(), false); // in snippet, outside placeholder
|
||||
assert.strictEqual(session.isSelectionWithinPlaceholders(), false); // in snippet, outside placeholder
|
||||
|
||||
editor.setSelections([new Selection(1, 6, 1, 6), new Selection(2, 10, 2, 10), new Selection(1, 1, 1, 1)]);
|
||||
assert.equal(session.isSelectionWithinPlaceholders(), false); // in snippet, outside placeholder
|
||||
assert.strictEqual(session.isSelectionWithinPlaceholders(), false); // in snippet, outside placeholder
|
||||
|
||||
editor.setSelections([new Selection(1, 6, 1, 6), new Selection(2, 10, 2, 10), new Selection(2, 20, 2, 21)]);
|
||||
assert.equal(session.isSelectionWithinPlaceholders(), false);
|
||||
assert.strictEqual(session.isSelectionWithinPlaceholders(), false);
|
||||
|
||||
// reset selection to placeholder
|
||||
session.next();
|
||||
assert.equal(session.isSelectionWithinPlaceholders(), true);
|
||||
assert.strictEqual(session.isSelectionWithinPlaceholders(), true);
|
||||
assertSelections(editor, new Selection(1, 10, 1, 13), new Selection(2, 14, 2, 17));
|
||||
|
||||
// reset selection to placeholder
|
||||
session.next();
|
||||
assert.equal(session.isSelectionWithinPlaceholders(), true);
|
||||
assert.equal(session.isAtLastPlaceholder, true);
|
||||
assert.strictEqual(session.isSelectionWithinPlaceholders(), true);
|
||||
assert.strictEqual(session.isAtLastPlaceholder, true);
|
||||
assertSelections(editor, new Selection(1, 13, 1, 13), new Selection(2, 17, 2, 17));
|
||||
});
|
||||
|
||||
|
@ -344,20 +344,20 @@ suite('SnippetSession', function () {
|
|||
|
||||
const first = new SnippetSession(editor, 'foo${2:bar}foo$0');
|
||||
first.insert();
|
||||
assert.equal(model.getValue(), 'foobarfoo');
|
||||
assert.strictEqual(model.getValue(), 'foobarfoo');
|
||||
assertSelections(editor, new Selection(1, 4, 1, 7));
|
||||
|
||||
const second = new SnippetSession(editor, 'ba${1:zzzz}$0');
|
||||
second.insert();
|
||||
assert.equal(model.getValue(), 'foobazzzzfoo');
|
||||
assert.strictEqual(model.getValue(), 'foobazzzzfoo');
|
||||
assertSelections(editor, new Selection(1, 6, 1, 10));
|
||||
|
||||
second.next();
|
||||
assert.equal(second.isAtLastPlaceholder, true);
|
||||
assert.strictEqual(second.isAtLastPlaceholder, true);
|
||||
assertSelections(editor, new Selection(1, 10, 1, 10));
|
||||
|
||||
first.next();
|
||||
assert.equal(first.isAtLastPlaceholder, true);
|
||||
assert.strictEqual(first.isAtLastPlaceholder, true);
|
||||
assertSelections(editor, new Selection(1, 13, 1, 13));
|
||||
});
|
||||
|
||||
|
@ -365,11 +365,11 @@ suite('SnippetSession', function () {
|
|||
|
||||
const session = new SnippetSession(editor, 'farboo$0');
|
||||
session.insert();
|
||||
assert.equal(session.isAtLastPlaceholder, true);
|
||||
assert.equal(session.isSelectionWithinPlaceholders(), false);
|
||||
assert.strictEqual(session.isAtLastPlaceholder, true);
|
||||
assert.strictEqual(session.isSelectionWithinPlaceholders(), false);
|
||||
|
||||
editor.trigger('test', 'type', { text: 'XXX' });
|
||||
assert.equal(session.isSelectionWithinPlaceholders(), false);
|
||||
assert.strictEqual(session.isSelectionWithinPlaceholders(), false);
|
||||
});
|
||||
|
||||
test('snippets, typing at beginning', function () {
|
||||
|
@ -379,12 +379,12 @@ suite('SnippetSession', function () {
|
|||
session.insert();
|
||||
|
||||
editor.setSelection(new Selection(1, 2, 1, 2));
|
||||
assert.equal(session.isSelectionWithinPlaceholders(), false);
|
||||
assert.equal(session.isAtLastPlaceholder, true);
|
||||
assert.strictEqual(session.isSelectionWithinPlaceholders(), false);
|
||||
assert.strictEqual(session.isAtLastPlaceholder, true);
|
||||
|
||||
editor.trigger('test', 'type', { text: 'XXX' });
|
||||
assert.equal(model.getLineContent(1), 'fXXXfarboounction foo() {');
|
||||
assert.equal(session.isSelectionWithinPlaceholders(), false);
|
||||
assert.strictEqual(model.getLineContent(1), 'fXXXfarboounction foo() {');
|
||||
assert.strictEqual(session.isSelectionWithinPlaceholders(), false);
|
||||
|
||||
session.next();
|
||||
assertSelections(editor, new Selection(1, 11, 1, 11));
|
||||
|
@ -412,7 +412,7 @@ suite('SnippetSession', function () {
|
|||
const session = new SnippetSession(editor, '@line=$TM_LINE_NUMBER$0');
|
||||
session.insert();
|
||||
|
||||
assert.equal(model.getValue(), '@line=1function foo() {\n @line=2console.log(a);\n}');
|
||||
assert.strictEqual(model.getValue(), '@line=1function foo() {\n @line=2console.log(a);\n}');
|
||||
assertSelections(editor, new Selection(1, 8, 1, 8), new Selection(2, 12, 2, 12));
|
||||
});
|
||||
|
||||
|
@ -428,10 +428,10 @@ suite('SnippetSession', function () {
|
|||
|
||||
session.next();
|
||||
assertSelections(editor, new Selection(1, 22, 1, 22));
|
||||
assert.equal(session.isAtLastPlaceholder, false);
|
||||
assert.strictEqual(session.isAtLastPlaceholder, false);
|
||||
|
||||
session.next();
|
||||
assert.equal(session.isAtLastPlaceholder, true);
|
||||
assert.strictEqual(session.isAtLastPlaceholder, true);
|
||||
assertSelections(editor, new Selection(1, 23, 1, 23));
|
||||
|
||||
session.prev();
|
||||
|
@ -456,8 +456,8 @@ suite('SnippetSession', function () {
|
|||
editor.trigger('test', 'type', { text: 'foo' });
|
||||
session.next();
|
||||
|
||||
assert.equal(model.getValue(), 'bar');
|
||||
assert.equal(session.isAtLastPlaceholder, true);
|
||||
assert.strictEqual(model.getValue(), 'bar');
|
||||
assert.strictEqual(session.isAtLastPlaceholder, true);
|
||||
assertSelections(editor, new Selection(1, 4, 1, 4));
|
||||
});
|
||||
|
||||
|
@ -471,8 +471,8 @@ suite('SnippetSession', function () {
|
|||
editor.trigger('test', 'type', { text: 'foo' });
|
||||
session.next();
|
||||
|
||||
assert.equal(model.getValue(), 'foo baz bar');
|
||||
assert.equal(session.isAtLastPlaceholder, true);
|
||||
assert.strictEqual(model.getValue(), 'foo baz bar');
|
||||
assert.strictEqual(session.isAtLastPlaceholder, true);
|
||||
assertSelections(editor, new Selection(1, 12, 1, 12));
|
||||
});
|
||||
|
||||
|
@ -493,8 +493,8 @@ suite('SnippetSession', function () {
|
|||
assertSelections(editor, new Selection(1, 16, 1, 16));
|
||||
session.next();
|
||||
|
||||
assert.equal(model.getValue(), 'clk : std_logic;\n');
|
||||
assert.equal(session.isAtLastPlaceholder, true);
|
||||
assert.strictEqual(model.getValue(), 'clk : std_logic;\n');
|
||||
assert.strictEqual(session.isAtLastPlaceholder, true);
|
||||
assertSelections(editor, new Selection(2, 1, 2, 1));
|
||||
});
|
||||
|
||||
|
@ -532,8 +532,8 @@ suite('SnippetSession', function () {
|
|||
editor.trigger('test', 'type', { text: 'string' });
|
||||
session.next();
|
||||
|
||||
assert.equal(model.getValue(), expected);
|
||||
assert.equal(session.isAtLastPlaceholder, true);
|
||||
assert.strictEqual(model.getValue(), expected);
|
||||
assert.strictEqual(session.isAtLastPlaceholder, true);
|
||||
assertSelections(editor, new Selection(4, 2, 4, 2));
|
||||
|
||||
});
|
||||
|
@ -556,8 +556,8 @@ suite('SnippetSession', function () {
|
|||
editor.trigger('test', 'type', { text: ' := \'1\'' });
|
||||
session.next();
|
||||
|
||||
assert.equal(model.getValue(), 'clk : std_logic := \'1\';\n');
|
||||
assert.equal(session.isAtLastPlaceholder, true);
|
||||
assert.strictEqual(model.getValue(), 'clk : std_logic := \'1\';\n');
|
||||
assert.strictEqual(session.isAtLastPlaceholder, true);
|
||||
assertSelections(editor, new Selection(2, 1, 2, 1));
|
||||
});
|
||||
|
||||
|
@ -570,13 +570,13 @@ suite('SnippetSession', function () {
|
|||
assertSelections(editor, new Selection(1, 1, 1, 2), new Selection(1, 5, 1, 6));
|
||||
session.next();
|
||||
|
||||
assert.equal(model.getValue(), '{fff}');
|
||||
assert.strictEqual(model.getValue(), '{fff}');
|
||||
assertSelections(editor, new Selection(1, 2, 1, 5));
|
||||
editor.trigger('test', 'type', { text: 'ggg' });
|
||||
session.next();
|
||||
|
||||
assert.equal(model.getValue(), '{ggg}');
|
||||
assert.equal(session.isAtLastPlaceholder, true);
|
||||
assert.strictEqual(model.getValue(), '{ggg}');
|
||||
assert.strictEqual(session.isAtLastPlaceholder, true);
|
||||
assertSelections(editor, new Selection(1, 6, 1, 6));
|
||||
});
|
||||
|
||||
|
@ -584,7 +584,7 @@ suite('SnippetSession', function () {
|
|||
editor.getModel().setValue('');
|
||||
const session = new SnippetSession(editor, '${1:{}${2:fff}${1/[\\{]/}/}$0');
|
||||
session.insert();
|
||||
assert.equal(editor.getModel().getValue(), '{fff{');
|
||||
assert.strictEqual(editor.getModel().getValue(), '{fff{');
|
||||
|
||||
assertSelections(editor, new Selection(1, 1, 1, 2), new Selection(1, 5, 1, 6));
|
||||
session.next();
|
||||
|
@ -599,25 +599,25 @@ suite('SnippetSession', function () {
|
|||
|
||||
editor.trigger('test', 'type', { text: '1' });
|
||||
editor.trigger('test', 'type', { text: '\n' });
|
||||
assert.equal(editor.getModel()!.getValue(), 'test 1\n');
|
||||
assert.strictEqual(editor.getModel()!.getValue(), 'test 1\n');
|
||||
|
||||
session.merge('test ${1:replaceme}');
|
||||
editor.trigger('test', 'type', { text: '2' });
|
||||
editor.trigger('test', 'type', { text: '\n' });
|
||||
|
||||
assert.equal(editor.getModel()!.getValue(), 'test 1\ntest 2\n');
|
||||
assert.strictEqual(editor.getModel()!.getValue(), 'test 1\ntest 2\n');
|
||||
|
||||
session.merge('test ${1:replaceme}');
|
||||
editor.trigger('test', 'type', { text: '3' });
|
||||
editor.trigger('test', 'type', { text: '\n' });
|
||||
|
||||
assert.equal(editor.getModel()!.getValue(), 'test 1\ntest 2\ntest 3\n');
|
||||
assert.strictEqual(editor.getModel()!.getValue(), 'test 1\ntest 2\ntest 3\n');
|
||||
|
||||
session.merge('test ${1:replaceme}');
|
||||
editor.trigger('test', 'type', { text: '4' });
|
||||
editor.trigger('test', 'type', { text: '\n' });
|
||||
|
||||
assert.equal(editor.getModel()!.getValue(), 'test 1\ntest 2\ntest 3\ntest 4\n');
|
||||
assert.strictEqual(editor.getModel()!.getValue(), 'test 1\ntest 2\ntest 3\ntest 4\n');
|
||||
});
|
||||
|
||||
test('Snippet variable text isn\'t whitespace normalised, #31124', function () {
|
||||
|
@ -642,7 +642,7 @@ suite('SnippetSession', function () {
|
|||
'end'
|
||||
].join('\n');
|
||||
|
||||
assert.equal(editor.getModel()!.getValue(), expected);
|
||||
assert.strictEqual(editor.getModel()!.getValue(), expected);
|
||||
|
||||
editor.getModel()!.setValue([
|
||||
'start',
|
||||
|
@ -665,7 +665,7 @@ suite('SnippetSession', function () {
|
|||
'end'
|
||||
].join('\n');
|
||||
|
||||
assert.equal(editor.getModel()!.getValue(), expected);
|
||||
assert.strictEqual(editor.getModel()!.getValue(), expected);
|
||||
});
|
||||
|
||||
test('Selecting text from left to right, and choosing item messes up code, #31199', function () {
|
||||
|
@ -680,7 +680,7 @@ suite('SnippetSession', function () {
|
|||
|
||||
editor.setSelections([new Selection(1, 9, 1, 12)]);
|
||||
new SnippetSession(editor, 'far', { overwriteBefore: 3, overwriteAfter: 0, adjustWhitespace: true, clipboardText: undefined, overtypingCapturer: undefined }).insert();
|
||||
assert.equal(model.getValue(), 'console.far');
|
||||
assert.strictEqual(model.getValue(), 'console.far');
|
||||
});
|
||||
|
||||
test('Tabs don\'t get replaced with spaces in snippet transformations #103818', function () {
|
||||
|
|
|
@ -50,9 +50,9 @@ suite('Snippet Variables Resolver', function () {
|
|||
const variable = <Variable>snippet.children[0];
|
||||
variable.resolve(resolver);
|
||||
if (variable.children.length === 0) {
|
||||
assert.equal(undefined, expected);
|
||||
assert.strictEqual(undefined, expected);
|
||||
} else {
|
||||
assert.equal(variable.toString(), expected);
|
||||
assert.strictEqual(variable.toString(), expected);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,17 +129,17 @@ suite('Snippet Variables Resolver', function () {
|
|||
|
||||
test('TextmateSnippet, resolve variable', function () {
|
||||
const snippet = new SnippetParser().parse('"$TM_CURRENT_WORD"', true);
|
||||
assert.equal(snippet.toString(), '""');
|
||||
assert.strictEqual(snippet.toString(), '""');
|
||||
snippet.resolveVariables(resolver);
|
||||
assert.equal(snippet.toString(), '"this"');
|
||||
assert.strictEqual(snippet.toString(), '"this"');
|
||||
|
||||
});
|
||||
|
||||
test('TextmateSnippet, resolve variable with default', function () {
|
||||
const snippet = new SnippetParser().parse('"${TM_CURRENT_WORD:foo}"', true);
|
||||
assert.equal(snippet.toString(), '"foo"');
|
||||
assert.strictEqual(snippet.toString(), '"foo"');
|
||||
snippet.resolveVariables(resolver);
|
||||
assert.equal(snippet.toString(), '"this"');
|
||||
assert.strictEqual(snippet.toString(), '"this"');
|
||||
});
|
||||
|
||||
test('More useful environment variables for snippets, #32737', function () {
|
||||
|
@ -171,14 +171,14 @@ suite('Snippet Variables Resolver', function () {
|
|||
.resolveVariables({ resolve(variable) { return varValue || variable.name; } });
|
||||
|
||||
const actual = snippet.toString();
|
||||
assert.equal(actual, expected);
|
||||
assert.strictEqual(actual, expected);
|
||||
}
|
||||
|
||||
test('Variable Snippet Transform', function () {
|
||||
|
||||
const snippet = new SnippetParser().parse('name=${TM_FILENAME/(.*)\\..+$/$1/}', true);
|
||||
snippet.resolveVariables(resolver);
|
||||
assert.equal(snippet.toString(), 'name=text');
|
||||
assert.strictEqual(snippet.toString(), 'name=text');
|
||||
|
||||
assertVariableResolve2('${ThisIsAVar/([A-Z]).*(Var)/$2/}', 'Var');
|
||||
assertVariableResolve2('${ThisIsAVar/([A-Z]).*(Var)/$2-${1:/downcase}/}', 'Var-t');
|
||||
|
@ -269,7 +269,7 @@ suite('Snippet Variables Resolver', function () {
|
|||
const snippet = new SnippetParser().parse(`$${varName}`);
|
||||
const variable = <Variable>snippet.children[0];
|
||||
|
||||
assert.equal(variable.resolve(resolver), true, `${varName} failed to resolve`);
|
||||
assert.strictEqual(variable.resolve(resolver), true, `${varName} failed to resolve`);
|
||||
}
|
||||
|
||||
test('Add time variables for snippets #41631, #43140', function () {
|
||||
|
@ -294,10 +294,10 @@ suite('Snippet Variables Resolver', function () {
|
|||
|
||||
const snippet = new SnippetParser().parse('${TM_LINE_NUMBER/(10)/${1:?It is:It is not}/} line 10', true);
|
||||
snippet.resolveVariables({ resolve() { return '10'; } });
|
||||
assert.equal(snippet.toString(), 'It is line 10');
|
||||
assert.strictEqual(snippet.toString(), 'It is line 10');
|
||||
|
||||
snippet.resolveVariables({ resolve() { return '11'; } });
|
||||
assert.equal(snippet.toString(), 'It is not line 10');
|
||||
assert.strictEqual(snippet.toString(), 'It is not line 10');
|
||||
});
|
||||
|
||||
test('Add workspace name and folder variables for snippets #68261', function () {
|
||||
|
|
|
@ -18,7 +18,7 @@ export const javascriptOnEnterRules = [
|
|||
}, {
|
||||
// e.g. * ...|
|
||||
beforeText: /^(\t|[ ])*[ ]\*([ ]([^\*]|\*(?!\/))*)?$/,
|
||||
oneLineAboveText: /(?=^(\s*(\/\*\*|\*)).*)(?=(?!(\s*\*\/)))/,
|
||||
previousLineText: /(?=^(\s*(\/\*\*|\*)).*)(?=(?!(\s*\*\/)))/,
|
||||
action: { indentAction: IndentAction.None, appendText: '* ' }
|
||||
}, {
|
||||
// e.g. */|
|
||||
|
|
|
@ -51,8 +51,8 @@ suite('OnEnter', () => {
|
|||
let support = new OnEnterSupport({
|
||||
onEnterRules: javascriptOnEnterRules
|
||||
});
|
||||
let testIndentAction = (oneLineAboveText: string, beforeText: string, afterText: string, expectedIndentAction: IndentAction | null, expectedAppendText: string | null, removeText: number = 0) => {
|
||||
let actual = support.onEnter(EditorAutoIndentStrategy.Advanced, oneLineAboveText, beforeText, afterText);
|
||||
let testIndentAction = (previousLineText: string, beforeText: string, afterText: string, expectedIndentAction: IndentAction | null, expectedAppendText: string | null, removeText: number = 0) => {
|
||||
let actual = support.onEnter(EditorAutoIndentStrategy.Advanced, previousLineText, beforeText, afterText);
|
||||
if (expectedIndentAction === null) {
|
||||
assert.strictEqual(actual, null, 'isNull:' + beforeText);
|
||||
} else {
|
||||
|
|
2
src/vs/monaco.d.ts
vendored
2
src/vs/monaco.d.ts
vendored
|
@ -5417,7 +5417,7 @@ declare namespace monaco.languages {
|
|||
/**
|
||||
* This rule will only execute if the text above the this line matches this regular expression.
|
||||
*/
|
||||
oneLineAboveText?: RegExp;
|
||||
previousLineText?: RegExp;
|
||||
/**
|
||||
* The action to execute.
|
||||
*/
|
||||
|
|
|
@ -42,12 +42,16 @@ export class ExtensionIdentifierWithVersion implements IExtensionIdentifierWithV
|
|||
}
|
||||
}
|
||||
|
||||
export function getExtensionId(publisher: string, name: string): string {
|
||||
return `${publisher}.${name}`;
|
||||
}
|
||||
|
||||
export function adoptToGalleryExtensionId(id: string): string {
|
||||
return id.toLocaleLowerCase();
|
||||
}
|
||||
|
||||
export function getGalleryExtensionId(publisher: string, name: string): string {
|
||||
return `${publisher.toLocaleLowerCase()}.${name.toLocaleLowerCase()}`;
|
||||
return adoptToGalleryExtensionId(getExtensionId(publisher, name));
|
||||
}
|
||||
|
||||
export function groupByExtension<T>(extensions: T[], getExtensionIdentifier: (t: T) => IExtensionIdentifier): T[][] {
|
||||
|
|
|
@ -826,7 +826,7 @@ function workbenchTreeDataPreamble<T, TFilterData, TOptions extends IAbstractTre
|
|||
};
|
||||
|
||||
const accessibilityOn = accessibilityService.isScreenReaderOptimized();
|
||||
const keyboardNavigation = accessibilityOn ? 'simple' : configurationService.getValue<string>(keyboardNavigationSettingKey);
|
||||
const keyboardNavigation = options.simpleKeyboardNavigation || accessibilityOn ? 'simple' : configurationService.getValue<string>(keyboardNavigationSettingKey);
|
||||
const horizontalScrolling = options.horizontalScrolling !== undefined ? options.horizontalScrolling : configurationService.getValue<boolean>(horizontalScrollingKey);
|
||||
const [workbenchListOptions, disposable] = toWorkbenchListOptions(options, configurationService, keybindingService);
|
||||
const additionalScrollHeight = options.additionalScrollHeight;
|
||||
|
|
|
@ -42,6 +42,24 @@ export interface ITunnelProvider {
|
|||
forwardPort(tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions): Promise<RemoteTunnel | undefined> | undefined;
|
||||
}
|
||||
|
||||
export interface ITunnel {
|
||||
remoteAddress: { port: number, host: string };
|
||||
|
||||
/**
|
||||
* The complete local address(ex. localhost:1234)
|
||||
*/
|
||||
localAddress: string;
|
||||
|
||||
public?: boolean;
|
||||
|
||||
/**
|
||||
* Implementers of Tunnel should fire onDidDispose when dispose is called.
|
||||
*/
|
||||
onDidDispose: Event<void>;
|
||||
|
||||
dispose(): Promise<void> | void;
|
||||
}
|
||||
|
||||
export interface ITunnelService {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
|
@ -177,6 +195,7 @@ export abstract class AbstractTunnelService implements ITunnelService {
|
|||
|
||||
const resolvedTunnel = this.retainOrCreateTunnel(addressProvider, remoteHost, remotePort, localPort, elevateIfNeeded, isPublic);
|
||||
if (!resolvedTunnel) {
|
||||
this.logService.trace(`Tunnel was not created.`);
|
||||
return resolvedTunnel;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { adoptToGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IStorageService, IStorageValueChangeEvent, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
|
||||
|
@ -32,7 +33,7 @@ export class ExtensionsStorageSyncService extends Disposable implements IExtensi
|
|||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
private static toKey(extension: IExtensionIdWithVersion): string {
|
||||
return `extensionKeys/${extension.id}@${extension.version}`;
|
||||
return `extensionKeys/${adoptToGalleryExtensionId(extension.id)}@${extension.version}`;
|
||||
}
|
||||
|
||||
private static fromKey(key: string): IExtensionIdWithVersion | undefined {
|
||||
|
|
|
@ -11,7 +11,7 @@ import { Event } from 'vs/base/common/event';
|
|||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IExtensionManagementService, IExtensionGalleryService, IGlobalExtensionEnablementService, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { ExtensionType, IExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { areSameExtensions, getExtensionId, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { merge } from 'vs/platform/userDataSync/common/extensionsMerge';
|
||||
|
@ -73,6 +73,15 @@ async function parseAndMigrateExtensions(syncData: ISyncData, extensionManagemen
|
|||
return extensions;
|
||||
}
|
||||
|
||||
function getExtensionStorageState(publisher: string, name: string, storageService: IStorageService): IStringDictionary<any> {
|
||||
const extensionStorageValue = storageService.get(getExtensionId(publisher, name) /* use the same id used in extension host */, StorageScope.GLOBAL) || '{}';
|
||||
return JSON.parse(extensionStorageValue);
|
||||
}
|
||||
|
||||
function storeExtensionStorageState(publisher: string, name: string, extensionState: IStringDictionary<any>, storageService: IStorageService): void {
|
||||
storageService.store(getExtensionId(publisher, name) /* use the same id used in extension host */, JSON.stringify(extensionState), StorageScope.GLOBAL, StorageTarget.MACHINE);
|
||||
}
|
||||
|
||||
export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUserDataSynchroniser {
|
||||
|
||||
private static readonly EXTENSIONS_DATA_URI = URI.from({ scheme: USER_DATA_SYNC_SCHEME, authority: 'extensions', path: `/extensions.json` });
|
||||
|
@ -99,7 +108,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
|
|||
@IUserDataSyncBackupStoreService userDataSyncBackupStoreService: IUserDataSyncBackupStoreService,
|
||||
@IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService,
|
||||
@IGlobalExtensionEnablementService private readonly extensionEnablementService: IGlobalExtensionEnablementService,
|
||||
@IIgnoredExtensionsManagementService private readonly extensionSyncManagementService: IIgnoredExtensionsManagementService,
|
||||
@IIgnoredExtensionsManagementService private readonly ignoredExtensionsManagementService: IIgnoredExtensionsManagementService,
|
||||
@IUserDataSyncLogService logService: IUserDataSyncLogService,
|
||||
@IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
|
@ -125,7 +134,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
|
|||
|
||||
const installedExtensions = await this.extensionManagementService.getInstalled();
|
||||
const localExtensions = this.getLocalExtensions(installedExtensions);
|
||||
const ignoredExtensions = this.extensionSyncManagementService.getIgnoredExtensions(installedExtensions);
|
||||
const ignoredExtensions = this.ignoredExtensionsManagementService.getIgnoredExtensions(installedExtensions);
|
||||
|
||||
if (remoteExtensions) {
|
||||
this.logService.trace(`${this.syncResourceLogLabel}: Merging remote extensions with local extensions...`);
|
||||
|
@ -209,7 +218,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
|
|||
|
||||
private async acceptLocal(resourcePreview: IExtensionResourcePreview): Promise<IExtensionResourceMergeResult> {
|
||||
const installedExtensions = await this.extensionManagementService.getInstalled();
|
||||
const ignoredExtensions = this.extensionSyncManagementService.getIgnoredExtensions(installedExtensions);
|
||||
const ignoredExtensions = this.ignoredExtensionsManagementService.getIgnoredExtensions(installedExtensions);
|
||||
const mergeResult = merge(resourcePreview.localExtensions, null, null, resourcePreview.skippedExtensions, ignoredExtensions);
|
||||
const { added, removed, updated, remote } = mergeResult;
|
||||
return {
|
||||
|
@ -225,7 +234,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
|
|||
|
||||
private async acceptRemote(resourcePreview: IExtensionResourcePreview): Promise<IExtensionResourceMergeResult> {
|
||||
const installedExtensions = await this.extensionManagementService.getInstalled();
|
||||
const ignoredExtensions = this.extensionSyncManagementService.getIgnoredExtensions(installedExtensions);
|
||||
const ignoredExtensions = this.ignoredExtensionsManagementService.getIgnoredExtensions(installedExtensions);
|
||||
const remoteExtensions = resourcePreview.remoteContent ? JSON.parse(resourcePreview.remoteContent) : null;
|
||||
if (remoteExtensions !== null) {
|
||||
const mergeResult = merge(resourcePreview.localExtensions, remoteExtensions, resourcePreview.localExtensions, [], ignoredExtensions);
|
||||
|
@ -285,7 +294,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
|
|||
async resolveContent(uri: URI): Promise<string | null> {
|
||||
if (this.extUri.isEqual(uri, ExtensionsSynchroniser.EXTENSIONS_DATA_URI)) {
|
||||
const installedExtensions = await this.extensionManagementService.getInstalled();
|
||||
const ignoredExtensions = this.extensionSyncManagementService.getIgnoredExtensions(installedExtensions);
|
||||
const ignoredExtensions = this.ignoredExtensionsManagementService.getIgnoredExtensions(installedExtensions);
|
||||
const localExtensions = this.getLocalExtensions(installedExtensions).filter(e => !ignoredExtensions.some(id => areSameExtensions({ id }, e.identifier)));
|
||||
return this.format(localExtensions);
|
||||
}
|
||||
|
@ -363,7 +372,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
|
|||
// Builtin Extension Sync: Enablement & State
|
||||
if (installedExtension && installedExtension.isBuiltin) {
|
||||
if (e.state && installedExtension.manifest.version === e.version) {
|
||||
this.updateExtensionState(e.state, e.identifier.id, installedExtension.manifest.version);
|
||||
this.updateExtensionState(e.state, installedExtension.manifest.publisher, installedExtension.manifest.name, installedExtension.manifest.version);
|
||||
}
|
||||
if (e.disabled) {
|
||||
this.logService.trace(`${this.syncResourceLogLabel}: Disabling extension...`, e.identifier.id);
|
||||
|
@ -382,14 +391,16 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
|
|||
const extension = await this.extensionGalleryService.getCompatibleExtension(e.identifier);
|
||||
|
||||
/* Update extension state only if
|
||||
* extension is installed and version is same as synced version or
|
||||
* extension is not installed and installable
|
||||
* extension is installed and version is same as synced version or
|
||||
* extension is not installed and installable
|
||||
*/
|
||||
if (e.state &&
|
||||
(installedExtension ? installedExtension.manifest.version === e.version /* Installed and has same version */
|
||||
: !!extension /* Installable */)
|
||||
) {
|
||||
this.updateExtensionState(e.state, e.identifier.id, installedExtension?.manifest.version);
|
||||
const publisher = installedExtension ? installedExtension.manifest.publisher : extension!.publisher;
|
||||
const name = installedExtension ? installedExtension.manifest.name : extension!.name;
|
||||
this.updateExtensionState(e.state, publisher, name, installedExtension?.manifest.version);
|
||||
}
|
||||
|
||||
if (extension) {
|
||||
|
@ -436,15 +447,15 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
|
|||
return newSkippedExtensions;
|
||||
}
|
||||
|
||||
private updateExtensionState(state: IStringDictionary<any>, id: string, version?: string): void {
|
||||
const extensionState = JSON.parse(this.storageService.get(id, StorageScope.GLOBAL) || '{}');
|
||||
const keys = version ? this.extensionsStorageSyncService.getKeysForSync({ id, version }) : undefined;
|
||||
private updateExtensionState(state: IStringDictionary<any>, publisher: string, name: string, version: string | undefined): void {
|
||||
const extensionState = getExtensionStorageState(publisher, name, this.storageService);
|
||||
const keys = version ? this.extensionsStorageSyncService.getKeysForSync({ id: getGalleryExtensionId(publisher, name), version }) : undefined;
|
||||
if (keys) {
|
||||
keys.forEach(key => extensionState[key] = state[key]);
|
||||
} else {
|
||||
forEach(state, ({ key, value }) => extensionState[key] = value);
|
||||
}
|
||||
this.storageService.store(id, JSON.stringify(extensionState), StorageScope.GLOBAL, StorageTarget.MACHINE);
|
||||
storeExtensionStorageState(publisher, name, extensionState, this.storageService);
|
||||
}
|
||||
|
||||
private parseExtensions(syncData: ISyncData): ISyncExtension[] {
|
||||
|
@ -465,8 +476,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
|
|||
try {
|
||||
const keys = this.extensionsStorageSyncService.getKeysForSync({ id: identifier.id, version: manifest.version });
|
||||
if (keys) {
|
||||
const extensionStorageValue = this.storageService.get(identifier.id, StorageScope.GLOBAL) || '{}';
|
||||
const extensionStorageState = JSON.parse(extensionStorageValue);
|
||||
const extensionStorageState = getExtensionStorageState(manifest.publisher, manifest.name, this.storageService);
|
||||
syncExntesion.state = Object.keys(extensionStorageState).reduce((state: IStringDictionary<any>, key) => {
|
||||
if (keys.includes(key)) {
|
||||
state[key] = extensionStorageState[key];
|
||||
|
@ -490,6 +500,7 @@ export class ExtensionsInitializer extends AbstractInitializer {
|
|||
@IExtensionGalleryService private readonly galleryService: IExtensionGalleryService,
|
||||
@IGlobalExtensionEnablementService private readonly extensionEnablementService: IGlobalExtensionEnablementService,
|
||||
@IStorageService private readonly storageService: IStorageService,
|
||||
@IIgnoredExtensionsManagementService private readonly ignoredExtensionsManagementService: IIgnoredExtensionsManagementService,
|
||||
@IFileService fileService: IFileService,
|
||||
@IEnvironmentService environmentService: IEnvironmentService,
|
||||
@IUserDataSyncLogService logService: IUserDataSyncLogService,
|
||||
|
@ -511,12 +522,18 @@ export class ExtensionsInitializer extends AbstractInitializer {
|
|||
const newlyEnabledExtensions: ILocalExtension[] = [];
|
||||
const installedExtensions = await this.extensionManagementService.getInstalled();
|
||||
const newExtensionsToSync = new Map<string, ISyncExtension>();
|
||||
const installedExtensionsToSync: ISyncExtension[] = [];
|
||||
const installedExtensionsToSync: { syncExtension: ISyncExtension, installedExtension: ILocalExtension }[] = [];
|
||||
const toInstall: { names: string[], uuids: string[] } = { names: [], uuids: [] };
|
||||
const toDisable: IExtensionIdentifier[] = [];
|
||||
for (const extension of remoteExtensions) {
|
||||
if (installedExtensions.some(i => areSameExtensions(i.identifier, extension.identifier))) {
|
||||
installedExtensionsToSync.push(extension);
|
||||
if (this.ignoredExtensionsManagementService.hasToNeverSyncExtension(extension.identifier.id)) {
|
||||
// Skip extension ignored to sync
|
||||
continue;
|
||||
}
|
||||
|
||||
const installedExtension = installedExtensions.find(i => areSameExtensions(i.identifier, extension.identifier));
|
||||
if (installedExtension) {
|
||||
installedExtensionsToSync.push({ syncExtension: extension, installedExtension });
|
||||
if (extension.disabled) {
|
||||
toDisable.push(extension.identifier);
|
||||
}
|
||||
|
@ -536,11 +553,11 @@ export class ExtensionsInitializer extends AbstractInitializer {
|
|||
}
|
||||
|
||||
// 1. Initialise already installed extensions state
|
||||
for (const extensionToSync of installedExtensionsToSync) {
|
||||
if (extensionToSync.state) {
|
||||
const extensionState = JSON.parse(this.storageService.get(extensionToSync.identifier.id, StorageScope.GLOBAL) || '{}');
|
||||
forEach(extensionToSync.state, ({ key, value }) => extensionState[key] = value);
|
||||
this.storageService.store(extensionToSync.identifier.id, JSON.stringify(extensionState), StorageScope.GLOBAL, StorageTarget.MACHINE);
|
||||
for (const { syncExtension, installedExtension } of installedExtensionsToSync) {
|
||||
if (syncExtension.state) {
|
||||
const extensionState = getExtensionStorageState(installedExtension.manifest.publisher, installedExtension.manifest.name, this.storageService);
|
||||
forEach(syncExtension.state, ({ key, value }) => extensionState[key] = value);
|
||||
storeExtensionStorageState(installedExtension.manifest.publisher, installedExtension.manifest.name, extensionState, this.storageService);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -560,7 +577,7 @@ export class ExtensionsInitializer extends AbstractInitializer {
|
|||
try {
|
||||
const extensionToSync = newExtensionsToSync.get(galleryExtension.identifier.id.toLowerCase())!;
|
||||
if (extensionToSync.state) {
|
||||
this.storageService.store(extensionToSync.identifier.id, JSON.stringify(extensionToSync.state), StorageScope.GLOBAL, StorageTarget.MACHINE);
|
||||
storeExtensionStorageState(galleryExtension.publisher, galleryExtension.name, extensionToSync.state, this.storageService);
|
||||
}
|
||||
this.logService.trace(`Installing extension...`, galleryExtension.identifier.id);
|
||||
const local = await this.extensionManagementService.installFromGallery(galleryExtension, { isMachineScoped: false } /* pass options to prevent install and sync dialog in web */);
|
||||
|
|
53
src/vs/vscode.d.ts
vendored
53
src/vs/vscode.d.ts
vendored
|
@ -4697,6 +4697,10 @@ declare module 'vscode' {
|
|||
* This rule will only execute if the text after the cursor matches this regular expression.
|
||||
*/
|
||||
afterText?: RegExp;
|
||||
/**
|
||||
* This rule will only execute if the text above the current line matches this regular expression.
|
||||
*/
|
||||
previousLineText?: RegExp;
|
||||
/**
|
||||
* The action to execute.
|
||||
*/
|
||||
|
@ -5821,6 +5825,11 @@ declare module 'vscode' {
|
|||
setKeysForSync(keys: string[]): void;
|
||||
};
|
||||
|
||||
/**
|
||||
* A storage utility for secrets.
|
||||
*/
|
||||
readonly secrets: SecretStorage;
|
||||
|
||||
/**
|
||||
* The uri of the directory containing the extension.
|
||||
*/
|
||||
|
@ -5958,6 +5967,48 @@ declare module 'vscode' {
|
|||
update(key: string, value: any): Thenable<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* The event data that is fired when a secret is added or removed.
|
||||
*/
|
||||
export interface SecretStorageChangeEvent {
|
||||
/**
|
||||
* The key of the secret that has changed.
|
||||
*/
|
||||
readonly key: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a storage utility for secrets, information that is
|
||||
* sensitive.
|
||||
*/
|
||||
export interface SecretStorage {
|
||||
/**
|
||||
* Retrieve a secret that was stored with key. Returns undefined if there
|
||||
* is no password matching that key.
|
||||
* @param key The key the secret was stored under.
|
||||
* @returns The stored value or `undefined`.
|
||||
*/
|
||||
get(key: string): Thenable<string | undefined>;
|
||||
|
||||
/**
|
||||
* Store a secret under a given key.
|
||||
* @param key The key to store the secret under.
|
||||
* @param value The secret.
|
||||
*/
|
||||
store(key: string, value: string): Thenable<void>;
|
||||
|
||||
/**
|
||||
* Remove a secret from storage.
|
||||
* @param key The key the secret was stored under.
|
||||
*/
|
||||
delete(key: string): Thenable<void>;
|
||||
|
||||
/**
|
||||
* Fires when a secret is stored or deleted.
|
||||
*/
|
||||
onDidChange: Event<SecretStorageChangeEvent>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a color theme kind.
|
||||
*/
|
||||
|
@ -12161,7 +12212,7 @@ declare module 'vscode' {
|
|||
/**
|
||||
* Optional reaction handler for creating and deleting reactions on a [comment](#Comment).
|
||||
*/
|
||||
reactionHandler?: (comment: Comment, reaction: CommentReaction) => Promise<void>;
|
||||
reactionHandler?: (comment: Comment, reaction: CommentReaction) => Thenable<void>;
|
||||
|
||||
/**
|
||||
* Dispose this comment controller.
|
||||
|
|
78
src/vs/vscode.proposed.d.ts
vendored
78
src/vs/vscode.proposed.d.ts
vendored
|
@ -881,15 +881,6 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region https://github.com/microsoft/vscode/issues/58440
|
||||
export interface OnEnterRule {
|
||||
/**
|
||||
* This rule will only execute if the text above the line matches this regular expression.
|
||||
*/
|
||||
oneLineAboveText?: RegExp;
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region Tree View: https://github.com/microsoft/vscode/issues/61313 @alexr00
|
||||
export interface TreeView<T> extends Disposable {
|
||||
reveal(element: T | undefined, options?: { select?: boolean, focus?: boolean, expand?: boolean | number; }): Thenable<void>;
|
||||
|
@ -1577,16 +1568,16 @@ declare module 'vscode' {
|
|||
* resolve the raw content for `uri` as the resouce is not necessarily a file on disk.
|
||||
*/
|
||||
// eslint-disable-next-line vscode-dts-provider-naming
|
||||
openNotebook(uri: Uri, openContext: NotebookDocumentOpenContext): NotebookData | Promise<NotebookData>;
|
||||
openNotebook(uri: Uri, openContext: NotebookDocumentOpenContext): NotebookData | Thenable<NotebookData>;
|
||||
// eslint-disable-next-line vscode-dts-provider-naming
|
||||
// eslint-disable-next-line vscode-dts-cancellation
|
||||
resolveNotebook(document: NotebookDocument, webview: NotebookCommunication): Promise<void>;
|
||||
resolveNotebook(document: NotebookDocument, webview: NotebookCommunication): Thenable<void>;
|
||||
// eslint-disable-next-line vscode-dts-provider-naming
|
||||
saveNotebook(document: NotebookDocument, cancellation: CancellationToken): Promise<void>;
|
||||
saveNotebook(document: NotebookDocument, cancellation: CancellationToken): Thenable<void>;
|
||||
// eslint-disable-next-line vscode-dts-provider-naming
|
||||
saveNotebookAs(targetResource: Uri, document: NotebookDocument, cancellation: CancellationToken): Promise<void>;
|
||||
saveNotebookAs(targetResource: Uri, document: NotebookDocument, cancellation: CancellationToken): Thenable<void>;
|
||||
// eslint-disable-next-line vscode-dts-provider-naming
|
||||
backupNotebook(document: NotebookDocument, context: NotebookDocumentBackupContext, cancellation: CancellationToken): Promise<NotebookDocumentBackup>;
|
||||
backupNotebook(document: NotebookDocument, context: NotebookDocumentBackupContext, cancellation: CancellationToken): Thenable<NotebookDocumentBackup>;
|
||||
}
|
||||
|
||||
export interface NotebookKernel {
|
||||
|
@ -1685,7 +1676,7 @@ declare module 'vscode' {
|
|||
): Disposable;
|
||||
|
||||
export function createNotebookEditorDecorationType(options: NotebookDecorationRenderOptions): NotebookEditorDecorationType;
|
||||
export function openNotebookDocument(uri: Uri, viewType?: string): Promise<NotebookDocument>;
|
||||
export function openNotebookDocument(uri: Uri, viewType?: string): Thenable<NotebookDocument>;
|
||||
export const onDidOpenNotebookDocument: Event<NotebookDocument>;
|
||||
export const onDidCloseNotebookDocument: Event<NotebookDocument>;
|
||||
export const onDidSaveNotebookDocument: Event<NotebookDocument>;
|
||||
|
@ -1729,7 +1720,7 @@ declare module 'vscode' {
|
|||
export const onDidChangeActiveNotebookEditor: Event<NotebookEditor | undefined>;
|
||||
export const onDidChangeNotebookEditorSelection: Event<NotebookEditorSelectionChangeEvent>;
|
||||
export const onDidChangeNotebookEditorVisibleRanges: Event<NotebookEditorVisibleRangesChangeEvent>;
|
||||
export function showNotebookDocument(document: NotebookDocument, options?: NotebookDocumentShowOptions): Promise<NotebookEditor>;
|
||||
export function showNotebookDocument(document: NotebookDocument, options?: NotebookDocumentShowOptions): Thenable<NotebookEditor>;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
@ -1940,7 +1931,7 @@ declare module 'vscode' {
|
|||
}
|
||||
|
||||
export namespace languages {
|
||||
export function getTokenInformationAtPosition(document: TextDocument, position: Position): Promise<TokenInformation>;
|
||||
export function getTokenInformationAtPosition(document: TextDocument, position: Position): Thenable<TokenInformation>;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
@ -2507,54 +2498,21 @@ declare module 'vscode' {
|
|||
export function openExternal(target: Uri, options?: OpenExternalOptions): Thenable<boolean>;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
//#endregionn
|
||||
|
||||
//#region https://github.com/microsoft/vscode/issues/112249
|
||||
//#region https://github.com/Microsoft/vscode/issues/15178
|
||||
|
||||
/**
|
||||
* The event data that is fired when a secret is added or removed.
|
||||
*/
|
||||
export interface SecretStorageChangeEvent {
|
||||
/**
|
||||
* The key of the secret that has changed.
|
||||
*/
|
||||
key: string;
|
||||
// TODO@API must be a class
|
||||
export interface OpenEditorInfo {
|
||||
name: string;
|
||||
resource: Uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a storage utility for secrets, information that is
|
||||
* sensitive.
|
||||
*/
|
||||
export interface SecretStorage {
|
||||
/**
|
||||
* Retrieve a secret that was stored with key. Returns undefined if there
|
||||
* is no password matching that key.
|
||||
* @param key The key the password was stored under.
|
||||
* @returns The stored value or `undefined`.
|
||||
*/
|
||||
get(key: string): Thenable<string | undefined>;
|
||||
export namespace window {
|
||||
export const openEditors: ReadonlyArray<OpenEditorInfo>;
|
||||
|
||||
/**
|
||||
* Store a secret under a given key.
|
||||
* @param key The key to store the password under.
|
||||
* @param value The password.
|
||||
*/
|
||||
set(key: string, value: string): Thenable<void>;
|
||||
|
||||
/**
|
||||
* Remove a secret from storage.
|
||||
* @param key The key the password was stored under.
|
||||
*/
|
||||
delete(key: string): Thenable<void>;
|
||||
|
||||
/**
|
||||
* Fires when a secret is set or deleted.
|
||||
*/
|
||||
onDidChange: Event<SecretStorageChangeEvent>;
|
||||
}
|
||||
|
||||
export interface ExtensionContext {
|
||||
secrets: SecretStorage;
|
||||
// todo@API proper event type
|
||||
export const onDidChangeOpenEditors: Event<void>;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
|
|
@ -3,13 +3,12 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { IExtHostContext, MainContext } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostContext, IExtHostEditorTabsShape, IExtHostContext, MainContext, IEditorTabDto } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { Verbosity } from 'vs/workbench/common/editor';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { GroupChangeKind, IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
|
||||
export interface ITabInfo {
|
||||
name: string;
|
||||
|
@ -19,34 +18,62 @@ export interface ITabInfo {
|
|||
@extHostNamedCustomer(MainContext.MainThreadEditorTabs)
|
||||
export class MainThreadEditorTabs {
|
||||
|
||||
private readonly _registration: IDisposable;
|
||||
private static _GroupEventFilter = new Set([GroupChangeKind.EDITOR_CLOSE, GroupChangeKind.EDITOR_OPEN]);
|
||||
|
||||
private readonly _dispoables = new DisposableStore();
|
||||
private readonly _groups = new Map<IEditorGroup, IDisposable>();
|
||||
private readonly _proxy: IExtHostEditorTabsShape;
|
||||
|
||||
constructor(
|
||||
_extHostContext: IExtHostContext,
|
||||
extHostContext: IExtHostContext,
|
||||
@IEditorGroupsService private readonly _editorGroupsService: IEditorGroupsService,
|
||||
) {
|
||||
this._registration = CommandsRegistry.registerCommand('_textEditorTabs', () => {
|
||||
return this._fetchTextEditors();
|
||||
});
|
||||
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostEditorTabs);
|
||||
|
||||
this._editorGroupsService.groups.forEach(this._subscribeToGroup, this);
|
||||
this._dispoables.add(_editorGroupsService.onDidAddGroup(this._subscribeToGroup, this));
|
||||
this._dispoables.add(_editorGroupsService.onDidRemoveGroup(e => {
|
||||
const subscription = this._groups.get(e);
|
||||
if (subscription) {
|
||||
subscription.dispose();
|
||||
this._groups.delete(e);
|
||||
this._pushEditorTabs();
|
||||
}
|
||||
}));
|
||||
this._pushEditorTabs();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._registration.dispose();
|
||||
dispose(this._groups.values());
|
||||
this._dispoables.dispose();
|
||||
}
|
||||
|
||||
private _fetchTextEditors(): ITabInfo[] {
|
||||
const result: ITabInfo[] = [];
|
||||
private _subscribeToGroup(group: IEditorGroup) {
|
||||
this._groups.get(group)?.dispose();
|
||||
const listener = group.onDidGroupChange(e => {
|
||||
if (MainThreadEditorTabs._GroupEventFilter.has(e.kind)) {
|
||||
this._pushEditorTabs();
|
||||
}
|
||||
});
|
||||
this._groups.set(group, listener);
|
||||
}
|
||||
|
||||
private _pushEditorTabs(): void {
|
||||
const tabs: IEditorTabDto[] = [];
|
||||
for (const group of this._editorGroupsService.groups) {
|
||||
for (const editor of group.editors) {
|
||||
if (editor.isDisposed() || !editor.resource) {
|
||||
continue;
|
||||
}
|
||||
result.push({
|
||||
tabs.push({
|
||||
group: group.id,
|
||||
name: editor.getTitle(Verbosity.SHORT) ?? '',
|
||||
resource: editor.resource
|
||||
});
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
this._proxy.$acceptEditorTabs(tabs);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -688,7 +688,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
|||
return {
|
||||
beforeText: MainThreadLanguageFeatures._reviveRegExp(onEnterRule.beforeText),
|
||||
afterText: onEnterRule.afterText ? MainThreadLanguageFeatures._reviveRegExp(onEnterRule.afterText) : undefined,
|
||||
oneLineAboveText: onEnterRule.oneLineAboveText ? MainThreadLanguageFeatures._reviveRegExp(onEnterRule.oneLineAboveText) : undefined,
|
||||
previousLineText: onEnterRule.previousLineText ? MainThreadLanguageFeatures._reviveRegExp(onEnterRule.previousLineText) : undefined,
|
||||
action: onEnterRule.action
|
||||
};
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ export class ExtHostSecretState implements ExtHostSecretStateShape {
|
|||
return this._proxy.$getPassword(extensionId, key);
|
||||
}
|
||||
|
||||
set(extensionId: string, key: string, value: string): Promise<void> {
|
||||
store(extensionId: string, key: string, value: string): Promise<void> {
|
||||
return this._proxy.$setPassword(extensionId, key, value);
|
||||
}
|
||||
|
||||
|
|
|
@ -84,6 +84,7 @@ import { IExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSyste
|
|||
import { ExtHostTesting } from 'vs/workbench/api/common/extHostTesting';
|
||||
import { ExtHostUriOpeners } from 'vs/workbench/api/common/extHostUriOpener';
|
||||
import { IExtHostSecretState } from 'vs/workbench/api/common/exHostSecretState';
|
||||
import { ExtHostEditorTabs } from 'vs/workbench/api/common/extHostEditorTabs';
|
||||
|
||||
export interface IExtensionApiFactory {
|
||||
(extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode;
|
||||
|
@ -133,6 +134,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
|||
const extHostOutputService = rpcProtocol.set(ExtHostContext.ExtHostOutputService, accessor.get(IExtHostOutputService));
|
||||
|
||||
// manually create and register addressable instances
|
||||
const extHostEditorTabs = rpcProtocol.set(ExtHostContext.ExtHostEditorTabs, new ExtHostEditorTabs());
|
||||
const extHostUrls = rpcProtocol.set(ExtHostContext.ExtHostUrls, new ExtHostUrls(rpcProtocol));
|
||||
const extHostDocuments = rpcProtocol.set(ExtHostContext.ExtHostDocuments, new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors));
|
||||
const extHostDocumentContentProviders = rpcProtocol.set(ExtHostContext.ExtHostDocumentContentProviders, new ExtHostDocumentContentProvider(rpcProtocol, extHostDocumentsAndEditors, extHostLogService));
|
||||
|
@ -683,6 +685,14 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
|||
checkProposedApiEnabled(extension);
|
||||
return extHostUriOpeners.registerUriOpener(extension.identifier, id, schemes, opener, metadata);
|
||||
},
|
||||
get openEditors() {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostEditorTabs.tabs;
|
||||
},
|
||||
get onDidChangeOpenEditors() {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostEditorTabs.onDidChangeTabs;
|
||||
}
|
||||
};
|
||||
|
||||
// namespace: workspace
|
||||
|
|
|
@ -335,7 +335,7 @@ export interface IIndentationRuleDto {
|
|||
export interface IOnEnterRuleDto {
|
||||
beforeText: IRegExpDto;
|
||||
afterText?: IRegExpDto;
|
||||
oneLineAboveText?: IRegExpDto;
|
||||
previousLineText?: IRegExpDto;
|
||||
action: EnterAction;
|
||||
}
|
||||
export interface ILanguageConfigurationDto {
|
||||
|
@ -607,15 +607,29 @@ export interface MainThreadEditorInsetsShape extends IDisposable {
|
|||
$postMessage(handle: number, value: any): Promise<boolean>;
|
||||
}
|
||||
|
||||
export interface MainThreadEditorTabsShape extends IDisposable {
|
||||
|
||||
}
|
||||
|
||||
export interface ExtHostEditorInsetsShape {
|
||||
$onDidDispose(handle: number): void;
|
||||
$onDidReceiveMessage(handle: number, message: any): void;
|
||||
}
|
||||
|
||||
//#region --- open editors model
|
||||
|
||||
export interface MainThreadEditorTabsShape extends IDisposable {
|
||||
// manage tabs: move, close, rearrange etc
|
||||
}
|
||||
|
||||
export interface IEditorTabDto {
|
||||
group: number;
|
||||
name: string;
|
||||
resource: UriComponents
|
||||
}
|
||||
|
||||
export interface IExtHostEditorTabsShape {
|
||||
$acceptEditorTabs(tabs: IEditorTabDto[]): void;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
export type WebviewHandle = string;
|
||||
|
||||
export interface WebviewPanelShowOptions {
|
||||
|
@ -1927,6 +1941,7 @@ export const ExtHostContext = {
|
|||
ExtHostCustomEditors: createExtId<ExtHostCustomEditorsShape>('ExtHostCustomEditors'),
|
||||
ExtHostWebviewViews: createExtId<ExtHostWebviewViewsShape>('ExtHostWebviewViews'),
|
||||
ExtHostEditorInsets: createExtId<ExtHostEditorInsetsShape>('ExtHostEditorInsets'),
|
||||
ExtHostEditorTabs: createExtId<IExtHostEditorTabsShape>('ExtHostEditorTabs'),
|
||||
ExtHostProgress: createMainId<ExtHostProgressShape>('ExtHostProgress'),
|
||||
ExtHostComments: createMainId<ExtHostCommentsShape>('ExtHostComments'),
|
||||
ExtHostSecretState: createMainId<ExtHostSecretStateShape>('ExtHostSecretState'),
|
||||
|
|
39
src/vs/workbench/api/common/extHostEditorTabs.ts
Normal file
39
src/vs/workbench/api/common/extHostEditorTabs.ts
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import type * as vscode from 'vscode';
|
||||
import { IEditorTabDto, IExtHostEditorTabsShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
|
||||
|
||||
export interface IEditorTab {
|
||||
name: string;
|
||||
group: number;
|
||||
resource: vscode.Uri
|
||||
}
|
||||
|
||||
export class ExtHostEditorTabs implements IExtHostEditorTabsShape {
|
||||
|
||||
private readonly _onDidChangeTabs = new Emitter<void>();
|
||||
readonly onDidChangeTabs: Event<void> = this._onDidChangeTabs.event;
|
||||
|
||||
private _tabs: IEditorTab[] = [];
|
||||
|
||||
get tabs(): readonly IEditorTab[] {
|
||||
return this._tabs;
|
||||
}
|
||||
|
||||
$acceptEditorTabs(tabs: IEditorTabDto[]): void {
|
||||
this._tabs = tabs.map(dto => {
|
||||
return {
|
||||
name: dto.name,
|
||||
group: dto.group,
|
||||
resource: URI.revive(dto.resource)
|
||||
};
|
||||
});
|
||||
this._onDidChangeTabs.fire();
|
||||
}
|
||||
}
|
|
@ -1921,7 +1921,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
|
|||
return {
|
||||
beforeText: ExtHostLanguageFeatures._serializeRegExp(onEnterRule.beforeText),
|
||||
afterText: onEnterRule.afterText ? ExtHostLanguageFeatures._serializeRegExp(onEnterRule.afterText) : undefined,
|
||||
oneLineAboveText: onEnterRule.oneLineAboveText ? ExtHostLanguageFeatures._serializeRegExp(onEnterRule.oneLineAboveText) : undefined,
|
||||
previousLineText: onEnterRule.previousLineText ? ExtHostLanguageFeatures._serializeRegExp(onEnterRule.previousLineText) : undefined,
|
||||
action: onEnterRule.action
|
||||
};
|
||||
}
|
||||
|
|
|
@ -33,8 +33,8 @@ export class ExtensionSecrets implements vscode.SecretStorage {
|
|||
return this._secretState.get(this._id, key);
|
||||
}
|
||||
|
||||
set(key: string, value: string): Promise<void> {
|
||||
return this._secretState.set(this._id, key, value);
|
||||
store(key: string, value: string): Promise<void> {
|
||||
return this._secretState.store(this._id, key, value);
|
||||
}
|
||||
|
||||
delete(key: string): Promise<void> {
|
||||
|
|
|
@ -197,6 +197,10 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
|||
|
||||
try {
|
||||
await provider.runTests({ tests, debug: req.debug }, cancellation);
|
||||
for (const { collection } of this.testSubscriptions.values()) {
|
||||
collection.flushDiff(); // ensure all states are updated
|
||||
}
|
||||
|
||||
return EMPTY_TEST_RESULT;
|
||||
} catch (e) {
|
||||
console.error(e); // so it appears to attached debuggers
|
||||
|
@ -621,16 +625,9 @@ class TextDocumentTestObserverFactory extends AbstractTestObserverFactory {
|
|||
const uriString = resourceUri.toString();
|
||||
this.diffListeners.set(uriString, onDiff);
|
||||
|
||||
const disposeListener = this.documents.onDidRemoveDocuments(evt => {
|
||||
if (evt.some(delta => delta.document.uri.toString() === uriString)) {
|
||||
this.unlisten(resourceUri);
|
||||
}
|
||||
});
|
||||
|
||||
this.proxy.$subscribeToDiffs(ExtHostTestingResource.TextDocument, resourceUri);
|
||||
return new Disposable(() => {
|
||||
this.proxy.$unsubscribeFromDiffs(ExtHostTestingResource.TextDocument, resourceUri);
|
||||
disposeListener.dispose();
|
||||
this.diffListeners.delete(uriString);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2315,9 +2315,6 @@ export class FunctionBreakpoint extends Breakpoint {
|
|||
|
||||
constructor(functionName: string, enabled?: boolean, condition?: string, hitCondition?: string, logMessage?: string) {
|
||||
super(enabled, condition, hitCondition, logMessage);
|
||||
if (!functionName) {
|
||||
throw illegalArgument('functionName');
|
||||
}
|
||||
this.functionName = functionName;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,13 +15,13 @@ import * as fs from 'fs';
|
|||
import * as pfs from 'vs/base/node/pfs';
|
||||
import { isLinux } from 'vs/base/common/platform';
|
||||
import { IExtHostTunnelService, TunnelDto } from 'vs/workbench/api/common/extHostTunnelService';
|
||||
import { asPromise } from 'vs/base/common/async';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { TunnelOptions, TunnelCreationOptions } from 'vs/platform/remote/common/tunnel';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { promisify } from 'util';
|
||||
import { MovingAverage } from 'vs/base/common/numbers';
|
||||
import { CandidatePort } from 'vs/workbench/services/remote/common/remoteExplorerService';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
||||
class ExtensionTunnel implements vscode.Tunnel {
|
||||
private _onDispose: Emitter<void> = new Emitter();
|
||||
|
@ -142,7 +142,8 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
|
|||
|
||||
constructor(
|
||||
@IExtHostRpcService extHostRpc: IExtHostRpcService,
|
||||
@IExtHostInitDataService initData: IExtHostInitDataService
|
||||
@IExtHostInitDataService initData: IExtHostInitDataService,
|
||||
@ILogService private readonly logService: ILogService
|
||||
) {
|
||||
super();
|
||||
this._proxy = extHostRpc.getProxy(MainContext.MainThreadTunnelService);
|
||||
|
@ -234,16 +235,24 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
|
|||
|
||||
async $forwardPort(tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions): Promise<TunnelDto | undefined> {
|
||||
if (this._forwardPortProvider) {
|
||||
const providedPort = this._forwardPortProvider(tunnelOptions, tunnelCreationOptions);
|
||||
if (providedPort !== undefined) {
|
||||
return asPromise(() => providedPort).then(tunnel => {
|
||||
try {
|
||||
this.logService.trace('$forwardPort: Getting tunnel from provider.');
|
||||
const providedPort = this._forwardPortProvider(tunnelOptions, tunnelCreationOptions);
|
||||
this.logService.trace('$forwardPort: Got tunnel promise from provider.');
|
||||
if (providedPort !== undefined) {
|
||||
const tunnel = await providedPort;
|
||||
this.logService.trace('$forwardPort: Successfully awaited tunnel from provider.');
|
||||
if (!this._extensionTunnels.has(tunnelOptions.remoteAddress.host)) {
|
||||
this._extensionTunnels.set(tunnelOptions.remoteAddress.host, new Map());
|
||||
}
|
||||
const disposeListener = this._register(tunnel.onDidDispose(() => this._proxy.$closeTunnel(tunnel.remoteAddress)));
|
||||
this._extensionTunnels.get(tunnelOptions.remoteAddress.host)!.set(tunnelOptions.remoteAddress.port, { tunnel, disposeListener });
|
||||
return Promise.resolve(TunnelDto.fromApiTunnel(tunnel));
|
||||
});
|
||||
return TunnelDto.fromApiTunnel(tunnel);
|
||||
} else {
|
||||
this.logService.trace('$forwardPort: Tunnel is undefined');
|
||||
}
|
||||
} catch (e) {
|
||||
this.logService.trace('$forwardPort: tunnel provider error');
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
|
|
|
@ -785,8 +785,8 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
|||
}
|
||||
}
|
||||
|
||||
// Check cache only in desktop local window and if extensions are not yet registered
|
||||
if (!this.environmentService.remoteAuthority && !this.hasExtensionsRegistered) {
|
||||
// Check cache only if extensions are not yet registered and current window is not native (desktop) remote connection window
|
||||
if (!this.hasExtensionsRegistered && !(this.environmentService.remoteAuthority && isNative)) {
|
||||
cachedViewContainer = cachedViewContainer || this.cachedViewContainers.find(({ id }) => id === viewContainerId);
|
||||
|
||||
// Show builtin ViewContainer if not registered yet
|
||||
|
|
|
@ -711,7 +711,7 @@ export class CustomMenubarControl extends MenubarControl {
|
|||
if (href) {
|
||||
webNavigationActions.push(new Action('goHome', nls.localize('goHome', "Go Home"), undefined, true,
|
||||
async (event?: MouseEvent) => {
|
||||
if (event?.ctrlKey) {
|
||||
if ((!isMacintosh && event?.ctrlKey) || (isMacintosh && event?.metaKey)) {
|
||||
window.open(href, '_blank');
|
||||
} else {
|
||||
window.location.href = href;
|
||||
|
|
|
@ -40,7 +40,7 @@ interface IEnterAction {
|
|||
interface IOnEnterRule {
|
||||
beforeText: string | IRegExp;
|
||||
afterText?: string | IRegExp;
|
||||
oneLineAboveText?: string | IRegExp;
|
||||
previousLineText?: string | IRegExp;
|
||||
action: IEnterAction;
|
||||
}
|
||||
|
||||
|
@ -328,10 +328,10 @@ export class LanguageConfigurationFileHandler {
|
|||
resultingOnEnterRule.afterText = afterText;
|
||||
}
|
||||
}
|
||||
if (onEnterRule.oneLineAboveText) {
|
||||
const oneLineAboveText = this._parseRegex(languageIdentifier, `onEnterRules[${i}].oneLineAboveText`, onEnterRule.oneLineAboveText);
|
||||
if (oneLineAboveText) {
|
||||
resultingOnEnterRule.oneLineAboveText = oneLineAboveText;
|
||||
if (onEnterRule.previousLineText) {
|
||||
const previousLineText = this._parseRegex(languageIdentifier, `onEnterRules[${i}].previousLineText`, onEnterRule.previousLineText);
|
||||
if (previousLineText) {
|
||||
resultingOnEnterRule.previousLineText = previousLineText;
|
||||
}
|
||||
}
|
||||
result = result || [];
|
||||
|
@ -741,21 +741,21 @@ const schema: IJSONSchema = {
|
|||
}
|
||||
}
|
||||
},
|
||||
oneLineAboveText: {
|
||||
previousLineText: {
|
||||
type: ['string', 'object'],
|
||||
description: nls.localize('schema.onEnterRules.oneLineAboveText', 'This rule will only execute if the text above the line matches this regular expression.'),
|
||||
description: nls.localize('schema.onEnterRules.previousLineText', 'This rule will only execute if the text above the line matches this regular expression.'),
|
||||
properties: {
|
||||
pattern: {
|
||||
type: 'string',
|
||||
description: nls.localize('schema.onEnterRules.oneLineAboveText.pattern', 'The RegExp pattern for oneLineAboveText.'),
|
||||
description: nls.localize('schema.onEnterRules.previousLineText.pattern', 'The RegExp pattern for previousLineText.'),
|
||||
default: '',
|
||||
},
|
||||
flags: {
|
||||
type: 'string',
|
||||
description: nls.localize('schema.onEnterRules.oneLineAboveText.flags', 'The RegExp flags for oneLineAboveText.'),
|
||||
description: nls.localize('schema.onEnterRules.previousLineText.flags', 'The RegExp flags for previousLineText.'),
|
||||
default: '',
|
||||
pattern: '^([gimuy]+)$',
|
||||
patternErrorMessage: nls.localize('schema.onEnterRules.oneLineAboveText.errorMessage', 'Must match the pattern `/^([gimuy]+)$/`.')
|
||||
patternErrorMessage: nls.localize('schema.onEnterRules.previousLineText.errorMessage', 'Must match the pattern `/^([gimuy]+)$/`.')
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -209,11 +209,6 @@ class DocumentSymbolsOutline implements IOutline<DocumentSymbolItem> {
|
|||
}
|
||||
|
||||
async reveal(entry: DocumentSymbolItem, options: IEditorOptions, sideBySide: boolean): Promise<void> {
|
||||
if (entry instanceof OutlineElement) {
|
||||
const position = Range.getStartPosition(entry.symbol.selectionRange);
|
||||
this._editor.revealPositionInCenterIfOutsideViewport(position, ScrollType.Immediate);
|
||||
this._editor.setPosition(position);
|
||||
}
|
||||
const model = OutlineModel.get(entry);
|
||||
if (!model || !(entry instanceof OutlineElement)) {
|
||||
return;
|
||||
|
|
|
@ -478,7 +478,7 @@ export abstract class InstallInOtherServerAction extends ExtensionAction {
|
|||
|| !this.extension.local
|
||||
|| this.extension.state !== ExtensionState.Installed
|
||||
|| this.extension.type !== ExtensionType.User
|
||||
|| this.extension.enablementState === EnablementState.DisabledByEnvironemt
|
||||
|| this.extension.enablementState === EnablementState.DisabledByEnvironment
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -19,8 +19,8 @@ export default class Messages {
|
|||
|
||||
public static MARKERS_PANEL_TITLE_PROBLEMS: string = nls.localize('markers.panel.title.problems', "Problems");
|
||||
|
||||
public static MARKERS_PANEL_NO_PROBLEMS_BUILT: string = nls.localize('markers.panel.no.problems.build', "No problems have been detected in the workspace so far.");
|
||||
public static MARKERS_PANEL_NO_PROBLEMS_ACTIVE_FILE_BUILT: string = nls.localize('markers.panel.no.problems.activeFile.build', "No problems have been detected in the current file so far.");
|
||||
public static MARKERS_PANEL_NO_PROBLEMS_BUILT: string = nls.localize('markers.panel.no.problems.build', "No problems have been detected in the workspace.");
|
||||
public static MARKERS_PANEL_NO_PROBLEMS_ACTIVE_FILE_BUILT: string = nls.localize('markers.panel.no.problems.activeFile.build', "No problems have been detected in the current file.");
|
||||
public static MARKERS_PANEL_NO_PROBLEMS_FILTERS: string = nls.localize('markers.panel.no.problems.filters', "No results found with provided filter criteria.");
|
||||
|
||||
public static MARKERS_PANEL_ACTION_TOOLTIP_MORE_FILTERS: string = nls.localize('markers.panel.action.moreFilters', "More Filters...");
|
||||
|
|
|
@ -17,7 +17,7 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService
|
|||
import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { InputFocusedContext, InputFocusedContextKey } from 'vs/platform/contextkey/common/contextkeys';
|
||||
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { IQuickInputService, IQuickPickItem, QuickPickInput } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { CATEGORIES } from 'vs/workbench/common/actions';
|
||||
|
@ -141,9 +141,9 @@ abstract class NotebookAction extends Action2 {
|
|||
super(desc);
|
||||
}
|
||||
|
||||
async run(accessor: ServicesAccessor, context?: INotebookActionContext): Promise<void> {
|
||||
async run(accessor: ServicesAccessor, context?: any): Promise<void> {
|
||||
if (!this.isNotebookActionContext(context)) {
|
||||
context = this.getActiveEditorContext(accessor);
|
||||
context = this.getEditorContextFromArgsOrActive(accessor, context);
|
||||
if (!context) {
|
||||
return;
|
||||
}
|
||||
|
@ -158,7 +158,7 @@ abstract class NotebookAction extends Action2 {
|
|||
return !!context && !!(context as INotebookActionContext).notebookEditor;
|
||||
}
|
||||
|
||||
protected getActiveEditorContext(accessor: ServicesAccessor): INotebookActionContext | undefined {
|
||||
protected getEditorContextFromArgsOrActive(accessor: ServicesAccessor, context?: any): INotebookActionContext | undefined {
|
||||
const editorService = accessor.get(IEditorService);
|
||||
|
||||
const editor = getActiveNotebookEditor(editorService);
|
||||
|
@ -198,7 +198,7 @@ abstract class NotebookCellAction<T = INotebookCellActionContext> extends Notebo
|
|||
return this.runWithContext(accessor, contextFromArgs);
|
||||
}
|
||||
|
||||
const activeEditorContext = this.getActiveEditorContext(accessor);
|
||||
const activeEditorContext = this.getEditorContextFromArgsOrActive(accessor);
|
||||
if (this.isCellActionContext(activeEditorContext)) {
|
||||
return this.runWithContext(accessor, activeEditorContext);
|
||||
}
|
||||
|
@ -207,6 +207,22 @@ abstract class NotebookCellAction<T = INotebookCellActionContext> extends Notebo
|
|||
abstract runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise<void>;
|
||||
}
|
||||
|
||||
export function getWidgetFromUri(accessor: ServicesAccessor, uri: URI) {
|
||||
const editorService = accessor.get(IEditorService);
|
||||
const notebookWidgetService = accessor.get(INotebookEditorWidgetService);
|
||||
const editorId = editorService.getEditors(EditorsOrder.SEQUENTIAL).find(editorId => editorId.editor instanceof NotebookEditorInput && editorId.editor.resource?.toString() === uri.toString());
|
||||
|
||||
if (editorId) {
|
||||
const widget = notebookWidgetService.widgets.find(widget => widget.textModel?.viewType === (editorId.editor as NotebookEditorInput).viewType && widget.uri?.toString() === editorId.editor.resource!.toString());
|
||||
|
||||
if (widget && widget.hasModel()) {
|
||||
return widget;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
registerAction2(class extends NotebookCellAction<ICellRange> {
|
||||
constructor() {
|
||||
super({
|
||||
|
@ -259,30 +275,19 @@ registerAction2(class extends NotebookCellAction<ICellRange> {
|
|||
const uri = URI.revive(additionalArgs[0]);
|
||||
|
||||
if (uri) {
|
||||
// we will run cell against this document
|
||||
const editorService = accessor.get(IEditorService);
|
||||
const editorGroupService = accessor.get(IEditorGroupsService);
|
||||
const instantiationService = accessor.get(IInstantiationService);
|
||||
const notebookWidgetService = accessor.get(INotebookEditorWidgetService);
|
||||
const editorId = editorService.getEditors(EditorsOrder.SEQUENTIAL).find(editorId => editorId.editor instanceof NotebookEditorInput && editorId.editor.resource?.toString() === uri.toString());
|
||||
const widget = getWidgetFromUri(accessor, uri);
|
||||
if (widget) {
|
||||
const cells = widget.viewModel.viewCells;
|
||||
|
||||
if (editorId) {
|
||||
const group = editorGroupService.getGroup(editorId.groupId);
|
||||
const widget = instantiationService.invokeFunction(notebookWidgetService.retrieveWidget, group!, editorId.editor as NotebookEditorInput);
|
||||
|
||||
if (widget.value?.hasModel()) {
|
||||
const cells = widget.value.viewModel.viewCells;
|
||||
|
||||
return {
|
||||
notebookEditor: widget.value!,
|
||||
cell: cells[context.start]
|
||||
};
|
||||
}
|
||||
return {
|
||||
notebookEditor: widget,
|
||||
cell: cells[context.start]
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const activeEditorContext = this.getActiveEditorContext(accessor);
|
||||
const activeEditorContext = this.getEditorContextFromArgsOrActive(accessor);
|
||||
|
||||
if (!activeEditorContext || !activeEditorContext.notebookEditor.viewModel || context.start >= activeEditorContext.notebookEditor.viewModel.viewCells.length) {
|
||||
return;
|
||||
|
@ -326,18 +331,39 @@ registerAction2(class extends NotebookCellAction<ICellRange> {
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'uri',
|
||||
description: 'The document uri',
|
||||
constraint: URI
|
||||
}
|
||||
]
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
getCellContextFromArgs(accessor: ServicesAccessor, context?: ICellRange): INotebookCellActionContext | undefined {
|
||||
getCellContextFromArgs(accessor: ServicesAccessor, context?: ICellRange, ...additionalArgs: any[]): INotebookCellActionContext | undefined {
|
||||
if (!context || typeof context.start !== 'number' || typeof context.end !== 'number' || context.start >= context.end) {
|
||||
return;
|
||||
}
|
||||
|
||||
const activeEditorContext = this.getActiveEditorContext(accessor);
|
||||
if (additionalArgs.length && additionalArgs[0]) {
|
||||
const uri = URI.revive(additionalArgs[0]);
|
||||
|
||||
if (uri) {
|
||||
const widget = getWidgetFromUri(accessor, uri);
|
||||
if (widget) {
|
||||
const cells = widget.viewModel.viewCells;
|
||||
|
||||
return {
|
||||
notebookEditor: widget,
|
||||
cell: cells[context.start]
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const activeEditorContext = this.getEditorContextFromArgsOrActive(accessor);
|
||||
|
||||
if (!activeEditorContext || !activeEditorContext.notebookEditor.viewModel || context.start >= activeEditorContext.notebookEditor.viewModel.viewCells.length) {
|
||||
return;
|
||||
|
@ -491,19 +517,62 @@ registerAction2(class extends NotebookAction {
|
|||
super({
|
||||
id: EXECUTE_NOTEBOOK_COMMAND_ID,
|
||||
title: localize('notebookActions.executeNotebook', "Execute Notebook"),
|
||||
description: {
|
||||
description: localize('notebookActions.executeNotebook', "Execute Notebook"),
|
||||
args: [
|
||||
{
|
||||
name: 'uri',
|
||||
description: 'The document uri',
|
||||
constraint: URI
|
||||
}
|
||||
]
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
getEditorContextFromArgsOrActive(accessor: ServicesAccessor, context?: UriComponents): INotebookActionContext | undefined {
|
||||
if (context) {
|
||||
const uri = URI.revive(context);
|
||||
|
||||
if (uri) {
|
||||
const widget = getWidgetFromUri(accessor, uri);
|
||||
|
||||
if (widget) {
|
||||
return {
|
||||
notebookEditor: widget,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const editorService = accessor.get(IEditorService);
|
||||
const editor = getActiveNotebookEditor(editorService);
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const activeCell = editor.getActiveCell();
|
||||
return {
|
||||
cell: activeCell,
|
||||
notebookEditor: editor
|
||||
};
|
||||
}
|
||||
|
||||
async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise<void> {
|
||||
renderAllMarkdownCells(context);
|
||||
|
||||
const editorService = accessor.get(IEditorService);
|
||||
const editor = editorService.getEditors(EditorsOrder.MOST_RECENTLY_ACTIVE).find(
|
||||
editor => editor.editor instanceof NotebookEditorInput && editor.editor.viewType === context.notebookEditor.viewModel.viewType && editor.editor.resource.toString() === context.notebookEditor.viewModel.uri.toString());
|
||||
const editorGroupService = accessor.get(IEditorGroupsService);
|
||||
const group = editorGroupService.activeGroup;
|
||||
|
||||
if (group) {
|
||||
if (group.activeEditor) {
|
||||
group.pinEditor(group.activeEditor);
|
||||
}
|
||||
if (editor) {
|
||||
const group = editorGroupService.getGroup(editor.groupId);
|
||||
group?.pinEditor(editor.editor);
|
||||
}
|
||||
|
||||
return context.notebookEditor.executeNotebook();
|
||||
|
@ -523,9 +592,51 @@ registerAction2(class extends NotebookAction {
|
|||
super({
|
||||
id: CANCEL_NOTEBOOK_COMMAND_ID,
|
||||
title: localize('notebookActions.cancelNotebook', "Cancel Notebook Execution"),
|
||||
description: {
|
||||
description: localize('notebookActions.executeNotebook', "Execute Notebook"),
|
||||
args: [
|
||||
{
|
||||
name: 'uri',
|
||||
description: 'The document uri',
|
||||
constraint: URI
|
||||
}
|
||||
]
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
getEditorContextFromArgsOrActive(accessor: ServicesAccessor, context?: UriComponents): INotebookActionContext | undefined {
|
||||
if (context) {
|
||||
const uri = URI.revive(context);
|
||||
|
||||
if (uri) {
|
||||
const widget = getWidgetFromUri(accessor, uri);
|
||||
|
||||
if (widget) {
|
||||
return {
|
||||
notebookEditor: widget,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const editorService = accessor.get(IEditorService);
|
||||
const editor = getActiveNotebookEditor(editorService);
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const activeCell = editor.getActiveCell();
|
||||
return {
|
||||
cell: activeCell,
|
||||
notebookEditor: editor
|
||||
};
|
||||
}
|
||||
|
||||
async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise<void> {
|
||||
return context.notebookEditor.cancelNotebookExecution();
|
||||
}
|
||||
|
@ -745,7 +856,7 @@ registerAction2(class extends NotebookAction {
|
|||
}
|
||||
|
||||
async run(accessor: ServicesAccessor): Promise<void> {
|
||||
const context = this.getActiveEditorContext(accessor);
|
||||
const context = this.getEditorContextFromArgsOrActive(accessor);
|
||||
if (context) {
|
||||
this.runWithContext(accessor, context);
|
||||
}
|
||||
|
@ -770,7 +881,7 @@ registerAction2(class extends NotebookAction {
|
|||
}
|
||||
|
||||
async run(accessor: ServicesAccessor): Promise<void> {
|
||||
const context = this.getActiveEditorContext(accessor);
|
||||
const context = this.getEditorContextFromArgsOrActive(accessor);
|
||||
if (context) {
|
||||
this.runWithContext(accessor, context);
|
||||
}
|
||||
|
|
|
@ -232,7 +232,8 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri
|
|||
this._register(this.editorService.overrideOpenEditor({
|
||||
getEditorOverrides: (resource: URI, options: IEditorOptions | undefined, group: IEditorGroup | undefined) => {
|
||||
|
||||
const currentEditorForResource = group && this.editorService.findEditors(resource, group);
|
||||
const currentEditorsForResource = group && this.editorService.findEditors(resource, group);
|
||||
const currentEditorForResource = currentEditorsForResource && currentEditorsForResource.length ? currentEditorsForResource[0] : undefined;
|
||||
|
||||
const associatedEditors = distinct([
|
||||
...this.getUserAssociatedNotebookEditors(resource),
|
||||
|
@ -335,8 +336,25 @@ export class NotebookContribution extends Disposable implements IWorkbenchContri
|
|||
return undefined;
|
||||
}
|
||||
|
||||
if (originalInput instanceof NotebookEditorInput) {
|
||||
return undefined;
|
||||
if (id && originalInput instanceof NotebookEditorInput) {
|
||||
if (originalInput.viewType === id) {
|
||||
return undefined;
|
||||
} else {
|
||||
return {
|
||||
override: (async () => {
|
||||
const notebookInput = NotebookEditorInput.create(this.instantiationService, originalInput.resource, originalInput.getName(), id);
|
||||
await group.replaceEditors([{
|
||||
editor: originalInput,
|
||||
replacement: notebookInput
|
||||
}]);
|
||||
if (group.activeEditorPane?.input === notebookInput) {
|
||||
return group.activeEditorPane;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
})()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (originalInput instanceof NotebookDiffEditorInput) {
|
||||
|
|
|
@ -16,5 +16,6 @@ export interface IBorrowValue<T> {
|
|||
|
||||
export interface INotebookEditorWidgetService {
|
||||
_serviceBrand: undefined;
|
||||
widgets: NotebookEditorWidget[];
|
||||
retrieveWidget(accessor: ServicesAccessor, group: IEditorGroup, input: NotebookEditorInput): IBorrowValue<NotebookEditorWidget>;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,10 @@ export class NotebookEditorWidgetService implements INotebookEditorWidgetService
|
|||
private readonly _notebookWidgets = new Map<number, ResourceMap<{ widget: NotebookEditorWidget, token: number | undefined }>>();
|
||||
private readonly _disposables = new DisposableStore();
|
||||
|
||||
get widgets() {
|
||||
return [...this._notebookWidgets.values()].map(val => [...val.values()].map(widget => widget.widget)).reduce((prev, curr) => { return [...prev, ...curr]; }, []);
|
||||
}
|
||||
|
||||
constructor(
|
||||
@IEditorGroupsService editorGroupService: IEditorGroupsService,
|
||||
@IEditorService editorService: IEditorService,
|
||||
|
|
|
@ -253,7 +253,6 @@ export class OutlinePane extends ViewPane {
|
|||
// update: refresh tree
|
||||
this._domNode.classList.remove('message');
|
||||
tree.updateChildren();
|
||||
tree.expandAll();
|
||||
}
|
||||
};
|
||||
updateTree();
|
||||
|
|
|
@ -3,44 +3,59 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ITunnelService, TunnelOptions, RemoteTunnel, TunnelCreationOptions } from 'vs/platform/remote/common/tunnel';
|
||||
import { ITunnelService, TunnelOptions, RemoteTunnel, TunnelCreationOptions, ITunnel } from 'vs/platform/remote/common/tunnel';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IRemoteExplorerService } from 'vs/workbench/services/remote/common/remoteExplorerService';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
||||
export class TunnelFactoryContribution extends Disposable implements IWorkbenchContribution {
|
||||
constructor(
|
||||
@ITunnelService tunnelService: ITunnelService,
|
||||
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
|
||||
@IOpenerService openerService: IOpenerService,
|
||||
@IRemoteExplorerService remoteExplorerService: IRemoteExplorerService
|
||||
@IRemoteExplorerService remoteExplorerService: IRemoteExplorerService,
|
||||
@ILogService logService: ILogService
|
||||
) {
|
||||
super();
|
||||
const tunnelFactory = environmentService.options?.tunnelProvider?.tunnelFactory;
|
||||
if (tunnelFactory) {
|
||||
this._register(tunnelService.setTunnelProvider({
|
||||
forwardPort: (tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions): Promise<RemoteTunnel> | undefined => {
|
||||
const tunnelPromise = tunnelFactory(tunnelOptions, tunnelCreationOptions);
|
||||
if (!tunnelPromise) {
|
||||
return undefined;
|
||||
forwardPort: (tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions): Promise<RemoteTunnel | undefined> | undefined => {
|
||||
let tunnelPromise: Promise<ITunnel> | undefined;
|
||||
try {
|
||||
tunnelPromise = tunnelFactory(tunnelOptions, tunnelCreationOptions);
|
||||
} catch (e) {
|
||||
logService.trace('tunnelFactory: tunnel provider error');
|
||||
}
|
||||
return new Promise(resolve => {
|
||||
tunnelPromise.then(async (tunnel) => {
|
||||
const localAddress = tunnel.localAddress.startsWith('http') ? tunnel.localAddress : `http://${tunnel.localAddress}`;
|
||||
const remoteTunnel: RemoteTunnel = {
|
||||
tunnelRemotePort: tunnel.remoteAddress.port,
|
||||
tunnelRemoteHost: tunnel.remoteAddress.host,
|
||||
// The tunnel factory may give us an inaccessible local address.
|
||||
// To make sure this doesn't happen, resolve the uri immediately.
|
||||
localAddress: (await openerService.resolveExternalUri(URI.parse(localAddress))).resolved.toString(),
|
||||
public: !!tunnel.public,
|
||||
dispose: async () => { await tunnel.dispose; }
|
||||
};
|
||||
resolve(remoteTunnel);
|
||||
});
|
||||
|
||||
return new Promise(async (resolve) => {
|
||||
if (!tunnelPromise) {
|
||||
resolve(undefined);
|
||||
return;
|
||||
}
|
||||
let tunnel: ITunnel;
|
||||
try {
|
||||
tunnel = await tunnelPromise;
|
||||
} catch (e) {
|
||||
logService.trace('tunnelFactory: tunnel provider promise error');
|
||||
resolve(undefined);
|
||||
return;
|
||||
}
|
||||
const localAddress = tunnel.localAddress.startsWith('http') ? tunnel.localAddress : `http://${tunnel.localAddress}`;
|
||||
const remoteTunnel: RemoteTunnel = {
|
||||
tunnelRemotePort: tunnel.remoteAddress.port,
|
||||
tunnelRemoteHost: tunnel.remoteAddress.host,
|
||||
// The tunnel factory may give us an inaccessible local address.
|
||||
// To make sure this doesn't happen, resolve the uri immediately.
|
||||
localAddress: (await openerService.resolveExternalUri(URI.parse(localAddress))).resolved.toString(),
|
||||
public: !!tunnel.public,
|
||||
dispose: async () => { await tunnel.dispose(); }
|
||||
};
|
||||
resolve(remoteTunnel);
|
||||
});
|
||||
}
|
||||
}, environmentService.options?.tunnelProvider?.features ?? { elevation: false, public: false }));
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
|
||||
import * as assert from 'assert';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { DeferredPromise } from 'vs/base/test/common/utils';
|
||||
import { QueryType, IFileQuery } from 'vs/workbench/services/search/common/search';
|
||||
import { FileQueryCacheState } from 'vs/workbench/contrib/search/common/cacheState';
|
||||
import { DeferredPromise } from 'vs/base/common/async';
|
||||
|
||||
suite('FileQueryCacheState', () => {
|
||||
|
||||
|
|
|
@ -4,10 +4,9 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as assert from 'assert';
|
||||
import * as sinon from 'sinon';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { DeferredPromise, timeout } from 'vs/base/common/async';
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { DeferredPromise } from 'vs/base/test/common/utils';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl';
|
||||
|
|
|
@ -21,7 +21,7 @@ suite('Snippets', function () {
|
|||
let file = new TestSnippetFile(URI.file('somepath/foo.code-snippets'), []);
|
||||
let bucket: Snippet[] = [];
|
||||
file.select('', bucket);
|
||||
assert.equal(bucket.length, 0);
|
||||
assert.strictEqual(bucket.length, 0);
|
||||
|
||||
file = new TestSnippetFile(URI.file('somepath/foo.code-snippets'), [
|
||||
new Snippet(['foo'], 'FooSnippet1', 'foo', '', 'snippet', 'test', SnippetSource.User),
|
||||
|
@ -34,23 +34,23 @@ suite('Snippets', function () {
|
|||
|
||||
bucket = [];
|
||||
file.select('foo', bucket);
|
||||
assert.equal(bucket.length, 2);
|
||||
assert.strictEqual(bucket.length, 2);
|
||||
|
||||
bucket = [];
|
||||
file.select('fo', bucket);
|
||||
assert.equal(bucket.length, 0);
|
||||
assert.strictEqual(bucket.length, 0);
|
||||
|
||||
bucket = [];
|
||||
file.select('bar', bucket);
|
||||
assert.equal(bucket.length, 1);
|
||||
assert.strictEqual(bucket.length, 1);
|
||||
|
||||
bucket = [];
|
||||
file.select('bar.comment', bucket);
|
||||
assert.equal(bucket.length, 2);
|
||||
assert.strictEqual(bucket.length, 2);
|
||||
|
||||
bucket = [];
|
||||
file.select('bazz', bucket);
|
||||
assert.equal(bucket.length, 1);
|
||||
assert.strictEqual(bucket.length, 1);
|
||||
});
|
||||
|
||||
test('SnippetFile#select - any scope', function () {
|
||||
|
@ -62,7 +62,7 @@ suite('Snippets', function () {
|
|||
|
||||
let bucket: Snippet[] = [];
|
||||
file.select('foo', bucket);
|
||||
assert.equal(bucket.length, 2);
|
||||
assert.strictEqual(bucket.length, 2);
|
||||
|
||||
});
|
||||
|
||||
|
@ -70,9 +70,9 @@ suite('Snippets', function () {
|
|||
|
||||
function assertNeedsClipboard(body: string, expected: boolean): void {
|
||||
let snippet = new Snippet(['foo'], 'FooSnippet1', 'foo', '', body, 'test', SnippetSource.User);
|
||||
assert.equal(snippet.needsClipboard, expected);
|
||||
assert.strictEqual(snippet.needsClipboard, expected);
|
||||
|
||||
assert.equal(SnippetParser.guessNeedsClipboard(body), expected);
|
||||
assert.strictEqual(SnippetParser.guessNeedsClipboard(body), expected);
|
||||
}
|
||||
|
||||
assertNeedsClipboard('foo$CLIPBOARD', true);
|
||||
|
|
|
@ -14,7 +14,7 @@ suite('getNonWhitespacePrefix', () => {
|
|||
getLineContent: (lineNumber: number) => line
|
||||
};
|
||||
let actual = getNonWhitespacePrefix(model, new Position(1, column));
|
||||
assert.equal(actual, expected);
|
||||
assert.strictEqual(actual, expected);
|
||||
}
|
||||
|
||||
test('empty line', () => {
|
||||
|
|
|
@ -11,9 +11,9 @@ suite('SnippetRewrite', function () {
|
|||
function assertRewrite(input: string, expected: string | boolean): void {
|
||||
const actual = new Snippet(['foo'], 'foo', 'foo', 'foo', input, 'foo', SnippetSource.User);
|
||||
if (typeof expected === 'boolean') {
|
||||
assert.equal(actual.codeSnippet, input);
|
||||
assert.strictEqual(actual.codeSnippet, input);
|
||||
} else {
|
||||
assert.equal(actual.codeSnippet, expected);
|
||||
assert.strictEqual(actual.codeSnippet, expected);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,8 +48,8 @@ suite('SnippetRewrite', function () {
|
|||
|
||||
test('lazy bogous variable rewrite', function () {
|
||||
const snippet = new Snippet(['fooLang'], 'foo', 'prefix', 'desc', 'This is ${bogous} because it is a ${var}', 'source', SnippetSource.Extension);
|
||||
assert.equal(snippet.body, 'This is ${bogous} because it is a ${var}');
|
||||
assert.equal(snippet.codeSnippet, 'This is ${1:bogous} because it is a ${2:var}');
|
||||
assert.equal(snippet.isBogous, true);
|
||||
assert.strictEqual(snippet.body, 'This is ${bogous} because it is a ${var}');
|
||||
assert.strictEqual(snippet.codeSnippet, 'This is ${1:bogous} because it is a ${2:var}');
|
||||
assert.strictEqual(snippet.isBogous, true);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -80,8 +80,8 @@ suite('SnippetsService', function () {
|
|||
const model = createTextModel('', undefined, modeService.getLanguageIdentifier('fooLang'));
|
||||
|
||||
return provider.provideCompletionItems(model, new Position(1, 1), context)!.then(result => {
|
||||
assert.equal(result.incomplete, undefined);
|
||||
assert.equal(result.suggestions.length, 2);
|
||||
assert.strictEqual(result.incomplete, undefined);
|
||||
assert.strictEqual(result.suggestions.length, 2);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -91,14 +91,14 @@ suite('SnippetsService', function () {
|
|||
const model = createTextModel('bar', undefined, modeService.getLanguageIdentifier('fooLang'));
|
||||
|
||||
return provider.provideCompletionItems(model, new Position(1, 4), context)!.then(result => {
|
||||
assert.equal(result.incomplete, undefined);
|
||||
assert.equal(result.suggestions.length, 1);
|
||||
assert.deepEqual(result.suggestions[0].label, {
|
||||
assert.strictEqual(result.incomplete, undefined);
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
assert.deepStrictEqual(result.suggestions[0].label, {
|
||||
name: 'bar',
|
||||
type: 'barTest'
|
||||
});
|
||||
assert.equal((result.suggestions[0].range as any).insert.startColumn, 1);
|
||||
assert.equal(result.suggestions[0].insertText, 'barCodeSnippet');
|
||||
assert.strictEqual((result.suggestions[0].range as any).insert.startColumn, 1);
|
||||
assert.strictEqual(result.suggestions[0].insertText, 'barCodeSnippet');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -126,48 +126,48 @@ suite('SnippetsService', function () {
|
|||
const model = createTextModel('bar-bar', undefined, modeService.getLanguageIdentifier('fooLang'));
|
||||
|
||||
await provider.provideCompletionItems(model, new Position(1, 3), context)!.then(result => {
|
||||
assert.equal(result.incomplete, undefined);
|
||||
assert.equal(result.suggestions.length, 2);
|
||||
assert.deepEqual(result.suggestions[0].label, {
|
||||
assert.strictEqual(result.incomplete, undefined);
|
||||
assert.strictEqual(result.suggestions.length, 2);
|
||||
assert.deepStrictEqual(result.suggestions[0].label, {
|
||||
name: 'bar',
|
||||
type: 'barTest'
|
||||
});
|
||||
assert.equal(result.suggestions[0].insertText, 's1');
|
||||
assert.equal((result.suggestions[0].range as any).insert.startColumn, 1);
|
||||
assert.deepEqual(result.suggestions[1].label, {
|
||||
assert.strictEqual(result.suggestions[0].insertText, 's1');
|
||||
assert.strictEqual((result.suggestions[0].range as any).insert.startColumn, 1);
|
||||
assert.deepStrictEqual(result.suggestions[1].label, {
|
||||
name: 'bar-bar',
|
||||
type: 'name'
|
||||
});
|
||||
assert.equal(result.suggestions[1].insertText, 's2');
|
||||
assert.equal((result.suggestions[1].range as any).insert.startColumn, 1);
|
||||
assert.strictEqual(result.suggestions[1].insertText, 's2');
|
||||
assert.strictEqual((result.suggestions[1].range as any).insert.startColumn, 1);
|
||||
});
|
||||
|
||||
await provider.provideCompletionItems(model, new Position(1, 5), context)!.then(result => {
|
||||
assert.equal(result.incomplete, undefined);
|
||||
assert.equal(result.suggestions.length, 1);
|
||||
assert.deepEqual(result.suggestions[0].label, {
|
||||
assert.strictEqual(result.incomplete, undefined);
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
assert.deepStrictEqual(result.suggestions[0].label, {
|
||||
name: 'bar-bar',
|
||||
type: 'name'
|
||||
});
|
||||
assert.equal(result.suggestions[0].insertText, 's2');
|
||||
assert.equal((result.suggestions[0].range as any).insert.startColumn, 1);
|
||||
assert.strictEqual(result.suggestions[0].insertText, 's2');
|
||||
assert.strictEqual((result.suggestions[0].range as any).insert.startColumn, 1);
|
||||
});
|
||||
|
||||
await provider.provideCompletionItems(model, new Position(1, 6), context)!.then(result => {
|
||||
assert.equal(result.incomplete, undefined);
|
||||
assert.equal(result.suggestions.length, 2);
|
||||
assert.deepEqual(result.suggestions[0].label, {
|
||||
assert.strictEqual(result.incomplete, undefined);
|
||||
assert.strictEqual(result.suggestions.length, 2);
|
||||
assert.deepStrictEqual(result.suggestions[0].label, {
|
||||
name: 'bar',
|
||||
type: 'barTest'
|
||||
});
|
||||
assert.equal(result.suggestions[0].insertText, 's1');
|
||||
assert.equal((result.suggestions[0].range as any).insert.startColumn, 5);
|
||||
assert.deepEqual(result.suggestions[1].label, {
|
||||
assert.strictEqual(result.suggestions[0].insertText, 's1');
|
||||
assert.strictEqual((result.suggestions[0].range as any).insert.startColumn, 5);
|
||||
assert.deepStrictEqual(result.suggestions[1].label, {
|
||||
name: 'bar-bar',
|
||||
type: 'name'
|
||||
});
|
||||
assert.equal(result.suggestions[1].insertText, 's2');
|
||||
assert.equal((result.suggestions[1].range as any).insert.startColumn, 1);
|
||||
assert.strictEqual(result.suggestions[1].insertText, 's2');
|
||||
assert.strictEqual((result.suggestions[1].range as any).insert.startColumn, 1);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -186,21 +186,21 @@ suite('SnippetsService', function () {
|
|||
|
||||
let model = createTextModel('\t<?php', undefined, modeService.getLanguageIdentifier('fooLang'));
|
||||
return provider.provideCompletionItems(model, new Position(1, 7), context)!.then(result => {
|
||||
assert.equal(result.suggestions.length, 1);
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
model.dispose();
|
||||
|
||||
model = createTextModel('\t<?', undefined, modeService.getLanguageIdentifier('fooLang'));
|
||||
return provider.provideCompletionItems(model, new Position(1, 4), context)!;
|
||||
}).then(result => {
|
||||
assert.equal(result.suggestions.length, 1);
|
||||
assert.equal((result.suggestions[0].range as any).insert.startColumn, 2);
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
assert.strictEqual((result.suggestions[0].range as any).insert.startColumn, 2);
|
||||
model.dispose();
|
||||
|
||||
model = createTextModel('a<?', undefined, modeService.getLanguageIdentifier('fooLang'));
|
||||
return provider.provideCompletionItems(model, new Position(1, 4), context)!;
|
||||
}).then(result => {
|
||||
assert.equal(result.suggestions.length, 1);
|
||||
assert.equal((result.suggestions[0].range as any).insert.startColumn, 2);
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
assert.strictEqual((result.suggestions[0].range as any).insert.startColumn, 2);
|
||||
model.dispose();
|
||||
});
|
||||
});
|
||||
|
@ -221,10 +221,10 @@ suite('SnippetsService', function () {
|
|||
|
||||
let model = createTextModel('<head>\n\t\n>/head>', undefined, modeService.getLanguageIdentifier('fooLang'));
|
||||
return provider.provideCompletionItems(model, new Position(1, 1), context)!.then(result => {
|
||||
assert.equal(result.suggestions.length, 1);
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
return provider.provideCompletionItems(model, new Position(2, 2), context)!;
|
||||
}).then(result => {
|
||||
assert.equal(result.suggestions.length, 1);
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -251,13 +251,13 @@ suite('SnippetsService', function () {
|
|||
|
||||
let model = createTextModel('', undefined, modeService.getLanguageIdentifier('fooLang'));
|
||||
return provider.provideCompletionItems(model, new Position(1, 1), context)!.then(result => {
|
||||
assert.equal(result.suggestions.length, 2);
|
||||
assert.strictEqual(result.suggestions.length, 2);
|
||||
let [first, second] = result.suggestions;
|
||||
assert.deepEqual(first.label, {
|
||||
assert.deepStrictEqual(first.label, {
|
||||
name: 'first',
|
||||
type: 'first'
|
||||
});
|
||||
assert.deepEqual(second.label, {
|
||||
assert.deepStrictEqual(second.label, {
|
||||
name: 'second',
|
||||
type: 'second'
|
||||
});
|
||||
|
@ -279,13 +279,13 @@ suite('SnippetsService', function () {
|
|||
let model = createTextModel('p-', undefined, modeService.getLanguageIdentifier('fooLang'));
|
||||
|
||||
let result = await provider.provideCompletionItems(model, new Position(1, 2), context)!;
|
||||
assert.equal(result.suggestions.length, 1);
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
|
||||
result = await provider.provideCompletionItems(model, new Position(1, 3), context)!;
|
||||
assert.equal(result.suggestions.length, 1);
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
|
||||
result = await provider.provideCompletionItems(model, new Position(1, 3), context)!;
|
||||
assert.equal(result.suggestions.length, 1);
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
});
|
||||
|
||||
test('No snippets suggestion on long lines beyond character 100 #58807', async function () {
|
||||
|
@ -304,7 +304,7 @@ suite('SnippetsService', function () {
|
|||
let model = createTextModel('Thisisaverylonglinegoingwithmore100bcharactersandthismakesintellisensebecomea Thisisaverylonglinegoingwithmore100bcharactersandthismakesintellisensebecomea b', undefined, modeService.getLanguageIdentifier('fooLang'));
|
||||
let result = await provider.provideCompletionItems(model, new Position(1, 158), context)!;
|
||||
|
||||
assert.equal(result.suggestions.length, 1);
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
});
|
||||
|
||||
test('Type colon will trigger snippet #60746', async function () {
|
||||
|
@ -323,7 +323,7 @@ suite('SnippetsService', function () {
|
|||
let model = createTextModel(':', undefined, modeService.getLanguageIdentifier('fooLang'));
|
||||
let result = await provider.provideCompletionItems(model, new Position(1, 2), context)!;
|
||||
|
||||
assert.equal(result.suggestions.length, 0);
|
||||
assert.strictEqual(result.suggestions.length, 0);
|
||||
});
|
||||
|
||||
test('substring of prefix can\'t trigger snippet #60737', async function () {
|
||||
|
@ -342,8 +342,8 @@ suite('SnippetsService', function () {
|
|||
let model = createTextModel('template', undefined, modeService.getLanguageIdentifier('fooLang'));
|
||||
let result = await provider.provideCompletionItems(model, new Position(1, 9), context)!;
|
||||
|
||||
assert.equal(result.suggestions.length, 1);
|
||||
assert.deepEqual(result.suggestions[0].label, {
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
assert.deepStrictEqual(result.suggestions[0].label, {
|
||||
name: 'mytemplate',
|
||||
type: 'mytemplate'
|
||||
});
|
||||
|
@ -365,7 +365,7 @@ suite('SnippetsService', function () {
|
|||
let model = createTextModel('Thisisaverylonglinegoingwithmore100bcharactersandthismakesintellisensebecomea Thisisaverylonglinegoingwithmore100bcharactersandthismakesintellisensebecomea b text_after_b', undefined, modeService.getLanguageIdentifier('fooLang'));
|
||||
let result = await provider.provideCompletionItems(model, new Position(1, 158), context)!;
|
||||
|
||||
assert.equal(result.suggestions.length, 1);
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
});
|
||||
|
||||
test('issue #61296: VS code freezes when editing CSS file with emoji', async function () {
|
||||
|
@ -388,7 +388,7 @@ suite('SnippetsService', function () {
|
|||
let model = createTextModel('.🐷-a-b', undefined, modeService.getLanguageIdentifier('fooLang'));
|
||||
let result = await provider.provideCompletionItems(model, new Position(1, 8), context)!;
|
||||
|
||||
assert.equal(result.suggestions.length, 1);
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
});
|
||||
|
||||
test('No snippets shown when triggering completions at whitespace on line that already has text #62335', async function () {
|
||||
|
@ -407,7 +407,7 @@ suite('SnippetsService', function () {
|
|||
let model = createTextModel('a ', undefined, modeService.getLanguageIdentifier('fooLang'));
|
||||
let result = await provider.provideCompletionItems(model, new Position(1, 3), context)!;
|
||||
|
||||
assert.equal(result.suggestions.length, 1);
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
});
|
||||
|
||||
test('Snippet prefix with special chars and numbers does not work #62906', async function () {
|
||||
|
@ -434,16 +434,16 @@ suite('SnippetsService', function () {
|
|||
let model = createTextModel(' <', undefined, modeService.getLanguageIdentifier('fooLang'));
|
||||
let result = await provider.provideCompletionItems(model, new Position(1, 3), context)!;
|
||||
|
||||
assert.equal(result.suggestions.length, 1);
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
let [first] = result.suggestions;
|
||||
assert.equal((first.range as any).insert.startColumn, 2);
|
||||
assert.strictEqual((first.range as any).insert.startColumn, 2);
|
||||
|
||||
model = createTextModel('1', undefined, modeService.getLanguageIdentifier('fooLang'));
|
||||
result = await provider.provideCompletionItems(model, new Position(1, 2), context)!;
|
||||
|
||||
assert.equal(result.suggestions.length, 1);
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
[first] = result.suggestions;
|
||||
assert.equal((first.range as any).insert.startColumn, 1);
|
||||
assert.strictEqual((first.range as any).insert.startColumn, 1);
|
||||
});
|
||||
|
||||
test('Snippet replace range', async function () {
|
||||
|
@ -462,26 +462,26 @@ suite('SnippetsService', function () {
|
|||
let model = createTextModel('not wordFoo bar', undefined, modeService.getLanguageIdentifier('fooLang'));
|
||||
let result = await provider.provideCompletionItems(model, new Position(1, 3), context)!;
|
||||
|
||||
assert.equal(result.suggestions.length, 1);
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
let [first] = result.suggestions;
|
||||
assert.equal((first.range as any).insert.endColumn, 3);
|
||||
assert.equal((first.range as any).replace.endColumn, 9);
|
||||
assert.strictEqual((first.range as any).insert.endColumn, 3);
|
||||
assert.strictEqual((first.range as any).replace.endColumn, 9);
|
||||
|
||||
model = createTextModel('not woFoo bar', undefined, modeService.getLanguageIdentifier('fooLang'));
|
||||
result = await provider.provideCompletionItems(model, new Position(1, 3), context)!;
|
||||
|
||||
assert.equal(result.suggestions.length, 1);
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
[first] = result.suggestions;
|
||||
assert.equal((first.range as any).insert.endColumn, 3);
|
||||
assert.equal((first.range as any).replace.endColumn, 3);
|
||||
assert.strictEqual((first.range as any).insert.endColumn, 3);
|
||||
assert.strictEqual((first.range as any).replace.endColumn, 3);
|
||||
|
||||
model = createTextModel('not word', undefined, modeService.getLanguageIdentifier('fooLang'));
|
||||
result = await provider.provideCompletionItems(model, new Position(1, 1), context)!;
|
||||
|
||||
assert.equal(result.suggestions.length, 1);
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
[first] = result.suggestions;
|
||||
assert.equal((first.range as any).insert.endColumn, 1);
|
||||
assert.equal((first.range as any).replace.endColumn, 9);
|
||||
assert.strictEqual((first.range as any).insert.endColumn, 1);
|
||||
assert.strictEqual((first.range as any).replace.endColumn, 9);
|
||||
});
|
||||
|
||||
test('Snippet replace-range incorrect #108894', async function () {
|
||||
|
@ -501,10 +501,10 @@ suite('SnippetsService', function () {
|
|||
let model = createTextModel('filler e KEEP ng filler', undefined, modeService.getLanguageIdentifier('fooLang'));
|
||||
let result = await provider.provideCompletionItems(model, new Position(1, 9), context)!;
|
||||
|
||||
assert.equal(result.suggestions.length, 1);
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
let [first] = result.suggestions;
|
||||
assert.equal((first.range as any).insert.endColumn, 9);
|
||||
assert.equal((first.range as any).replace.endColumn, 9);
|
||||
assert.strictEqual((first.range as any).insert.endColumn, 9);
|
||||
assert.strictEqual((first.range as any).replace.endColumn, 9);
|
||||
});
|
||||
|
||||
test('Snippet will replace auto-closing pair if specified in prefix', async function () {
|
||||
|
|
|
@ -997,6 +997,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
|||
case 'pwsh.exe':
|
||||
return WindowsShellType.PowerShell;
|
||||
case 'bash.exe':
|
||||
case 'git-cmd.exe':
|
||||
return WindowsShellType.GitBash;
|
||||
case 'wsl.exe':
|
||||
case 'ubuntu.exe':
|
||||
|
|
|
@ -7,7 +7,7 @@ import { Iterable } from 'vs/base/common/iterator';
|
|||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { TestRunState } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { ITestTreeElement } from 'vs/workbench/contrib/testing/browser/explorerProjections';
|
||||
import { maxPriority, statePriority } from 'vs/workbench/contrib/testing/browser/testExplorerTree';
|
||||
import { maxPriority, statePriority } from 'vs/workbench/contrib/testing/common/testingStates';
|
||||
import { InternalTestItem, TestIdWithProvider } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
|
||||
/**
|
||||
|
|
|
@ -7,11 +7,8 @@ import { IIdentityProvider } from 'vs/base/browser/ui/list/list';
|
|||
import { ICompressedTreeElement } from 'vs/base/browser/ui/tree/compressedObjectTreeModel';
|
||||
import { ObjectTree } from 'vs/base/browser/ui/tree/objectTree';
|
||||
import { ITreeElement } from 'vs/base/browser/ui/tree/tree';
|
||||
import { TestRunState } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { ITestTreeElement } from 'vs/workbench/contrib/testing/browser/explorerProjections';
|
||||
|
||||
export const isRunningState = (s: TestRunState) => s === TestRunState.Queued || s === TestRunState.Running;
|
||||
|
||||
export const testIdentityProvider: IIdentityProvider<ITestTreeElement> = {
|
||||
getId(element) {
|
||||
return element.treeId;
|
||||
|
|
|
@ -14,10 +14,10 @@ import { Location as ModeLocation } from 'vs/editor/common/modes';
|
|||
import { TestRunState } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { ITestTreeElement, ITestTreeProjection } from 'vs/workbench/contrib/testing/browser/explorerProjections';
|
||||
import { locationsEqual, TestLocationStore } from 'vs/workbench/contrib/testing/browser/explorerProjections/locationStore';
|
||||
import { isRunningState, NodeChangeList, NodeRenderDirective, NodeRenderFn, peersHaveChildren } from 'vs/workbench/contrib/testing/browser/explorerProjections/nodeHelper';
|
||||
import { NodeChangeList, NodeRenderDirective, NodeRenderFn, peersHaveChildren } from 'vs/workbench/contrib/testing/browser/explorerProjections/nodeHelper';
|
||||
import { StateElement } from 'vs/workbench/contrib/testing/browser/explorerProjections/stateNodes';
|
||||
import { statesInOrder } from 'vs/workbench/contrib/testing/browser/testExplorerTree';
|
||||
import { AbstractIncrementalTestCollection, IncrementalChangeCollector, IncrementalTestCollectionItem, InternalTestItem, TestIdWithProvider, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import { isRunningState, statesInOrder } from 'vs/workbench/contrib/testing/common/testingStates';
|
||||
import { TestSubscriptionListener } from 'vs/workbench/contrib/testing/common/workspaceTestCollectionService';
|
||||
|
||||
interface IStatusTestItem extends IncrementalTestCollectionItem {
|
||||
|
|
|
@ -15,9 +15,10 @@ import { TestRunState } from 'vs/workbench/api/common/extHostTypes';
|
|||
import { ITestTreeElement, ITestTreeProjection } from 'vs/workbench/contrib/testing/browser/explorerProjections';
|
||||
import { ListElementType } from 'vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByName';
|
||||
import { locationsEqual, TestLocationStore } from 'vs/workbench/contrib/testing/browser/explorerProjections/locationStore';
|
||||
import { isRunningState, NodeChangeList, NodeRenderFn } from 'vs/workbench/contrib/testing/browser/explorerProjections/nodeHelper';
|
||||
import { NodeChangeList, NodeRenderFn } from 'vs/workbench/contrib/testing/browser/explorerProjections/nodeHelper';
|
||||
import { StateElement } from 'vs/workbench/contrib/testing/browser/explorerProjections/stateNodes';
|
||||
import { AbstractIncrementalTestCollection, IncrementalChangeCollector, IncrementalTestCollectionItem, InternalTestItem, TestIdWithProvider, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import { isRunningState } from 'vs/workbench/contrib/testing/common/testingStates';
|
||||
import { TestSubscriptionListener } from 'vs/workbench/contrib/testing/common/workspaceTestCollectionService';
|
||||
|
||||
class ListTestStateElement implements ITestTreeElement {
|
||||
|
|
|
@ -11,7 +11,8 @@ import { TestRunState } from 'vs/workbench/api/common/extHostTypes';
|
|||
import { testStatesToIconColors } from 'vs/workbench/contrib/testing/browser/theme';
|
||||
|
||||
export const testingViewIcon = registerIcon('test-view-icon', Codicon.beaker, localize('testViewIcon', 'View icon of the test view.'));
|
||||
export const testingRunIcon = registerIcon('testing-run-icon', Codicon.debugStart, localize('testingRunIcon', 'Icon of the "run test" action.'));
|
||||
export const testingRunIcon = registerIcon('testing-run-icon', Codicon.run, localize('testingRunIcon', 'Icon of the "run test" action.'));
|
||||
export const testingRunAllIcon = registerIcon('testing-run-icon', Codicon.runAll, localize('testingRunAllIcon', 'Icon of the "run all tests" action.'));
|
||||
export const testingDebugIcon = registerIcon('testing-debug-icon', Codicon.debugAlt, localize('testingDebugIcon', 'Icon of the "debug test" action.'));
|
||||
export const testingCancelIcon = registerIcon('testing-cancel-icon', Codicon.close, localize('testingCancelIcon', 'Icon to cancel ongoing test runs.'));
|
||||
|
||||
|
|
|
@ -46,6 +46,10 @@
|
|||
margin-right: 0.25em;
|
||||
}
|
||||
|
||||
.test-explorer .test-explorer-messages {
|
||||
padding: 0 12px 8px;
|
||||
}
|
||||
|
||||
.monaco-workbench
|
||||
.test-explorer
|
||||
.monaco-action-bar
|
||||
|
@ -63,11 +67,11 @@
|
|||
}
|
||||
|
||||
/** -- peek */
|
||||
.monaco-editor .zone-widget .zone-widget-container.peekview-widget {
|
||||
.monaco-editor .zone-widget.test-output-peek .zone-widget-container.peekview-widget {
|
||||
border-top-width: 2px;
|
||||
border-bottom-width: 2px;
|
||||
}
|
||||
|
||||
/* .monaco-editor .zone-widget .zone-widget-container.peekview-widget .peekview-title .filename {
|
||||
height: 22px;
|
||||
} */
|
||||
.monaco-editor .zone-widget.test-output-peek .zone-widget-container.peekview-widget .peekview-title .filename {
|
||||
max-height: 29px;
|
||||
}
|
||||
|
|
|
@ -226,7 +226,7 @@ export class RunAllAction extends RunOrDebugAllAllAction {
|
|||
super(
|
||||
'testing.runAll',
|
||||
localize('runAllTests', 'Run All Tests'),
|
||||
icons.testingDebugIcon,
|
||||
icons.testingRunAllIcon,
|
||||
false,
|
||||
localize('noTestProvider', 'No tests found in this workspace. You may need to install a test provider extension'),
|
||||
);
|
||||
|
|
|
@ -33,8 +33,10 @@ import { TestService } from 'vs/workbench/contrib/testing/common/testServiceImpl
|
|||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
|
||||
import * as Action from './testExplorerActions';
|
||||
import { ITestResultService, TestResultService } from 'vs/workbench/contrib/testing/common/testResultService';
|
||||
|
||||
registerSingleton(ITestService, TestService);
|
||||
registerSingleton(ITestResultService, TestResultService);
|
||||
registerSingleton(IWorkspaceTestCollectionService, WorkspaceTestCollectionService);
|
||||
|
||||
const viewContainer = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({
|
||||
|
|
|
@ -10,12 +10,14 @@ import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget';
|
|||
import { ICompressedTreeNode } from 'vs/base/browser/ui/tree/compressedObjectTreeModel';
|
||||
import { ObjectTree } from 'vs/base/browser/ui/tree/objectTree';
|
||||
import { ITreeEvent, ITreeFilter, ITreeNode, ITreeRenderer, ITreeSorter, TreeFilterResult, TreeVisibility } from 'vs/base/browser/ui/tree/tree';
|
||||
import { DeferredPromise } from 'vs/base/common/async';
|
||||
import { Color, RGBA } from 'vs/base/common/color';
|
||||
import { throttle } from 'vs/base/common/decorators';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { FuzzyScore } from 'vs/base/common/filters';
|
||||
import { splitGlobAware } from 'vs/base/common/glob';
|
||||
import { Iterable } from 'vs/base/common/iterator';
|
||||
import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Disposable, DisposableStore, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import 'vs/css!./media/testing';
|
||||
import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
|
@ -30,10 +32,11 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
|
|||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { WorkbenchObjectTree } from 'vs/platform/list/browser/listService';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { IProgressService } from 'vs/platform/progress/common/progress';
|
||||
import { IProgress, IProgressService, IProgressStep } from 'vs/platform/progress/common/progress';
|
||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
import { foreground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
import { TestRunState } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { IResourceLabel, IResourceLabelOptions, IResourceLabelProps, ResourceLabels } from 'vs/workbench/browser/labels';
|
||||
import { ViewPane } from 'vs/workbench/browser/parts/views/viewPane';
|
||||
|
@ -47,13 +50,15 @@ import { StateByLocationProjection } from 'vs/workbench/contrib/testing/browser/
|
|||
import { StateByNameProjection } from 'vs/workbench/contrib/testing/browser/explorerProjections/stateByName';
|
||||
import { StateElement } from 'vs/workbench/contrib/testing/browser/explorerProjections/stateNodes';
|
||||
import { testingStatesToIcons } from 'vs/workbench/contrib/testing/browser/icons';
|
||||
import { cmpPriority, isFailedState } from 'vs/workbench/contrib/testing/browser/testExplorerTree';
|
||||
import { TestingExplorerFilter, TestingFilterState } from 'vs/workbench/contrib/testing/browser/testingExplorerFilter';
|
||||
import { TestingOutputPeekController } from 'vs/workbench/contrib/testing/browser/testingOutputPeek';
|
||||
import { TestExplorerViewGrouping, TestExplorerViewMode } from 'vs/workbench/contrib/testing/common/constants';
|
||||
import { IWorkspaceTestCollectionService, TestSubscriptionListener } from 'vs/workbench/contrib/testing/common/workspaceTestCollectionService';
|
||||
import { TestExplorerViewGrouping, TestExplorerViewMode, Testing } from 'vs/workbench/contrib/testing/common/constants';
|
||||
import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys';
|
||||
import { cmpPriority, isFailedState } from 'vs/workbench/contrib/testing/common/testingStates';
|
||||
import { ITestResultService, sumCounts, TestStateCount } from 'vs/workbench/contrib/testing/common/testResultService';
|
||||
import { ITestService } from 'vs/workbench/contrib/testing/common/testService';
|
||||
import { IWorkspaceTestCollectionService, TestSubscriptionListener } from 'vs/workbench/contrib/testing/common/workspaceTestCollectionService';
|
||||
import { IActivityService, NumberBadge, ProgressBadge } from 'vs/workbench/services/activity/common/activity';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { DebugAction, RunAction } from './testExplorerActions';
|
||||
|
||||
|
@ -101,12 +106,13 @@ export class TestingExplorerView extends ViewPane {
|
|||
this.filter = this.instantiationService.createInstance(TestingExplorerFilter, this.container, this.filterState);
|
||||
this._register(this.filter);
|
||||
|
||||
const messagesContainer = dom.append(this.container, dom.$('.test-explorer-messages'));
|
||||
this._register(this.instantiationService.createInstance(TestRunProgress, messagesContainer, this.getProgressLocation()));
|
||||
|
||||
const listContainer = dom.append(this.container, dom.$('.test-explorer-tree'));
|
||||
this.viewModel = this.instantiationService.createInstance(TestingExplorerViewModel, listContainer, this.onDidChangeBodyVisibility, this.currentSubscription, this.filterState);
|
||||
this._register(this.viewModel);
|
||||
|
||||
this.getProgressIndicator().show(true);
|
||||
|
||||
this._register(this.onDidChangeBodyVisibility(visible => {
|
||||
if (!visible && this.currentSubscription) {
|
||||
this.currentSubscription.dispose();
|
||||
|
@ -127,7 +133,7 @@ export class TestingExplorerView extends ViewPane {
|
|||
this.filter.saveState();
|
||||
}
|
||||
|
||||
private updateProgressIndicator(busy: number) {
|
||||
private updateDiscoveryProgress(busy: number) {
|
||||
if (!busy && this.finishDiscovery) {
|
||||
this.finishDiscovery();
|
||||
this.finishDiscovery = undefined;
|
||||
|
@ -148,7 +154,7 @@ export class TestingExplorerView extends ViewPane {
|
|||
|
||||
private createSubscription() {
|
||||
const handle = this.testCollection.subscribeToWorkspaceTests();
|
||||
handle.subscription.onBusyProvidersChange(() => this.updateProgressIndicator(handle.subscription.busyProviders));
|
||||
handle.subscription.onBusyProvidersChange(() => this.updateDiscoveryProgress(handle.subscription.busyProviders));
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
|
@ -228,6 +234,7 @@ export class TestingExplorerViewModel extends Disposable {
|
|||
instantiationService.createInstance(TestsRenderer, labels)
|
||||
],
|
||||
{
|
||||
simpleKeyboardNavigation: true,
|
||||
identityProvider: instantiationService.createInstance(IdentityProvider),
|
||||
hideTwistiesOfChildlessElements: true,
|
||||
sorter: instantiationService.createInstance(TreeSorter),
|
||||
|
@ -453,6 +460,12 @@ class CodeEditorTracker {
|
|||
}
|
||||
}
|
||||
|
||||
const enum FilterResult {
|
||||
Include,
|
||||
Exclude,
|
||||
Inherit,
|
||||
}
|
||||
|
||||
class TestsFilter implements ITreeFilter<ITestTreeElement> {
|
||||
private filters: [include: boolean, value: string][] | undefined;
|
||||
|
||||
|
@ -482,25 +495,32 @@ class TestsFilter implements ITreeFilter<ITestTreeElement> {
|
|||
}
|
||||
|
||||
public filter(element: ITestTreeElement): TreeFilterResult<void> {
|
||||
if (this.testFilterText(element.label)) {
|
||||
return TreeVisibility.Visible;
|
||||
for (let e: ITestTreeElement | null = element; e; e = e.parentItem) {
|
||||
switch (this.testFilterText(e.label)) {
|
||||
case FilterResult.Exclude:
|
||||
return TreeVisibility.Hidden;
|
||||
case FilterResult.Include:
|
||||
return TreeVisibility.Visible;
|
||||
case FilterResult.Inherit:
|
||||
// continue to parent
|
||||
}
|
||||
}
|
||||
|
||||
return Iterable.isEmpty(element.children) ? TreeVisibility.Hidden : TreeVisibility.Recurse;
|
||||
return TreeVisibility.Recurse;
|
||||
}
|
||||
|
||||
private testFilterText(data: string) {
|
||||
if (!this.filters) {
|
||||
return true;
|
||||
return FilterResult.Include;
|
||||
}
|
||||
|
||||
// start as included if the first glob is a negation
|
||||
let included = this.filters[0][0] === false;
|
||||
let included = this.filters[0][0] === false ? FilterResult.Exclude : FilterResult.Inherit;
|
||||
data = data.toLowerCase();
|
||||
|
||||
for (const [include, filter] of this.filters) {
|
||||
if (data.includes(filter)) {
|
||||
included = include;
|
||||
included = include ? FilterResult.Include : FilterResult.Exclude;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -616,9 +636,13 @@ class TestsRenderer implements ITreeRenderer<ITestTreeElement, FuzzyScore, TestT
|
|||
label.resource = test.item.location.uri;
|
||||
}
|
||||
|
||||
options.title = 'hover title';
|
||||
options.fileKind = FileKind.FILE;
|
||||
let title = element.label;
|
||||
for (let p = element.parentItem; p; p = p.parentItem) {
|
||||
title = `${p.label}, ${title}`;
|
||||
}
|
||||
|
||||
options.title = title;
|
||||
options.fileKind = FileKind.FILE;
|
||||
label.description = element.description;
|
||||
} else {
|
||||
options.fileKind = FileKind.ROOT_FOLDER;
|
||||
|
@ -647,3 +671,134 @@ class TestsRenderer implements ITreeRenderer<ITestTreeElement, FuzzyScore, TestT
|
|||
templateData.actionBar.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
const collectCounts = (count: TestStateCount) => {
|
||||
const failed = count[TestRunState.Errored] + count[TestRunState.Failed];
|
||||
const passed = count[TestRunState.Passed];
|
||||
const skipped = count[TestRunState.Skipped];
|
||||
|
||||
return {
|
||||
passed,
|
||||
failed,
|
||||
runSoFar: passed + failed,
|
||||
totalWillBeRun: passed + failed + count[TestRunState.Queued] + count[TestRunState.Running],
|
||||
skipped,
|
||||
};
|
||||
};
|
||||
|
||||
const getProgressText = ({ passed, runSoFar, skipped }: { passed: number, runSoFar: number, skipped: number }) => {
|
||||
const percent = (passed / runSoFar * 100).toFixed(0);
|
||||
if (skipped === 0) {
|
||||
return localize('testProgress', '{0}/{1} tests passed ({2}%)', passed, runSoFar, percent);
|
||||
} else {
|
||||
return localize('testProgressWithSkip', '{0}/{1} tests passed ({2}%, {3} skipped)', passed, runSoFar, percent, skipped);
|
||||
}
|
||||
};
|
||||
|
||||
class TestRunProgress {
|
||||
private current?: { update: IProgress<IProgressStep>; deferred: DeferredPromise<void> };
|
||||
private badge = new MutableDisposable();
|
||||
private readonly resultLister = this.resultService.onNewTestResult(result => {
|
||||
this.updateProgress();
|
||||
this.updateBadge();
|
||||
|
||||
result.onChange(this.throttledProgressUpdate, this);
|
||||
result.onComplete(() => {
|
||||
this.throttledProgressUpdate();
|
||||
this.updateBadge();
|
||||
});
|
||||
});
|
||||
|
||||
constructor(
|
||||
private readonly messagesContainer: HTMLElement,
|
||||
private readonly location: string,
|
||||
@IProgressService private readonly progress: IProgressService,
|
||||
@ITestResultService private readonly resultService: ITestResultService,
|
||||
@IActivityService private readonly activityService: IActivityService,
|
||||
) {
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
this.resultLister.dispose();
|
||||
this.current?.deferred.complete();
|
||||
this.badge.dispose();
|
||||
}
|
||||
|
||||
@throttle(200)
|
||||
private throttledProgressUpdate() {
|
||||
this.updateProgress();
|
||||
}
|
||||
|
||||
private updateProgress() {
|
||||
const running = this.resultService.results.filter(r => !r.isComplete);
|
||||
if (!running.length) {
|
||||
this.setIdleText(this.resultService.results[0]?.counts);
|
||||
this.current?.deferred.complete();
|
||||
this.current = undefined;
|
||||
} else if (!this.current) {
|
||||
this.progress.withProgress({ location: this.location, total: 100 }, update => {
|
||||
this.current = { update, deferred: new DeferredPromise() };
|
||||
this.updateProgress();
|
||||
return this.current.deferred.p;
|
||||
});
|
||||
} else {
|
||||
const counts = sumCounts(running.map(r => r.counts));
|
||||
this.setRunningText(counts);
|
||||
const { runSoFar, totalWillBeRun } = collectCounts(counts);
|
||||
this.current.update.report({ increment: runSoFar, total: totalWillBeRun });
|
||||
}
|
||||
}
|
||||
|
||||
private setRunningText(counts: TestStateCount) {
|
||||
this.messagesContainer.dataset.state = 'running';
|
||||
|
||||
const collected = collectCounts(counts);
|
||||
if (collected.runSoFar === 0) {
|
||||
this.messagesContainer.innerText = localize('testResultStarting', 'Test run is starting...');
|
||||
} else {
|
||||
this.messagesContainer.innerText = getProgressText(collected);
|
||||
}
|
||||
}
|
||||
|
||||
private setIdleText(lastCount?: TestStateCount) {
|
||||
if (!lastCount) {
|
||||
this.messagesContainer.innerText = '';
|
||||
} else {
|
||||
const collected = collectCounts(lastCount);
|
||||
this.messagesContainer.dataset.state = collected.failed ? 'failed' : 'running';
|
||||
this.messagesContainer.innerText = getProgressText(collected);
|
||||
}
|
||||
}
|
||||
|
||||
private updateBadge() {
|
||||
this.badge.value = undefined;
|
||||
const result = this.resultService.results[0]; // currently running, or last run
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result.isComplete) {
|
||||
const badge = new ProgressBadge(() => localize('testBadgeRunning', 'Test run in progress'));
|
||||
this.badge.value = this.activityService.showViewActivity(Testing.ExplorerViewId, { badge, clazz: 'progress-badge' });
|
||||
return;
|
||||
}
|
||||
|
||||
const failures = result.counts[TestRunState.Failed] + result.counts[TestRunState.Errored];
|
||||
if (failures === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const badge = new NumberBadge(failures, () => localize('testBadgeFailures', '{0} tests failed', failures));
|
||||
this.badge.value = this.activityService.showViewActivity(Testing.ExplorerViewId, { badge });
|
||||
}
|
||||
}
|
||||
|
||||
registerThemingParticipant((theme, collector) => {
|
||||
if (theme.type === 'dark') {
|
||||
const foregroundColor = theme.getColor(foreground);
|
||||
if (foregroundColor) {
|
||||
const fgWithOpacity = new Color(new RGBA(foregroundColor.rgba.r, foregroundColor.rgba.g, foregroundColor.rgba.b, 0.65));
|
||||
collector.addRule(`.test-explorer .test-explorer-messages { color: ${fgWithOpacity}; }`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -102,7 +102,7 @@ export class TestingOutputPeek extends PeekViewWidget {
|
|||
@IInstantiationService protected readonly instantiationService: IInstantiationService,
|
||||
@ITextModelService private readonly modelService: ITextModelService,
|
||||
) {
|
||||
super(editor, { showFrame: false, showArrow: true, isResizeable: true, isAccessible: true }, instantiationService);
|
||||
super(editor, { showFrame: false, showArrow: true, isResizeable: true, isAccessible: true, className: 'test-output-peek' }, instantiationService);
|
||||
|
||||
this._disposables.add(themeService.onDidColorThemeChange(this.applyTheme, this));
|
||||
this.applyTheme(themeService.getColorTheme());
|
||||
|
|
|
@ -188,6 +188,10 @@ export class SingleUseTestCollection implements IDisposable {
|
|||
|
||||
@throttle(200)
|
||||
protected throttleSendDiff() {
|
||||
this.flushDiff();
|
||||
}
|
||||
|
||||
public flushDiff() {
|
||||
const diff = this.collectDiff();
|
||||
if (diff.length) {
|
||||
this.publishDiff(diff);
|
||||
|
|
229
src/vs/workbench/contrib/testing/common/testResultService.ts
Normal file
229
src/vs/workbench/contrib/testing/common/testResultService.ts
Normal file
|
@ -0,0 +1,229 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { TestRunState } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { IncrementalTestCollectionItem, InternalTestItem, TestIdWithProvider } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys';
|
||||
import { isRunningState, statesInOrder } from 'vs/workbench/contrib/testing/common/testingStates';
|
||||
import { IMainThreadTestCollection } from 'vs/workbench/contrib/testing/common/testService';
|
||||
|
||||
export type TestStateCount = { [K in TestRunState]: number };
|
||||
|
||||
const makeEmptyCounts = () => {
|
||||
const o: Partial<TestStateCount> = {};
|
||||
for (const state of statesInOrder) {
|
||||
o[state] = 0;
|
||||
}
|
||||
|
||||
return o as TestStateCount;
|
||||
};
|
||||
|
||||
export const sumCounts = (counts: TestStateCount[]) => {
|
||||
const total = makeEmptyCounts();
|
||||
for (const count of counts) {
|
||||
for (const state of statesInOrder) {
|
||||
total[state] += count[state];
|
||||
}
|
||||
}
|
||||
|
||||
return total;
|
||||
};
|
||||
|
||||
const makeNode = (
|
||||
collection: IMainThreadTestCollection,
|
||||
test: IncrementalTestCollectionItem,
|
||||
): TestResultItem => {
|
||||
const mapped: TestResultItem = { ...test, children: [] };
|
||||
for (const childId of test.children) {
|
||||
const child = collection.getNodeById(childId);
|
||||
if (child) {
|
||||
mapped.children.push(makeNode(collection, child));
|
||||
}
|
||||
}
|
||||
|
||||
return mapped;
|
||||
};
|
||||
|
||||
export interface TestResultItem extends InternalTestItem {
|
||||
children: TestResultItem[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Results of a test. These are created when the test initially started running
|
||||
* and marked as "complete" when the run finishes.
|
||||
*/
|
||||
export class TestResult {
|
||||
/**
|
||||
* Creates a new TestResult, pulling tests from the associated list
|
||||
* of collections.
|
||||
*/
|
||||
public static from(
|
||||
collections: ReadonlyArray<IMainThreadTestCollection>,
|
||||
tests: ReadonlyArray<TestIdWithProvider>,
|
||||
) {
|
||||
const mapped: TestResultItem[] = [];
|
||||
for (const test of tests) {
|
||||
for (const collection of collections) {
|
||||
const node = collection.getNodeById(test.testId);
|
||||
if (node) {
|
||||
mapped.push(makeNode(collection, node));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new TestResult(mapped);
|
||||
}
|
||||
|
||||
private completeEmitter = new Emitter<void>();
|
||||
private changeEmitter = new Emitter<void>();
|
||||
private _complete = false;
|
||||
private _cachedCounts?: { [K in TestRunState]: number };
|
||||
|
||||
public onChange = this.changeEmitter.event;
|
||||
public onComplete = this.completeEmitter.event;
|
||||
|
||||
/**
|
||||
* Gets whether the test run has finished.
|
||||
*/
|
||||
public get isComplete() {
|
||||
return this._complete;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a count of tests in each state.
|
||||
*/
|
||||
public get counts() {
|
||||
if (this._cachedCounts) {
|
||||
return this._cachedCounts;
|
||||
}
|
||||
|
||||
const counts = makeEmptyCounts();
|
||||
this.forEachTest(({ item }) => {
|
||||
counts[item.state.runState]++;
|
||||
});
|
||||
|
||||
if (this._complete) {
|
||||
this._cachedCounts = counts;
|
||||
}
|
||||
|
||||
return counts;
|
||||
}
|
||||
|
||||
constructor(public readonly tests: TestResultItem[]) { }
|
||||
|
||||
|
||||
/**
|
||||
* Notifies the service that all tests are complete.
|
||||
*/
|
||||
public markComplete() {
|
||||
if (this._complete) {
|
||||
throw new Error('cannot complete a test result multiple times');
|
||||
}
|
||||
|
||||
// shallow clone test items to 'disconnect' them from the underlying
|
||||
// connection and stop state changes. Also, marked any still-running
|
||||
// tests as skipped.
|
||||
this.forEachTest(test => {
|
||||
test.item = { ...test.item };
|
||||
if (isRunningState(test.item.state.runState)) {
|
||||
test.item.state = { ...test.item.state, runState: TestRunState.Skipped };
|
||||
}
|
||||
});
|
||||
|
||||
this._complete = true;
|
||||
this.completeEmitter.fire();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires the 'change' event, should be called by the runner.
|
||||
*/
|
||||
public notifyChanged() {
|
||||
this.changeEmitter.fire();
|
||||
}
|
||||
|
||||
private forEachTest(fn: (test: TestResultItem) => void) {
|
||||
const queue = [this.tests];
|
||||
while (queue.length) {
|
||||
for (const test of queue.pop()!) {
|
||||
fn(test);
|
||||
queue.push(test.children);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface ITestResultService {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
/**
|
||||
* List of test results. Currently running tests are always at the top.
|
||||
*/
|
||||
readonly results: TestResult[];
|
||||
|
||||
/**
|
||||
* Fired after a new event is added to the 'active' array.
|
||||
*/
|
||||
readonly onNewTestResult: Event<TestResult>;
|
||||
|
||||
/**
|
||||
* Adds a new test result to the collection.
|
||||
*/
|
||||
push(result: TestResult): TestResult;
|
||||
}
|
||||
|
||||
export const ITestResultService = createDecorator<ITestResultService>('testResultService');
|
||||
|
||||
const RETAIN_LAST_RESULTS = 10;
|
||||
|
||||
export class TestResultService implements ITestResultService {
|
||||
declare _serviceBrand: undefined;
|
||||
private newResultEmitter = new Emitter<TestResult>();
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public results: TestResult[] = [];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public readonly onNewTestResult = this.newResultEmitter.event;
|
||||
|
||||
private readonly isRunning: IContextKey<boolean>;
|
||||
|
||||
constructor(@IContextKeyService contextKeyService: IContextKeyService) {
|
||||
this.isRunning = TestingContextKeys.isRunning.bindTo(contextKeyService);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public push(result: TestResult): TestResult {
|
||||
this.results.unshift(result);
|
||||
if (this.results.length > RETAIN_LAST_RESULTS) {
|
||||
this.results.pop();
|
||||
}
|
||||
|
||||
result.onComplete(this.onComplete, this);
|
||||
this.isRunning.set(true);
|
||||
this.newResultEmitter.fire(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private onComplete() {
|
||||
// move the complete test run down behind any still-running ones
|
||||
for (let i = 0; i < this.results.length - 2; i++) {
|
||||
if (this.results[i].isComplete && !this.results[i + 1].isComplete) {
|
||||
[this.results[i], this.results[i + 1]] = [this.results[i + 1], this.results[i]];
|
||||
}
|
||||
}
|
||||
|
||||
this.isRunning.set(!this.results[0]?.isComplete);
|
||||
}
|
||||
}
|
|
@ -75,10 +75,7 @@ export interface ITestService {
|
|||
readonly onDidChangeProviders: Event<{ delta: number; }>;
|
||||
readonly providers: number;
|
||||
readonly subscriptions: ReadonlyArray<{ resource: ExtHostTestingResource, uri: URI; }>;
|
||||
|
||||
readonly testRuns: Iterable<RunTestsRequest>;
|
||||
readonly onTestRunStarted: Event<RunTestsRequest>;
|
||||
readonly onTestRunCompleted: Event<{ req: RunTestsRequest, result: RunTestsResult; }>;
|
||||
|
||||
registerTestController(id: string, controller: MainTestController): void;
|
||||
unregisterTestController(id: string): void;
|
||||
|
|
|
@ -16,6 +16,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
|
|||
import { ExtHostTestingResource } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { AbstractIncrementalTestCollection, collectTestResults, EMPTY_TEST_RESULT, getTestSubscriptionKey, IncrementalTestCollectionItem, InternalTestItem, RunTestsRequest, RunTestsResult, TestDiffOpType, TestIdWithProvider, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys';
|
||||
import { ITestResultService, TestResult } from 'vs/workbench/contrib/testing/common/testResultService';
|
||||
import { IMainThreadTestCollection, ITestService, MainTestController, TestDiffListener } from 'vs/workbench/contrib/testing/common/testService';
|
||||
|
||||
type TestLocationIdent = { resource: ExtHostTestingResource, uri: URI };
|
||||
|
@ -39,7 +40,6 @@ export class TestService extends Disposable implements ITestService {
|
|||
private readonly busyStateChangeEmitter = new Emitter<TestLocationIdent & { busy: boolean }>();
|
||||
private readonly changeProvidersEmitter = new Emitter<{ delta: number }>();
|
||||
private readonly providerCount: IContextKey<number>;
|
||||
private readonly isRunning: IContextKey<boolean>;
|
||||
private readonly runStartedEmitter = new Emitter<RunTestsRequest>();
|
||||
private readonly runCompletedEmitter = new Emitter<{ req: RunTestsRequest, result: RunTestsResult }>();
|
||||
private readonly runningTests = new Map<RunTestsRequest, CancellationTokenSource>();
|
||||
|
@ -48,10 +48,9 @@ export class TestService extends Disposable implements ITestService {
|
|||
public readonly onTestRunStarted = this.runStartedEmitter.event;
|
||||
public readonly onTestRunCompleted = this.runCompletedEmitter.event;
|
||||
|
||||
constructor(@IContextKeyService contextKeyService: IContextKeyService, @INotificationService private readonly notificationService: INotificationService) {
|
||||
constructor(@IContextKeyService contextKeyService: IContextKeyService, @INotificationService private readonly notificationService: INotificationService, @ITestResultService private readonly testResults: ITestResultService) {
|
||||
super();
|
||||
this.providerCount = TestingContextKeys.providerCount.bindTo(contextKeyService);
|
||||
this.isRunning = TestingContextKeys.isRunning.bindTo(contextKeyService);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -128,32 +127,37 @@ export class TestService extends Disposable implements ITestService {
|
|||
* @inheritdoc
|
||||
*/
|
||||
public async runTests(req: RunTestsRequest, token = CancellationToken.None): Promise<RunTestsResult> {
|
||||
const tests = groupBy(req.tests, (a, b) => a.providerId === b.providerId ? 0 : 1);
|
||||
const cancelSource = new CancellationTokenSource(token);
|
||||
const requests = tests.map(group => {
|
||||
const providerId = group[0].providerId;
|
||||
const controller = this.testControllers.get(providerId);
|
||||
return controller?.runTests({ providerId, debug: req.debug, ids: group.map(t => t.testId) }, cancelSource.token).catch(err => {
|
||||
this.notificationService.error(localize('testError', 'An error occurred attempting to run tests: {0}', err.message));
|
||||
let result: TestResult | undefined;
|
||||
const subscriptions = [...this.testSubscriptions.values()]
|
||||
.filter(v => req.tests.some(t => v.collection.getNodeById(t.testId)))
|
||||
.map(s => this.subscribeToDiffs(s.ident.resource, s.ident.uri, () => result?.notifyChanged()));
|
||||
result = this.testResults.push(TestResult.from(subscriptions.map(s => s.collection), req.tests));
|
||||
|
||||
try {
|
||||
const tests = groupBy(req.tests, (a, b) => a.providerId === b.providerId ? 0 : 1);
|
||||
const cancelSource = new CancellationTokenSource(token);
|
||||
const requests = tests.map(group => {
|
||||
const providerId = group[0].providerId;
|
||||
const controller = this.testControllers.get(providerId);
|
||||
return controller?.runTests({ providerId, debug: req.debug, ids: group.map(t => t.testId) }, cancelSource.token).catch(err => {
|
||||
this.notificationService.error(localize('testError', 'An error occurred attempting to run tests: {0}', err.message));
|
||||
return EMPTY_TEST_RESULT;
|
||||
});
|
||||
}).filter(isDefined);
|
||||
|
||||
if (requests.length === 0) {
|
||||
return EMPTY_TEST_RESULT;
|
||||
});
|
||||
}).filter(isDefined);
|
||||
}
|
||||
|
||||
if (requests.length === 0) {
|
||||
return EMPTY_TEST_RESULT;
|
||||
this.runningTests.set(req, cancelSource);
|
||||
const result = collectTestResults(await Promise.all(requests));
|
||||
this.runningTests.delete(req);
|
||||
|
||||
return result;
|
||||
} finally {
|
||||
subscriptions.forEach(s => s.dispose());
|
||||
result.markComplete();
|
||||
}
|
||||
|
||||
this.runningTests.set(req, cancelSource);
|
||||
this.runStartedEmitter.fire(req);
|
||||
this.isRunning.set(true);
|
||||
|
||||
const result = collectTestResults(await Promise.all(requests));
|
||||
|
||||
this.runningTests.delete(req);
|
||||
this.runCompletedEmitter.fire({ req, result });
|
||||
this.isRunning.set(this.runningTests.size > 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -223,7 +227,7 @@ export class TestService extends Disposable implements ITestService {
|
|||
const sub = this.testSubscriptions.get(getTestSubscriptionKey(resource, URI.revive(uri)));
|
||||
if (sub) {
|
||||
sub.collection.apply(diff);
|
||||
console.log('accept', sub.collection, diff);
|
||||
// console.log('accept', sub.collection, diff);
|
||||
sub.onDiff.fire(diff);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,3 +38,5 @@ export const cmpPriority = (a: TestRunState, b: TestRunState) => statePriority[b
|
|||
export const maxPriority = (a: TestRunState, b: TestRunState) => statePriority[a] > statePriority[b] ? a : b;
|
||||
|
||||
export const statesInOrder = Object.keys(statePriority).map(s => Number(s) as TestRunState).sort(cmpPriority);
|
||||
|
||||
export const isRunningState = (s: TestRunState) => s === TestRunState.Queued || s === TestRunState.Running;
|
|
@ -79,10 +79,10 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench
|
|||
|
||||
getEnablementState(extension: IExtension): EnablementState {
|
||||
if (this.extensionBisectService.isDisabledByBisect(extension)) {
|
||||
return EnablementState.DisabledByEnvironemt;
|
||||
return EnablementState.DisabledByEnvironment;
|
||||
}
|
||||
if (this._isDisabledInEnv(extension)) {
|
||||
return EnablementState.DisabledByEnvironemt;
|
||||
return EnablementState.DisabledByEnvironment;
|
||||
}
|
||||
if (this._isDisabledByExtensionKind(extension)) {
|
||||
return EnablementState.DisabledByExtensionKind;
|
||||
|
@ -97,7 +97,7 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench
|
|||
return false;
|
||||
}
|
||||
const enablementState = this.getEnablementState(extension);
|
||||
if (enablementState === EnablementState.DisabledByEnvironemt || enablementState === EnablementState.DisabledByExtensionKind) {
|
||||
if (enablementState === EnablementState.DisabledByEnvironment || enablementState === EnablementState.DisabledByExtensionKind) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue