未验证 提交 4ae47297 编写于 作者: D Daniel Imms 提交者: GitHub

Merge branch 'master' into merogge/integration

......@@ -57,7 +57,6 @@ const compilations = [
'microsoft-authentication/tsconfig.json',
'npm/tsconfig.json',
'php-language-features/tsconfig.json',
'python/tsconfig.json',
'search-result/tsconfig.json',
'simple-browser/tsconfig.json',
'testing-editor-contributions/tsconfig.json',
......
......@@ -132,11 +132,11 @@
"git": {
"name": "vscode-codicons",
"repositoryUrl": "https://github.com/microsoft/vscode-codicons",
"commitHash": "f0caa623812a8ed5059516277675b4158d4c4867"
"commitHash": "ccdcf91d57d3a5a1d6b620d95d518bab4d75984d"
}
},
"license": "MIT and Creative Commons Attribution 4.0",
"version": "0.0.1"
"version": "0.0.14"
},
{
"component": {
......
......@@ -807,8 +807,8 @@
},
"dependencies": {
"vscode-languageclient": "^7.0.0",
"vscode-nls": "^4.1.2",
"vscode-uri": "^3.0.1"
"vscode-nls": "^5.0.0",
"vscode-uri": "^3.0.2"
},
"devDependencies": {
"@types/node": "^12.19.9"
......
......@@ -10,9 +10,9 @@
"main": "./out/node/cssServerMain",
"browser": "./dist/browser/cssServerMain",
"dependencies": {
"vscode-css-languageservice": "^5.0.2",
"vscode-css-languageservice": "^5.0.3",
"vscode-languageserver": "^7.0.0",
"vscode-uri": "^3.0.1"
"vscode-uri": "^3.0.2"
},
"devDependencies": {
"@types/mocha": "^8.2.0",
......
......@@ -12,15 +12,15 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.19.9.tgz#990ad687ad8b26ef6dcc34a4f69c33d40c95b679"
integrity sha512-yj0DOaQeUrk3nJ0bd3Y5PeDRJ6W0r+kilosLA+dzF3dola/o9hxhMSg2sFvVcA2UHS5JSOsZp4S0c1OEXc4m1Q==
vscode-css-languageservice@^5.0.2:
version "5.0.2"
resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-5.0.2.tgz#92ec069c797dfcf5a67313a18f0211c27b0c6bf6"
integrity sha512-iGOukMyK4EVDIArBMuDmKpBDvg+zoXPyJZi2mGPICKkKTiK8mDAlTr4uxhldg/dwpABEaMlNb+5xeCFDaUyRlQ==
vscode-css-languageservice@^5.0.3:
version "5.0.3"
resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-5.0.3.tgz#2d400a47e73d0bfc5bc0d3fdf5be487cfdca341b"
integrity sha512-KJt4jhCxqrgGrC02UsQsKw90dPkFknMHsH5HTInT7gkDRRfGFwEd+e2O1/E75br3TdFhvRmzjljYz5thZ58L3A==
dependencies:
vscode-languageserver-textdocument "^1.0.1"
vscode-languageserver-types "^3.16.0"
vscode-nls "^5.0.0"
vscode-uri "^3.0.1"
vscode-uri "^3.0.2"
vscode-jsonrpc@6.0.0:
version "6.0.0"
......@@ -57,7 +57,7 @@ vscode-nls@^5.0.0:
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.0.tgz#99f0da0bd9ea7cda44e565a74c54b1f2bc257840"
integrity sha512-u0Lw+IYlgbEJFF6/qAqG2d1jQmJl0eyAGJHoAJqr2HT4M2BNuQYSEiSE75f52pXHSJm8AlTjnLLbBFPrdz2hpA==
vscode-uri@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.1.tgz#c36502fcf6fa57e63441d3804b5826018e26062e"
integrity sha512-LnMgm97uZM2JDjX/vKbbCk+phm++Ih31e5Ao3lqokawhDRocp2ZAVMRiIhPZx6fS5Sqnquyhxh8ABn9TWCvHoA==
vscode-uri@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.2.tgz#ecfd1d066cb8ef4c3a208decdbab9a8c23d055d0"
integrity sha512-jkjy6pjU1fxUvI51P+gCsxg1u2n8LSt0W6KrCNQceaziKzff74GoWmjVG46KieVzybO1sttPQmYfrwSHey7GUA==
......@@ -73,15 +73,15 @@ vscode-languageserver-types@3.16.0:
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz#ecf393fc121ec6974b2da3efb3155644c514e247"
integrity sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==
vscode-nls@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.2.tgz#ca8bf8bb82a0987b32801f9fddfdd2fb9fd3c167"
integrity sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw==
vscode-nls@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.0.tgz#99f0da0bd9ea7cda44e565a74c54b1f2bc257840"
integrity sha512-u0Lw+IYlgbEJFF6/qAqG2d1jQmJl0eyAGJHoAJqr2HT4M2BNuQYSEiSE75f52pXHSJm8AlTjnLLbBFPrdz2hpA==
vscode-uri@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.1.tgz#c36502fcf6fa57e63441d3804b5826018e26062e"
integrity sha512-LnMgm97uZM2JDjX/vKbbCk+phm++Ih31e5Ao3lqokawhDRocp2ZAVMRiIhPZx6fS5Sqnquyhxh8ABn9TWCvHoA==
vscode-uri@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.2.tgz#ecfd1d066cb8ef4c3a208decdbab9a8c23d055d0"
integrity sha512-jkjy6pjU1fxUvI51P+gCsxg1u2n8LSt0W6KrCNQceaziKzff74GoWmjVG46KieVzybO1sttPQmYfrwSHey7GUA==
yallist@^4.0.0:
version "4.0.0"
......
......@@ -9,11 +9,11 @@
},
"main": "./out/node/htmlServerMain",
"dependencies": {
"vscode-css-languageservice": "^5.0.0",
"vscode-html-languageservice": "^4.0.0",
"vscode-css-languageservice": "^5.0.3",
"vscode-html-languageservice": "^4.0.1",
"vscode-languageserver": "^7.0.0",
"vscode-nls": "^5.0.0",
"vscode-uri": "^2.1.2"
"vscode-uri": "^3.0.2"
},
"devDependencies": {
"@types/mocha": "^8.2.0",
......
......@@ -12,25 +12,25 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.19.9.tgz#990ad687ad8b26ef6dcc34a4f69c33d40c95b679"
integrity sha512-yj0DOaQeUrk3nJ0bd3Y5PeDRJ6W0r+kilosLA+dzF3dola/o9hxhMSg2sFvVcA2UHS5JSOsZp4S0c1OEXc4m1Q==
vscode-css-languageservice@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-5.0.0.tgz#04fd899e25407a2fccd8f59a5896e2f020269bda"
integrity sha512-DTMa8QbVmujFPvD3NxoC5jjIXCyCG+cvn3hNzwQRhvhsk8LblNymBZBwzfcDdgEtqsi4O/2AB5HnMIRzxhzEzg==
vscode-css-languageservice@^5.0.3:
version "5.0.3"
resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-5.0.3.tgz#2d400a47e73d0bfc5bc0d3fdf5be487cfdca341b"
integrity sha512-KJt4jhCxqrgGrC02UsQsKw90dPkFknMHsH5HTInT7gkDRRfGFwEd+e2O1/E75br3TdFhvRmzjljYz5thZ58L3A==
dependencies:
vscode-languageserver-textdocument "^1.0.1"
vscode-languageserver-types "^3.16.0"
vscode-nls "^5.0.0"
vscode-uri "^2.1.2"
vscode-uri "^3.0.2"
vscode-html-languageservice@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-4.0.0.tgz#a562cb1dfe7e40a9d1f50dbd8c4ec2d02f393f01"
integrity sha512-UmC+GS0IqBeZnOAmdtQvaDzoH1c5/un+b7qALUziu/Y4SOPXso5dF+YkJeTqsde6YU2pLm78RtMDzl9BParwbw==
vscode-html-languageservice@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-4.0.1.tgz#6fb50fcf782937dffe0de55e12a8c43c817ec0f1"
integrity sha512-CZtnuQoDwZdmPLKLMC6RqFlRTw0jvZK71l53u5ZIM3hSoVKAqW33gahBVNFpC3TPFxZSx0jqEhBTLf37RUMkWg==
dependencies:
vscode-languageserver-textdocument "^1.0.1"
vscode-languageserver-types "^3.16.0"
vscode-nls "^5.0.0"
vscode-uri "^2.1.2"
vscode-uri "^3.0.2"
vscode-jsonrpc@6.0.0:
version "6.0.0"
......@@ -67,7 +67,7 @@ vscode-nls@^5.0.0:
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.0.tgz#99f0da0bd9ea7cda44e565a74c54b1f2bc257840"
integrity sha512-u0Lw+IYlgbEJFF6/qAqG2d1jQmJl0eyAGJHoAJqr2HT4M2BNuQYSEiSE75f52pXHSJm8AlTjnLLbBFPrdz2hpA==
vscode-uri@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.1.2.tgz#c8d40de93eb57af31f3c715dd650e2ca2c096f1c"
integrity sha512-8TEXQxlldWAuIODdukIb+TR5s+9Ds40eSJrw+1iDDA9IFORPjMELarNQE3myz5XIkWWpdprmJjm1/SxMlWOC8A==
vscode-uri@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.2.tgz#ecfd1d066cb8ef4c3a208decdbab9a8c23d055d0"
integrity sha512-jkjy6pjU1fxUvI51P+gCsxg1u2n8LSt0W6KrCNQceaziKzff74GoWmjVG46KieVzybO1sttPQmYfrwSHey7GUA==
......@@ -14,9 +14,9 @@
"dependencies": {
"jsonc-parser": "^3.0.0",
"request-light": "^0.4.0",
"vscode-json-languageservice": "^4.0.0",
"vscode-json-languageservice": "^4.0.2",
"vscode-languageserver": "^7.0.0",
"vscode-uri": "^2.1.2"
"vscode-uri": "^3.0.2"
},
"devDependencies": {
"@types/mocha": "^8.2.0",
......
......@@ -80,16 +80,16 @@ request-light@^0.4.0:
https-proxy-agent "^2.2.4"
vscode-nls "^4.1.2"
vscode-json-languageservice@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-4.0.0.tgz#b4b81be7e49ee36b0227bb800131a0a5abbcdd13"
integrity sha512-5Px31Pj+Rrw9/S9SvPs2+Zh0wrnsG/N3MMrYs7zV495RdC0hev5Y1rtBVRivnucQohLgetUQ0jN9M/ScT60Oyg==
vscode-json-languageservice@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-4.0.2.tgz#8f91dc3a33dac180063067f8277f4facdc0795b6"
integrity sha512-d8Ahw990Cq/G60CzN26rehXcbhbMgMGMmXeN6C/V/RYZUhfs16EELRK+EL7b/3Y8ZGshtKqboePSeDVa94qqFg==
dependencies:
jsonc-parser "^3.0.0"
vscode-languageserver-textdocument "^1.0.1"
vscode-languageserver-types "^3.16.0"
vscode-nls "^5.0.0"
vscode-uri "^2.1.2"
vscode-uri "^3.0.2"
vscode-jsonrpc@6.0.0:
version "6.0.0"
......@@ -131,7 +131,7 @@ vscode-nls@^5.0.0:
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.0.tgz#99f0da0bd9ea7cda44e565a74c54b1f2bc257840"
integrity sha512-u0Lw+IYlgbEJFF6/qAqG2d1jQmJl0eyAGJHoAJqr2HT4M2BNuQYSEiSE75f52pXHSJm8AlTjnLLbBFPrdz2hpA==
vscode-uri@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.1.2.tgz#c8d40de93eb57af31f3c715dd650e2ca2c096f1c"
integrity sha512-8TEXQxlldWAuIODdukIb+TR5s+9Ds40eSJrw+1iDDA9IFORPjMELarNQE3myz5XIkWWpdprmJjm1/SxMlWOC8A==
vscode-uri@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.2.tgz#ecfd1d066cb8ef4c3a208decdbab9a8c23d055d0"
integrity sha512-jkjy6pjU1fxUvI51P+gCsxg1u2n8LSt0W6KrCNQceaziKzff74GoWmjVG46KieVzybO1sttPQmYfrwSHey7GUA==
......@@ -16,6 +16,7 @@ import { toBase64UrlEncoding } from './utils';
import fetch, { Response } from 'node-fetch';
import { sha256 } from './env/node/sha256';
import * as nls from 'vscode-nls';
import { MicrosoftAuthenticationSession } from './microsoft-authentication';
const localize = nls.loadMessageBundle();
......@@ -26,6 +27,7 @@ const tenant = 'organizations';
interface IToken {
accessToken?: string; // When unable to refresh due to network problems, the access token becomes undefined
idToken?: string; // depending on the scopes can be either supplied or empty
expiresIn?: number; // How long access token is valid, in seconds
expiresAt?: number; // UNIX epoch time at which token will expire
......@@ -71,6 +73,11 @@ export interface ITokenResponse {
id_token?: string;
}
export interface IMicrosoftTokens {
accessToken: string;
idToken?: string;
}
function parseQuery(uri: vscode.Uri) {
return uri.query.split('&').reduce((prev: any, current) => {
const queryString = current.split('=');
......@@ -228,29 +235,36 @@ export class AzureActiveDirectoryService {
}
}
private async convertToSession(token: IToken): Promise<vscode.AuthenticationSession> {
const resolvedToken = await this.resolveAccessToken(token);
private async convertToSession(token: IToken): Promise<MicrosoftAuthenticationSession> {
const resolvedTokens = await this.resolveAccessAndIdTokens(token);
return {
id: token.sessionId,
accessToken: resolvedToken,
accessToken: resolvedTokens.accessToken,
idToken: resolvedTokens.idToken,
account: token.account,
scopes: token.scope.split(' ')
};
}
private async resolveAccessToken(token: IToken): Promise<string> {
private async resolveAccessAndIdTokens(token: IToken): Promise<IMicrosoftTokens> {
if (token.accessToken && (!token.expiresAt || token.expiresAt > Date.now())) {
token.expiresAt
? Logger.info(`Token available from cache, expires in ${token.expiresAt - Date.now()} milliseconds`)
: Logger.info('Token available from cache');
return Promise.resolve(token.accessToken);
return Promise.resolve({
accessToken: token.accessToken,
idToken: token.idToken
});
}
try {
Logger.info('Token expired or unavailable, trying refresh');
const refreshedToken = await this.refreshToken(token.refreshToken, token.scope, token.sessionId);
if (refreshedToken.accessToken) {
return refreshedToken.accessToken;
return {
accessToken: refreshedToken.accessToken,
idToken: refreshedToken.idToken
};
} else {
throw new Error();
}
......@@ -501,6 +515,7 @@ export class AzureActiveDirectoryService {
expiresIn: json.expires_in,
expiresAt: json.expires_in ? Date.now() + json.expires_in * 1000 : undefined,
accessToken: json.access_token,
idToken: json.id_token,
refreshToken: json.refresh_token,
scope,
sessionId: existingId || `${claims.tid}/${(claims.oid || (claims.altsecid || '' + claims.ipd || ''))}/${uuid()}`,
......
......@@ -3,15 +3,14 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
//@ts-check
import { AuthenticationSession } from 'vscode';
'use strict';
const withDefaults = require('../shared.webpack.config');
module.exports = withDefaults({
context: __dirname,
entry: {
pythonMain: './src/pythonMain.ts'
}
});
/**
* Represents a session of a currently logged in Microsoft user.
*/
export interface MicrosoftAuthenticationSession extends AuthenticationSession {
/**
* The id token.
*/
idToken?: string;
}
\ No newline at end of file
test/**
src/**
out/**
tsconfig.json
extension.webpack.config.js
extension-browser.webpack.config.js
cgmanifest.json
.vscode
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
//@ts-check
'use strict';
const withBrowserDefaults = require('../shared.webpack.config').browser;
module.exports = withBrowserDefaults({
context: __dirname,
entry: {
extension: './src/pythonMain.ts'
},
output: {
filename: 'pythonMain.js'
}
});
......@@ -46,5 +46,11 @@
"start": "^\\s*#\\s*region\\b",
"end": "^\\s*#\\s*endregion\\b"
}
}
},
"onEnterRules": [
{
"beforeText": "^\\s*(?:def|class|for|if|elif|else|while|try|with|finally|except|async).*?:\\s*$",
"action": { "indent": "indent" }
}
]
}
......@@ -6,9 +6,6 @@
"publisher": "vscode",
"license": "MIT",
"engines": { "vscode": "*" },
"activationEvents": ["onLanguage:python"],
"main": "./out/pythonMain",
"browser": "./dist/browser/pythonMain",
"extensionKind": [ "ui", "workspace", "web" ],
"contributes": {
"languages": [{
......@@ -29,8 +26,6 @@
}]
},
"scripts": {
"compile": "gulp compile-extension:python",
"watch": "gulp watch-extension:python",
"update-grammar": "node ../../build/npm/update-grammar.js MagicStack/MagicPython grammars/MagicPython.tmLanguage ./syntaxes/MagicPython.tmLanguage.json grammars/MagicRegExp.tmLanguage ./syntaxes/MagicRegExp.tmLanguage.json"
}
}
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ExtensionContext, languages, IndentAction } from 'vscode';
export function activate(_context: ExtensionContext): any {
languages.setLanguageConfiguration('python', {
onEnterRules: [
{
beforeText: /^\s*(?:def|class|for|if|elif|else|while|try|with|finally|except|async).*?:\s*$/,
action: { indentAction: IndentAction.Indent }
}
]
});
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/// <reference path='../../../../src/vs/vscode.d.ts'/>
\ No newline at end of file
{
"extends": "../shared.tsconfig.json",
"compilerOptions": {
"outDir": "./out"
},
"include": [
"src/**/*"
]
}
\ No newline at end of file
......@@ -20,11 +20,13 @@ export interface ILinkDescriptor {
export interface ILinkStyles {
readonly textLinkForeground?: Color;
readonly disabled?: boolean;
}
export class Link extends Disposable {
readonly el: HTMLAnchorElement;
private disabled: boolean;
private styles: ILinkStyles = {
textLinkForeground: Color.fromHex('#006AB1')
};
......@@ -50,9 +52,12 @@ export class Link extends Disposable {
this._register(onOpen(e => {
EventHelper.stop(e, true);
openerService.open(link.href);
if (!this.disabled) {
openerService.open(link.href);
}
}));
this.disabled = false;
this.applyStyles();
}
......@@ -62,6 +67,26 @@ export class Link extends Disposable {
}
private applyStyles(): void {
this.el.style.color = this.styles.textLinkForeground?.toString() || '';
const color = this.styles.textLinkForeground?.toString();
if (color) {
this.el.style.color = color;
}
if (typeof this.styles.disabled === 'boolean' && this.styles.disabled !== this.disabled) {
if (this.styles.disabled) {
this.el.setAttribute('aria-disabled', 'true');
this.el.tabIndex = -1;
this.el.style.pointerEvents = 'none';
this.el.style.opacity = '0.4';
this.el.style.cursor = 'default';
this.disabled = true;
} else {
this.el.setAttribute('aria-disabled', 'false');
this.el.tabIndex = 0;
this.el.style.pointerEvents = 'auto';
this.el.style.opacity = '1';
this.el.style.cursor = 'pointer';
this.disabled = false;
}
}
}
}
......@@ -2074,13 +2074,31 @@ declare module 'vscode' {
* Returns an observer that retrieves tests in the given text document.
*/
export function createDocumentTestObserver(document: TextDocument): TestObserver;
/**
* The last or selected test run. Cleared when a new test run starts.
*/
export const testResults: TestResults | undefined;
/**
* Event that fires when the testResults are updated.
*/
export const onDidChangeTestResults: Event<void>;
}
export interface TestResults {
/**
* The results from the latest test run. The array contains a snapshot of
* all tests involved in the run at the moment when it completed.
*/
readonly tests: ReadonlyArray<RequiredTestItem> | undefined;
}
export interface TestObserver {
/**
* List of tests returned by test provider for files in the workspace.
*/
readonly tests: ReadonlyArray<TestItem>;
readonly tests: ReadonlyArray<RequiredTestItem>;
/**
* An event that fires when an existing test in the collection changes, or
......@@ -2110,23 +2128,23 @@ declare module 'vscode' {
/**
* List of all tests that are newly added.
*/
readonly added: ReadonlyArray<TestItem>;
readonly added: ReadonlyArray<RequiredTestItem>;
/**
* List of existing tests that have updated.
*/
readonly updated: ReadonlyArray<TestItem>;
readonly updated: ReadonlyArray<RequiredTestItem>;
/**
* List of existing tests that have been removed.
*/
readonly removed: ReadonlyArray<TestItem>;
readonly removed: ReadonlyArray<RequiredTestItem>;
/**
* Highest node in the test tree under which changes were made. This can
* be easily plugged into events like the TreeDataProvider update event.
*/
readonly commonChangeAncestor: TestItem | null;
readonly commonChangeAncestor: RequiredTestItem | null;
}
/**
......@@ -2232,6 +2250,16 @@ declare module 'vscode' {
*/
label: string;
/**
* Optional unique identifier for the TestItem. This is used to correlate
* test results and tests in the document with those in the workspace
* (test explorer). This must not change for the lifetime of a test item.
*
* If the ID is not provided, it defaults to the concatenation of the
* item's label and its parent's ID, if any.
*/
readonly id?: string;
/**
* Optional description that appears next to the label.
*/
......@@ -2268,6 +2296,15 @@ declare module 'vscode' {
state: TestState;
}
/**
* A {@link TestItem} with its defaults filled in.
*/
export type RequiredTestItem = {
[K in keyof Required<TestItem>]: K extends 'children'
? RequiredTestItem[]
: (K extends 'description' | 'location' ? TestItem[K] : Required<TestItem>[K])
};
export enum TestRunState {
// Initial state
Unset = 0,
......
......@@ -4,10 +4,11 @@
*--------------------------------------------------------------------------------------------*/
import { CancellationToken } from 'vs/base/common/cancellation';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { Disposable, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle';
import { URI, UriComponents } from 'vs/base/common/uri';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { getTestSubscriptionKey, RunTestsRequest, RunTestsResult, TestDiffOpType, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService';
import { ITestService } from 'vs/workbench/contrib/testing/common/testService';
import { ExtHostContext, ExtHostTestingResource, ExtHostTestingShape, IExtHostContext, MainContext, MainThreadTestingShape } from '../common/extHost.protocol';
......@@ -36,13 +37,25 @@ export class MainThreadTesting extends Disposable implements MainThreadTestingSh
constructor(
extHostContext: IExtHostContext,
@ITestService private readonly testService: ITestService,
@ITestResultService resultService: ITestResultService,
) {
super();
this.proxy = extHostContext.getProxy(ExtHostContext.ExtHostTesting);
this._register(this.testService.onShouldSubscribe(args => this.proxy.$subscribeToTests(args.resource, args.uri)));
this._register(this.testService.onShouldUnsubscribe(args => this.proxy.$unsubscribeFromTests(args.resource, args.uri)));
const testCompleteListener = this._register(new MutableDisposable());
this._register(resultService.onNewTestResult(results => {
testCompleteListener.value = results.onComplete(() => this.proxy.$publishTestResults({ tests: results.tests }));
}));
testService.updateRootProviderCount(1);
const lastCompleted = resultService.results.find(r => !r.isComplete);
if (lastCompleted) {
this.proxy.$publishTestResults({ tests: lastCompleted.tests });
}
for (const { resource, uri } of this.testService.subscriptions) {
this.proxy.$subscribeToTests(resource, uri);
}
......
......@@ -340,6 +340,14 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
checkProposedApiEnabled(extension);
return extHostTesting.runTests(provider);
},
get onDidChangeTestResults() {
checkProposedApiEnabled(extension);
return extHostTesting.onLastResultsChanged;
},
get testResults() {
checkProposedApiEnabled(extension);
return extHostTesting.lastResults;
},
};
// namespace: extensions
......
......@@ -58,7 +58,7 @@ import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib
import { DebugConfigurationProviderTriggerKind } from 'vs/workbench/api/common/extHostTypes';
import { IAccessibilityInformation } from 'vs/platform/accessibility/common/accessibility';
import { IExtensionIdWithVersion } from 'vs/platform/userDataSync/common/extensionsStorageSync';
import { InternalTestItem, RunTestForProviderRequest, RunTestsRequest, RunTestsResult, TestIdWithProvider, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
import { InternalTestItem, InternalTestResults, RunTestForProviderRequest, RunTestsRequest, RunTestsResult, TestIdWithProvider, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
import { CandidatePort } from 'vs/workbench/services/remote/common/remoteExplorerService';
export interface IEnvironment {
......@@ -1843,6 +1843,7 @@ export interface ExtHostTestingShape {
$unsubscribeFromTests(resource: ExtHostTestingResource, uri: UriComponents): void;
$lookupTest(test: TestIdWithProvider): Promise<InternalTestItem | undefined>;
$acceptDiff(resource: ExtHostTestingResource, uri: UriComponents, diff: TestsDiff): void;
$publishTestResults(results: InternalTestResults): void;
}
export interface MainThreadTestingShape {
......
......@@ -13,18 +13,20 @@ import { isDefined } from 'vs/base/common/types';
import { URI, UriComponents } from 'vs/base/common/uri';
import { generateUuid } from 'vs/base/common/uuid';
import { ExtHostTestingResource, ExtHostTestingShape, MainContext, MainThreadTestingShape } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData';
import { IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { TestItem } from 'vs/workbench/api/common/extHostTypeConverters';
import { Disposable, RequiredTestItem } from 'vs/workbench/api/common/extHostTypes';
import { Disposable } from 'vs/workbench/api/common/extHostTypes';
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
import { OwnedTestCollection, SingleUseTestCollection } from 'vs/workbench/contrib/testing/common/ownedTestCollection';
import { AbstractIncrementalTestCollection, EMPTY_TEST_RESULT, IncrementalChangeCollector, IncrementalTestCollectionItem, InternalTestItem, RunTestForProviderRequest, RunTestsResult, TestDiffOpType, TestIdWithProvider, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
import { AbstractIncrementalTestCollection, EMPTY_TEST_RESULT, IncrementalChangeCollector, IncrementalTestCollectionItem, InternalTestItem, InternalTestItemWithChildren, InternalTestResults, RunTestForProviderRequest, RunTestsResult, TestDiffOpType, TestIdWithProvider, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
import type * as vscode from 'vscode';
const getTestSubscriptionKey = (resource: ExtHostTestingResource, uri: URI) => `${resource}:${uri.toString()}`;
export class ExtHostTesting implements ExtHostTestingShape {
private readonly resultsChangedEmitter = new Emitter<void>();
private readonly providers = new Map<string, vscode.TestProvider>();
private readonly proxy: MainThreadTestingShape;
private readonly ownedTests = new OwnedTestCollection();
......@@ -37,6 +39,9 @@ export class ExtHostTesting implements ExtHostTestingShape {
private workspaceObservers: WorkspaceFolderTestObserverFactory;
private textDocumentObservers: TextDocumentTestObserverFactory;
public onLastResultsChanged = this.resultsChangedEmitter.event;
public lastResults?: vscode.TestResults;
constructor(@IExtHostRpcService rpc: IExtHostRpcService, @IExtHostDocumentsAndEditors private readonly documents: IExtHostDocumentsAndEditors, @IExtHostWorkspace private readonly workspace: IExtHostWorkspace) {
this.proxy = rpc.getProxy(MainContext.MainThreadTesting);
this.workspaceObservers = new WorkspaceFolderTestObserverFactory(this.proxy);
......@@ -97,6 +102,19 @@ export class ExtHostTesting implements ExtHostTestingShape {
}, token);
}
/**
* Updates test results shown to extensions.
* @override
*/
public $publishTestResults(results: InternalTestResults): void {
const convert = (item: InternalTestItemWithChildren): vscode.RequiredTestItem =>
({ ...TestItem.toShallow(item.item), children: item.children.map(convert) });
this.lastResults = { tests: results.tests.map(convert) };
this.resultsChangedEmitter.fire();
}
/**
* Handles a request to read tests for a file, or workspace.
* @override
......@@ -110,12 +128,26 @@ export class ExtHostTesting implements ExtHostTestingShape {
let method: undefined | ((p: vscode.TestProvider) => vscode.TestHierarchy<vscode.TestItem> | undefined);
if (resource === ExtHostTestingResource.TextDocument) {
const document = this.documents.getDocument(uri);
let document = this.documents.getDocument(uri);
// we can ask to subscribe to tests before the documents are populated in
// the extension host. Try to wait.
if (!document) {
const store = new DisposableStore();
document = await new Promise<ExtHostDocumentData | undefined>(resolve => {
store.add(disposableTimeout(() => resolve(undefined), 5000));
store.add(this.documents.onDidAddDocuments(e => {
const data = e.find(data => data.document.uri.toString() === uri.toString());
if (data) { resolve(data); }
}));
}).finally(() => store.dispose());
}
if (document) {
const folder = await this.workspace.getWorkspaceFolder2(uri, false);
method = p => p.createDocumentTestHierarchy
? p.createDocumentTestHierarchy(document.document)
: this.createDefaultDocumentTestHierarchy(p, document.document, folder);
? p.createDocumentTestHierarchy(document!.document)
: this.createDefaultDocumentTestHierarchy(p, document!.document, folder);
}
} else {
const folder = await this.workspace.getWorkspaceFolder2(uri, false);
......@@ -383,7 +415,7 @@ export class TestItemFilteredWrapper implements vscode.TestItem {
interface MirroredCollectionTestItem extends IncrementalTestCollectionItem {
revived: vscode.TestItem;
depth: number;
wrapped?: vscode.TestItem;
wrapped?: vscode.RequiredTestItem;
}
class MirroredChangeCollector extends IncrementalChangeCollector<MirroredCollectionTestItem> {
......@@ -412,7 +444,7 @@ class MirroredChangeCollector extends IncrementalChangeCollector<MirroredCollect
* @override
*/
public update(node: MirroredCollectionTestItem): void {
Object.assign(node.revived, TestItem.to(node.item));
Object.assign(node.revived, TestItem.toShallow(node.item));
if (!this.added.has(node)) {
this.updated.add(node);
}
......@@ -546,8 +578,8 @@ export class MirroredTestCollection extends AbstractIncrementalTestCollection<Mi
/**
* Translates the item IDs to TestItems for exposure to extensions.
*/
public getAllAsTestItem(itemIds: Iterable<string>): vscode.TestItem[] {
let output: vscode.TestItem[] = [];
public getAllAsTestItem(itemIds: Iterable<string>): vscode.RequiredTestItem[] {
let output: vscode.RequiredTestItem[] = [];
for (const itemId of itemIds) {
const item = this.items.get(itemId);
if (item) {
......@@ -578,7 +610,7 @@ export class MirroredTestCollection extends AbstractIncrementalTestCollection<Mi
* @override
*/
protected createItem(item: InternalTestItem, parent?: MirroredCollectionTestItem): MirroredCollectionTestItem {
return { ...item, revived: TestItem.to(item.item), depth: parent ? parent.depth + 1 : 0, children: new Set() };
return { ...item, revived: TestItem.toShallow(item.item), depth: parent ? parent.depth + 1 : 0, children: new Set() };
}
/**
......@@ -591,9 +623,9 @@ export class MirroredTestCollection extends AbstractIncrementalTestCollection<Mi
/**
* Gets the public test item instance for the given mirrored record.
*/
public getPublicTestItem(item: MirroredCollectionTestItem): vscode.TestItem {
public getPublicTestItem(item: MirroredCollectionTestItem): vscode.RequiredTestItem {
if (!item.wrapped) {
item.wrapped = new ExtHostTestItem(item, this);
item.wrapped = new TestItemFromMirror(item, this);
}
return item.wrapped;
......@@ -606,10 +638,11 @@ const getMirroredItemId = (item: vscode.TestItem) => {
const MirroredItemId = Symbol('MirroredItemId');
class ExtHostTestItem implements vscode.TestItem, RequiredTestItem {
class TestItemFromMirror implements vscode.RequiredTestItem {
readonly #internal: MirroredCollectionTestItem;
readonly #collection: MirroredTestCollection;
public get id() { return this.#internal.revived.id!; }
public get label() { return this.#internal.revived.label; }
public get description() { return this.#internal.revived.description; }
public get state() { return this.#internal.revived.state; }
......@@ -628,14 +661,15 @@ class ExtHostTestItem implements vscode.TestItem, RequiredTestItem {
}
public toJSON() {
const serialized: RequiredTestItem & TestIdWithProvider = {
const serialized: vscode.RequiredTestItem & TestIdWithProvider = {
id: this.id,
label: this.label,
description: this.description,
state: this.state,
location: this.location,
runnable: this.runnable,
debuggable: this.debuggable,
children: this.children.map(c => (c as ExtHostTestItem).toJSON()),
children: this.children.map(c => (c as TestItemFromMirror).toJSON()),
providerId: this.#internal.providerId,
testId: this.#internal.id,
......
......@@ -1412,8 +1412,9 @@ export namespace TestState {
export namespace TestItem {
export function from(item: vscode.TestItem): ITestItem {
export function from(item: vscode.TestItem, parentExtId?: string): ITestItem {
return {
extId: item.id ?? (parentExtId ? `${parentExtId}\0${item.label}` : item.label),
label: item.label,
location: item.location ? location.from(item.location) : undefined,
debuggable: item.debuggable ?? false,
......@@ -1423,8 +1424,9 @@ export namespace TestItem {
};
}
export function to(item: ITestItem): vscode.TestItem {
export function toShallow(item: ITestItem): Omit<vscode.RequiredTestItem, 'children'> {
return {
id: item.extId,
label: item.label,
location: item.location && location.to({
range: item.location.range,
......
......@@ -2978,15 +2978,7 @@ export class TestState {
}
}
type AllowedUndefined = 'description' | 'location';
/**
* Test item without any optional properties. Only some properties are
* permitted to be undefined, but they must still exist.
*/
export type RequiredTestItem = {
[K in keyof Required<vscode.TestItem>]: K extends AllowedUndefined ? vscode.TestItem[K] : Required<vscode.TestItem>[K]
};
export type RequiredTestItem = vscode.RequiredTestItem;
export type TestItem = vscode.TestItem;
......
......@@ -601,6 +601,16 @@ export abstract class ViewPane extends Pane implements IView {
append(p, link.el);
disposables.add(link);
disposables.add(attachLinkStyler(link, this.themeService));
if (precondition && node.href.startsWith('command:')) {
const updateEnablement = () => link.style({ disabled: !this.contextKeyService.contextMatchesRules(precondition) });
updateEnablement();
const keys = new Set();
precondition.keys().forEach(key => keys.add(key));
const onDidChangeContext = Event.filter(this.contextKeyService.onDidChangeContext, e => e.affectsSome(keys));
onDidChangeContext(updateEnablement, null, disposables);
}
}
}
}
......
......@@ -775,6 +775,10 @@ abstract class SingleSideDiffElement extends AbstractElementRenderer {
this.templateData.outputHeaderContainer.style.display = 'none';
this.templateData.outputInfoContainer.style.display = 'none';
return;
} else {
this.templateData.outputHeaderContainer.style.display = 'flex';
this.templateData.outputInfoContainer.style.display = 'block';
}
this._outputHeaderContainer = this.templateData.outputHeaderContainer;
......@@ -1191,6 +1195,9 @@ export class ModifiedElement extends AbstractElementRenderer {
this.templateData.outputHeaderContainer.style.display = 'none';
this.templateData.outputInfoContainer.style.display = 'none';
return;
} else {
this.templateData.outputHeaderContainer.style.display = 'flex';
this.templateData.outputInfoContainer.style.display = 'block';
}
this._outputHeaderContainer = this.templateData.outputHeaderContainer;
......
......@@ -98,6 +98,10 @@
cursor: pointer;
}
.monaco-editor .testing-inline-message {
.monaco-editor .testing-inline-message-content {
cursor: pointer;
}
.monaco-editor .testing-inline-message-line {
background: red;
}
......@@ -131,16 +131,12 @@ interface ITestDecoration extends IDisposable {
const hasValidLocation = <T extends { location?: ModeLocation }>(editorUri: URI, t: T): t is T & { location: ModeLocation } =>
t.location?.uri.toString() === editorUri.toString();
const firstLineRange = (editor: ICodeEditor, originalRange: IRange) => {
const model = editor.getModel();
const endColumn = model?.getLineMaxColumn(originalRange.startLineNumber);
return {
startLineNumber: originalRange.startLineNumber,
endLineNumber: originalRange.startLineNumber,
startColumn: 0,
endColumn: endColumn || 1,
};
};
const firstLineRange = (originalRange: IRange) => ({
startLineNumber: originalRange.startLineNumber,
endLineNumber: originalRange.startLineNumber,
startColumn: 0,
endColumn: 1,
});
class RunTestDecoration implements ITestDecoration {
/**
......@@ -182,7 +178,7 @@ class RunTestDecoration implements ITestDecoration {
: test.children.size > 0 ? testingRunAllIcon : testingRunIcon;
this.editorDecoration = {
range: firstLineRange(this.editor, this.location.range),
range: firstLineRange(this.location.range),
options: {
isWholeLine: true,
glyphMarginClassName: ThemeIcon.asClassName(icon) + ' testing-run-glyph',
......@@ -286,14 +282,16 @@ class TestMessageDecoration implements ITestDecoration {
color: `${colorTheme.getColor(testMessageSeverityColors[severity].decorationForeground)}`,
fontSize: `${editor.getOption(EditorOption.fontSize)}px`,
fontFamily: editor.getOption(EditorOption.fontFamily),
padding: `0px 12px`,
padding: `0px 12px 0px 24px`,
},
}, undefined, editor);
const options = editorService.resolveDecorationOptions(this.decorationId, true);
options.hoverMessage = typeof message === 'string' ? new MarkdownString().appendText(message) : message;
options.afterContentClassName = `${options.afterContentClassName} testing-inline-message`;
options.afterContentClassName = `${options.afterContentClassName} testing-inline-message-content`;
options.zIndex = 10; // todo: in spite of the z-index, this appears behind gitlens
options.className = `testing-inline-message-margin testing-inline-message-severity-${severity}`;
options.isWholeLine = true;
const rulerColor = severity === TestMessageSeverity.Error
? overviewRulerError
......@@ -307,7 +305,7 @@ class TestMessageDecoration implements ITestDecoration {
options.overviewRuler = { color: themeColorFromId(rulerColor), position: OverviewRulerLane.Right };
}
this.editorDecoration = { range: firstLineRange(editor, location.range), options };
this.editorDecoration = { range: firstLineRange(location.range), options };
}
click(e: IEditorMouseEvent): boolean {
......
......@@ -5,7 +5,7 @@
import * as dom from 'vs/base/browser/dom';
import { Color } from 'vs/base/common/color';
import { IReference, MutableDisposable } from 'vs/base/common/lifecycle';
import { Disposable, IReference, MutableDisposable } from 'vs/base/common/lifecycle';
import { clamp } from 'vs/base/common/numbers';
import { count } from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
......@@ -35,7 +35,7 @@ interface ITestDto {
messageUri: URI;
}
export class TestingOutputPeekController implements IEditorContribution {
export class TestingOutputPeekController extends Disposable implements IEditorContribution {
/**
* Gets the controller associated with the given code editor.
*/
......@@ -46,7 +46,7 @@ export class TestingOutputPeekController implements IEditorContribution {
/**
* Currently-shown peek view.
*/
private readonly peek = new MutableDisposable<TestingOutputPeek>();
private readonly peek = this._register(new MutableDisposable<TestingOutputPeek>());
/**
* Context key updated when the peek is visible/hidden.
......@@ -60,14 +60,9 @@ export class TestingOutputPeekController implements IEditorContribution {
@ITestService private readonly testService: ITestService,
@IContextKeyService contextKeyService: IContextKeyService,
) {
super();
this.visible = TestingContextKeys.peekVisible.bindTo(contextKeyService);
}
/**
* @inheritdoc
*/
public dispose(): void {
this.removePeek();
this._register(editor.onDidChangeModel(() => this.peek.clear()));
}
/**
......@@ -107,7 +102,7 @@ export class TestingOutputPeekController implements IEditorContribution {
* Disposes the peek view, if any.
*/
public removePeek() {
this.peek.value = undefined;
this.peek.clear();
}
private async retrieveTest(uri: URI): Promise<ITestDto | undefined> {
......
......@@ -3,8 +3,10 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Color, RGBA } from 'vs/base/common/color';
import { localize } from 'vs/nls';
import { editorErrorForeground, editorForeground, editorHintForeground, editorInfoForeground, editorWarningForeground, registerColor } from 'vs/platform/theme/common/colorRegistry';
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { TestMessageSeverity, TestRunState } from 'vs/workbench/api/common/extHostTypes';
export const testingColorIconFailed = registerColor('testing.iconFailed', {
......@@ -58,6 +60,7 @@ export const testingPeekBorder = registerColor('testing.peekBorder', {
export const testMessageSeverityColors: {
[K in TestMessageSeverity]: {
decorationForeground: string,
marginBackground: string,
};
} = {
[TestMessageSeverity.Error]: {
......@@ -66,6 +69,11 @@ export const testMessageSeverityColors: {
{ dark: editorErrorForeground, light: editorErrorForeground, hc: editorForeground },
localize('testing.message.error.decorationForeground', 'Text color of test error messages shown inline in the editor.')
),
marginBackground: registerColor(
'testing.message.error.lineBackground',
{ dark: new Color(new RGBA(255, 0, 0, 0.2)), light: new Color(new RGBA(255, 0, 0, 0.2)), hc: null },
localize('testing.message.error.marginBackground', 'Margin color beside error messages shown inline in the editor.')
),
},
[TestMessageSeverity.Warning]: {
decorationForeground: registerColor(
......@@ -73,6 +81,11 @@ export const testMessageSeverityColors: {
{ dark: editorWarningForeground, light: editorWarningForeground, hc: editorForeground },
localize('testing.message.warning.decorationForeground', 'Text color of test warning messages shown inline in the editor.')
),
marginBackground: registerColor(
'testing.message.warning.lineBackground',
{ dark: new Color(new RGBA(255, 208, 0, 0.2)), light: new Color(new RGBA(255, 208, 0, 0.2)), hc: null },
localize('testing.message.warning.marginBackground', 'Margin color beside warning messages shown inline in the editor.')
),
},
[TestMessageSeverity.Information]: {
decorationForeground: registerColor(
......@@ -80,6 +93,11 @@ export const testMessageSeverityColors: {
{ dark: editorInfoForeground, light: editorInfoForeground, hc: editorForeground },
localize('testing.message.info.decorationForeground', 'Text color of test info messages shown inline in the editor.')
),
marginBackground: registerColor(
'testing.message.info.lineBackground',
{ dark: new Color(new RGBA(0, 127, 255, 0.2)), light: new Color(new RGBA(0, 127, 255, 0.2)), hc: null },
localize('testing.message.info.marginBackground', 'Margin color beside info messages shown inline in the editor.')
),
},
[TestMessageSeverity.Hint]: {
decorationForeground: registerColor(
......@@ -87,6 +105,11 @@ export const testMessageSeverityColors: {
{ dark: editorHintForeground, light: editorHintForeground, hc: editorForeground },
localize('testing.message.hint.decorationForeground', 'Text color of test hint messages shown inline in the editor.')
),
marginBackground: registerColor(
'testing.message.hint.lineBackground',
{ dark: null, light: null, hc: editorForeground },
localize('testing.message.hint.marginBackground', 'Margin color beside hint messages shown inline in the editor.')
),
},
};
......@@ -98,3 +121,12 @@ export const testStatesToIconColors: { [K in TestRunState]?: string } = {
[TestRunState.Unset]: testingColorIconUnset,
[TestRunState.Skipped]: testingColorIconUnset,
};
registerThemingParticipant((theme, collector) => {
for (const [state, { marginBackground }] of Object.entries(testMessageSeverityColors)) {
collector.addRule(`.monaco-editor .testing-inline-message-severity-${state} {
background: ${theme.getColor(marginBackground)};
}`);
}
});
......@@ -126,12 +126,13 @@ export class SingleUseTestCollection implements IDisposable {
private addItem(actual: ApiTestItem, providerId: string, parent: string | null) {
let internal = this.testItemToInternal.get(actual);
const parentItem = parent ? this.testIdToInternal.get(parent) : null;
if (!internal) {
internal = {
actual,
id: this.getId(),
parent,
item: TestItem.from(actual),
item: TestItem.from(actual, parentItem?.item.extId),
providerId,
previousChildren: new Set(),
previousEquals: itemEqualityComparator(actual),
......@@ -141,7 +142,7 @@ export class SingleUseTestCollection implements IDisposable {
this.testIdToInternal.set(internal.id, internal);
this.diff.push([TestDiffOpType.Add, { id: internal.id, parent, providerId, item: internal.item }]);
} else if (!internal.previousEquals(actual)) {
internal.item = TestItem.from(actual);
internal.item = TestItem.from(actual, parentItem?.item.extId);
internal.previousEquals = itemEqualityComparator(actual);
this.diff.push([TestDiffOpType.Update, { id: internal.id, parent, providerId, item: internal.item }]);
}
......@@ -200,6 +201,7 @@ export class SingleUseTestCollection implements IDisposable {
}
const keyMap: { [K in keyof Omit<RequiredTestItem, 'children'>]: null } = {
id: null,
label: null,
location: null,
state: null,
......
......@@ -62,6 +62,8 @@ export interface ITestState {
* The TestItem from .d.ts, as a plain object without children.
*/
export interface ITestItem {
/** ID of the test given by the test provider */
extId: string;
label: string;
children?: never;
location: ModeLocation | undefined;
......@@ -81,6 +83,14 @@ export interface InternalTestItem {
item: ITestItem;
}
export interface InternalTestItemWithChildren extends InternalTestItem {
children: InternalTestItemWithChildren[];
}
export interface InternalTestResults {
tests: InternalTestItemWithChildren[];
}
export const enum TestDiffOpType {
/** Adds a new test (with children) */
Add,
......
......@@ -8,7 +8,7 @@ import { generateUuid } from 'vs/base/common/uuid';
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 { IncrementalTestCollectionItem, InternalTestItemWithChildren, 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';
......@@ -50,9 +50,7 @@ const makeNode = (
return mapped;
};
export interface TestResultItem extends InternalTestItem {
children: TestResultItem[]
}
export interface TestResultItem extends InternalTestItemWithChildren { }
/**
* Results of a test. These are created when the test initially started running
......
......@@ -68,7 +68,7 @@ const viewsWelcomeExtensionPointSchema = Object.freeze<IConfigurationPropertySch
},
[ViewsWelcomeExtensionPointFields.enablement]: {
type: 'string',
description: nls.localize('contributes.viewsWelcome.view.enablement', "Condition when the welcome content buttons should be enabled."),
description: nls.localize('contributes.viewsWelcome.view.enablement', "Condition when the welcome content buttons and command links should be enabled."),
},
}
}
......
......@@ -17,6 +17,7 @@ import { Range } from 'vs/editor/common/core/range';
const simplify = (item: TestItem) => {
if ('toJSON' in item) {
item = (item as any).toJSON();
delete (item as any).id;
delete (item as any).providerId;
delete (item as any).testId;
}
......@@ -70,10 +71,10 @@ suite('ExtHost Testing', () => {
single.addRoot(tests, 'pid');
assert.deepStrictEqual(single.collectDiff(), [
[TestDiffOpType.Add, { id: '0', providerId: 'pid', parent: null, item: convert.TestItem.from(stubTest('root')) }],
[TestDiffOpType.Add, { id: '1', providerId: 'pid', parent: '0', item: convert.TestItem.from(stubTest('a')) }],
[TestDiffOpType.Add, { id: '2', providerId: 'pid', parent: '1', item: convert.TestItem.from(stubTest('aa')) }],
[TestDiffOpType.Add, { id: '3', providerId: 'pid', parent: '1', item: convert.TestItem.from(stubTest('ab')) }],
[TestDiffOpType.Add, { id: '4', providerId: 'pid', parent: '0', item: convert.TestItem.from(stubTest('b')) }],
[TestDiffOpType.Add, { id: '1', providerId: 'pid', parent: '0', item: convert.TestItem.from(stubTest('a'), 'root') }],
[TestDiffOpType.Add, { id: '2', providerId: 'pid', parent: '1', item: convert.TestItem.from(stubTest('aa'), 'root\0a') }],
[TestDiffOpType.Add, { id: '3', providerId: 'pid', parent: '1', item: convert.TestItem.from(stubTest('ab'), 'root\0a') }],
[TestDiffOpType.Add, { id: '4', providerId: 'pid', parent: '0', item: convert.TestItem.from(stubTest('b'), 'root') }],
]);
});
......@@ -91,7 +92,7 @@ suite('ExtHost Testing', () => {
tests.children![0].description = 'Hello world'; /* item a */
single.onItemChange(tests, 'pid');
assert.deepStrictEqual(single.collectDiff(), [
[TestDiffOpType.Update, { id: '1', parent: '0', providerId: 'pid', item: convert.TestItem.from({ ...stubTest('a'), description: 'Hello world' }) }],
[TestDiffOpType.Update, { id: '1', parent: '0', providerId: 'pid', item: convert.TestItem.from({ ...stubTest('a'), description: 'Hello world' }, 'root') }],
]);
single.onItemChange(tests, 'pid');
......@@ -121,7 +122,7 @@ suite('ExtHost Testing', () => {
single.onItemChange(tests, 'pid');
assert.deepStrictEqual(single.collectDiff(), [
[TestDiffOpType.Add, { id: '5', providerId: 'pid', parent: '1', item: convert.TestItem.from(child) }],
[TestDiffOpType.Add, { id: '5', providerId: 'pid', parent: '1', item: convert.TestItem.from(child, 'root\0a') }],
]);
assert.deepStrictEqual([...owned.idToInternal.keys()].sort(), ['0', '1', '2', '3', '4', '5']);
assert.strictEqual(single.itemToInternal.size, 6);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册