未验证 提交 f3b2680e 编写于 作者: A Alexandru Dima 提交者: GitHub

Merge branch 'master' into alex/terminal-exthost-improvements

......@@ -60,6 +60,7 @@ const compilations = [
'python/tsconfig.json',
'search-result/tsconfig.json',
'simple-browser/tsconfig.json',
'testing-editor-contributions/tsconfig.json',
'typescript-language-features/test-workspace/tsconfig.json',
'typescript-language-features/tsconfig.json',
'vscode-api-tests/tsconfig.json',
......
......@@ -35,6 +35,7 @@ exports.dirs = [
'extensions/php-language-features',
'extensions/search-result',
'extensions/simple-browser',
'extensions/testing-editor-contributions',
'extensions/typescript-language-features',
'extensions/vscode-api-tests',
'extensions/vscode-colorize-tests',
......
......@@ -432,7 +432,11 @@ export class Git {
if (options.recursive) {
command.push('--recursive');
}
await this.exec(options.parentPath, command, { cancellationToken, onSpawn });
await this.exec(options.parentPath, command, {
cancellationToken,
env: { 'GIT_HTTP_USER_AGENT': this.userAgent },
onSpawn,
});
} catch (err) {
if (err.stderr) {
err.stderr = err.stderr.replace(/^Cloning.+$/m, '').trim();
......@@ -1568,6 +1572,7 @@ export class Repository {
const args = ['fetch'];
const spawnOptions: SpawnOptions = {
cancellationToken: options.cancellationToken,
env: { 'GIT_HTTP_USER_AGENT': this.git.userAgent }
};
if (options.remote) {
......@@ -1589,7 +1594,7 @@ export class Repository {
}
if (options.silent) {
spawnOptions.env = { 'VSCODE_GIT_FETCH_SILENT': 'true' };
spawnOptions.env!['VSCODE_GIT_FETCH_SILENT'] = 'true';
}
try {
......@@ -1626,7 +1631,10 @@ export class Repository {
}
try {
await this.run(args, options);
await this.run(args, {
cancellationToken: options.cancellationToken,
env: { 'GIT_HTTP_USER_AGENT': this.git.userAgent }
});
} catch (err) {
if (/^CONFLICT \([^)]+\): \b/m.test(err.stdout || '')) {
err.gitErrorCode = GitErrorCodes.Conflict;
......@@ -1694,10 +1702,8 @@ export class Repository {
args.push(name);
}
args.splice(0, 0, '-c', `http.userAgent=${this.git.userAgent}`);
try {
await this.run(args);
await this.run(args, { env: { 'GIT_HTTP_USER_AGENT': this.git.userAgent } });
} catch (err) {
if (/^error: failed to push some refs to\b/m.test(err.stderr || '')) {
err.gitErrorCode = GitErrorCodes.PushRejected;
......
......@@ -155,6 +155,11 @@ window.addEventListener('message', e => {
iframe.focus();
break;
}
case 'didChangeFocusLockIndicatorEnabled':
{
toggleFocusLockIndicatorEnabled(e.data.enabled);
break;
}
}
});
events_1.onceDocumentLoaded(() => {
......@@ -189,10 +194,14 @@ events_1.onceDocumentLoaded(() => {
iframe.src = input.value;
});
navigateTo(settings.url);
toggleFocusLockIndicatorEnabled(settings.focusLockIndicatorEnabled);
function navigateTo(url) {
iframe.src = url;
}
});
function toggleFocusLockIndicatorEnabled(enabled) {
document.body.classList.toggle('enable-focus-lock-indicator', enabled);
}
/***/ })
......
{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./preview-src/events.ts","webpack:///./preview-src/index.ts"],"names":[],"mappings":";QAAA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;;QAEA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;;;QAGA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;QACA,0CAA0C,gCAAgC;QAC1E;QACA;;QAEA;QACA;QACA;QACA,wDAAwD,kBAAkB;QAC1E;QACA,iDAAiD,cAAc;QAC/D;;QAEA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA,yCAAyC,iCAAiC;QAC1E,gHAAgH,mBAAmB,EAAE;QACrI;QACA;;QAEA;QACA;QACA;QACA,2BAA2B,0BAA0B,EAAE;QACvD,iCAAiC,eAAe;QAChD;QACA;QACA;;QAEA;QACA,sDAAsD,+DAA+D;;QAErH;QACA;;;QAGA;QACA;;;;;;;;;;;;;AClFa;AACb;AACA;AACA;AACA;AACA,8CAA8C,cAAc;AAC5D;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;ACfa;AACb;AACA;AACA;AACA;AACA,8CAA8C,cAAc;AAC5D,iBAAiB,mBAAO,CAAC,yCAAU;AACnC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC;AACD;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA,KAAK;AACL;AACA;AACA;AACA,KAAK;AACL;AACA;AACA,KAAK;AACL;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA,SAAS;AACT,KAAK;AACL;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA,CAAC","file":"index.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = \"./preview-src/index.ts\");\n","\"use strict\";\n/*---------------------------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.onceDocumentLoaded = void 0;\nfunction onceDocumentLoaded(f) {\n if (document.readyState === 'loading' || document.readyState === 'uninitialized') {\n document.addEventListener('DOMContentLoaded', f);\n }\n else {\n f();\n }\n}\nexports.onceDocumentLoaded = onceDocumentLoaded;\n","\"use strict\";\n/*---------------------------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\nObject.defineProperty(exports, \"__esModule\", { value: true });\nconst events_1 = require(\"./events\");\nconst vscode = acquireVsCodeApi();\nfunction getSettings() {\n const element = document.getElementById('simple-browser-settings');\n if (element) {\n const data = element.getAttribute('data-settings');\n if (data) {\n return JSON.parse(data);\n }\n }\n throw new Error(`Could not load settings`);\n}\nconst settings = getSettings();\nconst iframe = document.querySelector('iframe');\nconst header = document.querySelector('.header');\nconst input = header.querySelector('.url-input');\nconst forwardButton = header.querySelector('.forward-button');\nconst backButton = header.querySelector('.back-button');\nconst reloadButton = header.querySelector('.reload-button');\nconst openExternalButton = header.querySelector('.open-external-button');\nwindow.addEventListener('message', e => {\n switch (e.data.type) {\n case 'focus':\n {\n iframe.focus();\n break;\n }\n }\n});\nevents_1.onceDocumentLoaded(() => {\n setInterval(() => {\n var _a;\n const iframeFocused = ((_a = document.activeElement) === null || _a === void 0 ? void 0 : _a.tagName) === 'IFRAME';\n document.body.classList.toggle('iframe-focused', iframeFocused);\n }, 50);\n iframe.addEventListener('load', () => {\n // Noop\n });\n input.addEventListener('change', e => {\n const url = e.target.value;\n navigateTo(url);\n });\n forwardButton.addEventListener('click', () => {\n history.forward();\n });\n backButton.addEventListener('click', () => {\n history.back();\n });\n openExternalButton.addEventListener('click', () => {\n vscode.postMessage({\n type: 'openExternal',\n url: input.value\n });\n });\n reloadButton.addEventListener('click', () => {\n // This does not seem to trigger what we want\n // history.go(0);\n // This incorrectly adds entries to the history but does reload\n iframe.src = input.value;\n });\n navigateTo(settings.url);\n function navigateTo(url) {\n iframe.src = url;\n }\n});\n"],"sourceRoot":""}
\ No newline at end of file
{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./preview-src/events.ts","webpack:///./preview-src/index.ts"],"names":[],"mappings":";QAAA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;;QAEA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;;;QAGA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;QACA,0CAA0C,gCAAgC;QAC1E;QACA;;QAEA;QACA;QACA;QACA,wDAAwD,kBAAkB;QAC1E;QACA,iDAAiD,cAAc;QAC/D;;QAEA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA,yCAAyC,iCAAiC;QAC1E,gHAAgH,mBAAmB,EAAE;QACrI;QACA;;QAEA;QACA;QACA;QACA,2BAA2B,0BAA0B,EAAE;QACvD,iCAAiC,eAAe;QAChD;QACA;QACA;;QAEA;QACA,sDAAsD,+DAA+D;;QAErH;QACA;;;QAGA;QACA;;;;;;;;;;;;;AClFa;AACb;AACA;AACA;AACA;AACA,8CAA8C,cAAc;AAC5D;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;ACfa;AACb;AACA;AACA;AACA;AACA,8CAA8C,cAAc;AAC5D,iBAAiB,mBAAO,CAAC,yCAAU;AACnC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC;AACD;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA,KAAK;AACL;AACA;AACA;AACA,KAAK;AACL;AACA;AACA,KAAK;AACL;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA,SAAS;AACT,KAAK;AACL;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA,CAAC;AACD;AACA;AACA","file":"index.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = \"./preview-src/index.ts\");\n","\"use strict\";\n/*---------------------------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.onceDocumentLoaded = void 0;\nfunction onceDocumentLoaded(f) {\n if (document.readyState === 'loading' || document.readyState === 'uninitialized') {\n document.addEventListener('DOMContentLoaded', f);\n }\n else {\n f();\n }\n}\nexports.onceDocumentLoaded = onceDocumentLoaded;\n","\"use strict\";\n/*---------------------------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\nObject.defineProperty(exports, \"__esModule\", { value: true });\nconst events_1 = require(\"./events\");\nconst vscode = acquireVsCodeApi();\nfunction getSettings() {\n const element = document.getElementById('simple-browser-settings');\n if (element) {\n const data = element.getAttribute('data-settings');\n if (data) {\n return JSON.parse(data);\n }\n }\n throw new Error(`Could not load settings`);\n}\nconst settings = getSettings();\nconst iframe = document.querySelector('iframe');\nconst header = document.querySelector('.header');\nconst input = header.querySelector('.url-input');\nconst forwardButton = header.querySelector('.forward-button');\nconst backButton = header.querySelector('.back-button');\nconst reloadButton = header.querySelector('.reload-button');\nconst openExternalButton = header.querySelector('.open-external-button');\nwindow.addEventListener('message', e => {\n switch (e.data.type) {\n case 'focus':\n {\n iframe.focus();\n break;\n }\n case 'didChangeFocusLockIndicatorEnabled':\n {\n toggleFocusLockIndicatorEnabled(e.data.enabled);\n break;\n }\n }\n});\nevents_1.onceDocumentLoaded(() => {\n setInterval(() => {\n var _a;\n const iframeFocused = ((_a = document.activeElement) === null || _a === void 0 ? void 0 : _a.tagName) === 'IFRAME';\n document.body.classList.toggle('iframe-focused', iframeFocused);\n }, 50);\n iframe.addEventListener('load', () => {\n // Noop\n });\n input.addEventListener('change', e => {\n const url = e.target.value;\n navigateTo(url);\n });\n forwardButton.addEventListener('click', () => {\n history.forward();\n });\n backButton.addEventListener('click', () => {\n history.back();\n });\n openExternalButton.addEventListener('click', () => {\n vscode.postMessage({\n type: 'openExternal',\n url: input.value\n });\n });\n reloadButton.addEventListener('click', () => {\n // This does not seem to trigger what we want\n // history.go(0);\n // This incorrectly adds entries to the history but does reload\n iframe.src = input.value;\n });\n navigateTo(settings.url);\n toggleFocusLockIndicatorEnabled(settings.focusLockIndicatorEnabled);\n function navigateTo(url) {\n iframe.src = url;\n }\n});\nfunction toggleFocusLockIndicatorEnabled(enabled) {\n document.body.classList.toggle('enable-focus-lock-indicator', enabled);\n}\n"],"sourceRoot":""}
\ No newline at end of file
......@@ -110,6 +110,6 @@ iframe {
pointer-events: none;
}
.iframe-focused .iframe-focused-alert {
.iframe-focused.enable-focus-lock-indicator .iframe-focused-alert {
display: block;
}
......@@ -9,12 +9,12 @@
"license": "MIT",
"aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217",
"engines": {
"vscode": "^1.50.0"
"vscode": "^1.53.0"
},
"main": "./out/extension",
"browser": "./dist/browser/extension",
"categories": [
"Programming Languages"
"Other"
],
"activationEvents": [
"onCommand:simpleBrowser.show",
......@@ -33,11 +33,26 @@
{
"title": "Simple Browser",
"properties": {
"simpleBrowser.focusLockIndicator.enabled": {
"type": "boolean",
"default": true,
"title": "Focus Lock Indicator Enabled",
"description": "%configuration.focusLockIndicator.enabled.description%"
},
"simpleBrowser.opener.enabled": {
"type": "boolean",
"default": false,
"title": "Opener Enabled",
"description": "(Experimental) Enables opening http and https urls using the built-in seper browser"
"description": "%configuration.opener.enabled.description%"
},
"simpleBrowser.opener.enabledHosts": {
"type": "array",
"title": "Opener Enabled Hosts",
"markdownDescription": "%configuration.opener.enabledHosts.description%",
"default": [
"localhost",
"127.0.0.1"
]
}
}
}
......
{
"displayName": "Markdown Language Features",
"description": "Provides rich language support for Markdown.",
"markdown.preview.breaks.desc": "Sets how line-breaks are rendered in the markdown preview. Setting it to 'true' creates a <br> for newlines inside paragraphs.",
"markdown.preview.linkify": "Enable or disable conversion of URL-like text to links in the markdown preview.",
"markdown.preview.doubleClickToSwitchToEditor.desc": "Double click in the markdown preview to switch to the editor.",
"markdown.preview.fontFamily.desc": "Controls the font family used in the markdown preview.",
"markdown.preview.fontSize.desc": "Controls the font size in pixels used in the markdown preview.",
"markdown.preview.lineHeight.desc": "Controls the line height used in the markdown preview. This number is relative to the font size.",
"markdown.preview.markEditorSelection.desc": "Mark the current editor selection in the markdown preview.",
"markdown.preview.scrollEditorWithPreview.desc": "When a markdown preview is scrolled, update the view of the editor.",
"markdown.preview.scrollPreviewWithEditor.desc": "When a markdown editor is scrolled, update the view of the preview.",
"markdown.preview.title": "Open Preview",
"markdown.previewSide.title": "Open Preview to the Side",
"markdown.showLockedPreviewToSide.title": "Open Locked Preview to the Side",
"markdown.showSource.title": "Show Source",
"markdown.styles.dec": "A list of URLs or local paths to CSS style sheets to use from the markdown preview. Relative paths are interpreted relative to the folder open in the explorer. If there is no open folder, they are interpreted relative to the location of the markdown file. All '\\' need to be written as '\\\\'.",
"markdown.showPreviewSecuritySelector.title": "Change Preview Security Settings",
"markdown.trace.desc": "Enable debug logging for the markdown extension.",
"markdown.preview.refresh.title": "Refresh Preview",
"markdown.preview.toggleLock.title": "Toggle Preview Locking",
"configuration.markdown.preview.openMarkdownLinks.description": "Controls how links to other markdown files in the markdown preview should be opened.",
"configuration.markdown.preview.openMarkdownLinks.inEditor": "Try to open links in the editor",
"configuration.markdown.preview.openMarkdownLinks.inPreview": "Try to open links in the markdown preview",
"configuration.markdown.links.openLocation.description": "Controls where links in markdown files should be opened.",
"configuration.markdown.links.openLocation.currentGroup": "Open links in the active editor group.",
"configuration.markdown.links.openLocation.beside": "Open links beside the active editor."
"displayName": "Simple Browser",
"description": "A very basic built-in webview for displaying web content.",
"configuration.focusLockIndicator.enabled.description": "Enable/disable ",
"configuration.opener.enabled.description": "(Experimental) Enables using the built-in simple browser when opening http and https urls.",
"configuration.opener.enabledHosts.description": "Lists of hosts to open in the simple browser. For example: `localhost`."
}
......@@ -37,6 +37,11 @@ window.addEventListener('message', e => {
iframe.focus();
break;
}
case 'didChangeFocusLockIndicatorEnabled':
{
toggleFocusLockIndicatorEnabled(e.data.enabled);
break;
}
}
});
......@@ -79,8 +84,14 @@ onceDocumentLoaded(() => {
});
navigateTo(settings.url);
toggleFocusLockIndicatorEnabled(settings.focusLockIndicatorEnabled);
function navigateTo(url: string): void {
iframe.src = url;
}
});
function toggleFocusLockIndicatorEnabled(enabled: boolean) {
document.body.classList.toggle('enable-focus-lock-indicator', enabled);
}
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
export function disposeAll(disposables: vscode.Disposable[]) {
while (disposables.length) {
const item = disposables.pop();
if (item) {
item.dispose();
}
}
}
export abstract class Disposable {
private _isDisposed = false;
protected _disposables: vscode.Disposable[] = [];
public dispose(): any {
if (this._isDisposed) {
return;
}
this._isDisposed = true;
disposeAll(this._disposables);
}
protected _register<T extends vscode.Disposable>(value: T): T {
if (this._isDisposed) {
value.dispose();
} else {
this._disposables.push(value);
}
return value;
}
protected get isDisposed() {
return this._isDisposed;
}
}
......@@ -3,13 +3,14 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { URL } from 'url';
import * as vscode from 'vscode';
import { SimpleBrowserManager } from './simpleBrowserManager';
import * as nls from 'vscode-nls';
import { SimpleBrowserManager } from './simpleBrowserManager';
const localize = nls.loadMessageBundle();
const openCommand = 'simpleBrowser.open';
const openApiCommand = 'simpleBrowser.api.open';
const showCommand = 'simpleBrowser.show';
export function activate(context: vscode.ExtensionContext) {
......@@ -30,8 +31,8 @@ export function activate(context: vscode.ExtensionContext) {
}
}));
context.subscriptions.push(vscode.commands.registerCommand(openCommand, (url: vscode.Uri) => {
manager.show(url.toString());
context.subscriptions.push(vscode.commands.registerCommand(openApiCommand, (url: vscode.Uri, showOptions?: { preserveFocus?: boolean }) => {
manager.show(url.toString(), showOptions);
}));
context.subscriptions.push(vscode.window.registerExternalUriOpener(['http', 'https'], {
......@@ -41,9 +42,22 @@ export function activate(context: vscode.ExtensionContext) {
return undefined;
}
const enabledHosts = configuration.get<string[]>('opener.enabledHosts', [
'localhost',
'127.0.0.1'
]);
try {
const url = new URL(uri.toString());
if (!enabledHosts.includes(url.hostname)) {
return;
}
} catch {
return undefined;
}
return {
title: localize('openTitle', "Open in simple browser"),
command: openCommand,
command: openApiCommand,
arguments: [uri]
};
}
......
......@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import { SimpleBrowserView } from './simpleBrowserView';
import { ShowOptions, SimpleBrowserView } from './simpleBrowserView';
export class SimpleBrowserManager {
......@@ -19,11 +19,11 @@ export class SimpleBrowserManager {
this._activeView = undefined;
}
public show(url: string): void {
public show(url: string, options?: ShowOptions): void {
if (this._activeView) {
this._activeView.show(url);
this._activeView.show(url, options);
} else {
const view = new SimpleBrowserView(this.extensionUri, url);
const view = new SimpleBrowserView(this.extensionUri, url, options);
view.onDispose(() => {
if (this._activeView === view) {
this._activeView = undefined;
......
......@@ -5,31 +5,40 @@
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
import { Disposable } from './dispose';
const localize = nls.loadMessageBundle();
export class SimpleBrowserView {
export interface ShowOptions {
readonly preserveFocus?: boolean;
}
export class SimpleBrowserView extends Disposable {
public static readonly viewType = 'simpleBrowser.view';
private static readonly title = localize('view.title', "Simple Browser");
private readonly _webviewPanel: vscode.WebviewPanel;
private readonly _onDidDispose = new vscode.EventEmitter<void>();
private readonly _onDidDispose = this._register(new vscode.EventEmitter<void>());
public readonly onDispose = this._onDidDispose.event;
constructor(
private readonly extensionUri: vscode.Uri,
url: string,
showOptions?: ShowOptions
) {
this._webviewPanel = vscode.window.createWebviewPanel(SimpleBrowserView.viewType, SimpleBrowserView.title, {
super();
this._webviewPanel = this._register(vscode.window.createWebviewPanel(SimpleBrowserView.viewType, SimpleBrowserView.title, {
viewColumn: vscode.ViewColumn.Active,
preserveFocus: showOptions?.preserveFocus
}, {
enableScripts: true,
retainContextWhenHidden: true,
});
}));
this._webviewPanel.webview.onDidReceiveMessage(e => {
this._register(this._webviewPanel.webview.onDidReceiveMessage(e => {
switch (e.type) {
case 'openExternal':
try {
......@@ -40,26 +49,38 @@ export class SimpleBrowserView {
}
break;
}
});
}));
this._webviewPanel.onDidDispose(() => {
this._register(this._webviewPanel.onDidDispose(() => {
this.dispose();
});
}));
this._register(vscode.workspace.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('simpleBrowser.focusLockIndicator.enabled')) {
const configuration = vscode.workspace.getConfiguration('simpleBrowser');
this._webviewPanel.webview.postMessage({
type: 'didChangeFocusLockIndicatorEnabled',
focusLockEnabled: configuration.get<boolean>('focusLockIndicator.enabled', true)
});
}
}));
this.show(url);
}
public dispose() {
this._onDidDispose.fire();
this._webviewPanel.dispose();
super.dispose();
}
public show(url: string) {
public show(url: string, options?: ShowOptions) {
this._webviewPanel.webview.html = this.getHtml(url);
this._webviewPanel.reveal();
this._webviewPanel.reveal(undefined, options?.preserveFocus);
}
private getHtml(url: string) {
const configuration = vscode.workspace.getConfiguration('simpleBrowser');
const nonce = new Date().getTime() + '' + new Date().getMilliseconds();
const mainJs = this.extensionResourceUrl('media', 'index.js');
......@@ -82,6 +103,7 @@ export class SimpleBrowserView {
<meta id="simple-browser-settings" data-settings="${escapeAttribute(JSON.stringify({
url: url,
focusLockEnabled: configuration.get<boolean>('focusLockIndicator.enabled', true)
}))}">
<link rel="stylesheet" type="text/css" href="${mainCss}">
......
src/**
out/**
tsconfig.json
extension.webpack.config.js
extension-browser.webpack.config.js
yarn.lock
# Testing Editor Contributions
**Notice:** This extension is bundled with Visual Studio Code. It can be disabled but not uninstalled.
Provides the in-editor experience for tests and test results
/*---------------------------------------------------------------------------------------------
* 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;
const path = require('path');
module.exports = withBrowserDefaults({
context: __dirname,
entry: {
extension: './src/extension.ts'
},
output: {
filename: 'extension.js',
path: path.join(__dirname, 'dist')
}
});
/*---------------------------------------------------------------------------------------------
* 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 withDefaults = require('../shared.webpack.config');
module.exports = withDefaults({
context: __dirname,
resolve: {
mainFields: ['module', 'main']
},
entry: {
extension: './src/extension.ts',
}
});
{
"name": "testing-editor-contributions",
"displayName": "%displayName%",
"description": "%description%",
"version": "1.0.0",
"enableProposedApi": true,
"publisher": "vscode",
"license": "MIT",
"engines": {
"vscode": "^1.39.0"
},
"categories": [
"Other"
],
"main": "./out/extension.js",
"browser": "./dist/extension.js",
"activationEvents": [
"onStartupFinished"
],
"dependencies": {
"vscode-nls": "^5.0.0"
},
"contributes": {
"configuration": {
"title": "Testing",
"properties": {
"testing.enableCodeLens": {
"description": "%config.enableCodeLens%",
"type": "boolean",
"default": true
},
"testing.enableProblemDiagnostics": {
"description": "%config.enableProblemDiagnostics%",
"type": "boolean",
"default": true
}
}
}
},
"scripts": {
"vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:testing-editor-contributions ./tsconfig.json"
},
"prettier": {
"printWidth": 100,
"singleQuote": true,
"trailingComma": "all"
}
}
{
"displayName": "Testing Editor Contributions",
"description": "Provides the in-editor experience for tests and test results.",
"action.run": "Run Tests",
"action.debug": "Debug",
"tooltip.run": "Run {0}",
"tooltip.debug": "Debug {0}",
"tooltip.runState": "{0}/{0} Tests Passed",
"tooltip.runStateWithDuration": "{0}/{1} Tests Passed in {2}",
"state.failed": "Failed",
"state.passed": "Passed",
"state.passedWithDuration": "Passed in {0}",
"config.enableCodeLens": "Whether code lens on test cases and suites should be visible",
"config.enableProblemDiagnostics": "Whether test failures should be reported in the 'problems' view and show as errors in the editor."
}
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle();
interface IDisposable {
dispose(): void;
}
const enum Constants {
ConfigSection = 'testing',
EnableCodeLensConfig = 'enableCodeLens',
EnableDiagnosticsConfig = 'enableProblemDiagnostics',
}
export function activate(context: vscode.ExtensionContext) {
const diagnostics = vscode.languages.createDiagnosticCollection();
const services = new TestingEditorServices(diagnostics);
context.subscriptions.push(
services,
diagnostics,
vscode.languages.registerCodeLensProvider({ scheme: 'file' }, services),
);
}
class TestingConfig implements IDisposable {
private section = vscode.workspace.getConfiguration(Constants.ConfigSection);
private readonly changeEmitter = new vscode.EventEmitter<void>();
private readonly listener = vscode.workspace.onDidChangeConfiguration(evt => {
if (evt.affectsConfiguration(Constants.ConfigSection)) {
this.section = vscode.workspace.getConfiguration(Constants.ConfigSection);
this.changeEmitter.fire();
}
});
public readonly onChange = this.changeEmitter.event;
public get codeLens() {
return this.section.get(Constants.EnableCodeLensConfig, true);
}
public get diagnostics() {
return this.section.get(Constants.EnableDiagnosticsConfig, true);
}
public get isEnabled() {
return this.codeLens || this.diagnostics;
}
public dispose() {
this.listener.dispose();
}
}
export class TestingEditorServices implements IDisposable, vscode.CodeLensProvider {
private readonly codeLensChangeEmitter = new vscode.EventEmitter<void>();
private readonly documents = new Map<string, DocumentTestObserver>();
private readonly config = new TestingConfig();
private disposables: IDisposable[];
private wasEnabled = this.config.isEnabled;
/**
* @inheritdoc
*/
public readonly onDidChangeCodeLenses = this.codeLensChangeEmitter.event;
constructor(private readonly diagnostics: vscode.DiagnosticCollection) {
this.disposables = [
new vscode.Disposable(() => this.expireAll()),
this.config,
vscode.window.onDidChangeVisibleTextEditors((editors) => {
if (!this.config.isEnabled) {
return;
}
const expiredEditors = new Set(this.documents.keys());
for (const editor of editors) {
const key = editor.document.uri.toString();
this.ensure(key, editor.document);
expiredEditors.delete(key);
}
for (const expired of expiredEditors) {
this.expire(expired);
}
}),
vscode.workspace.onDidCloseTextDocument((document) => {
this.expire(document.uri.toString());
}),
this.config.onChange(() => {
if (!this.wasEnabled || this.config.isEnabled) {
this.attachToAllVisible();
} else if (this.wasEnabled || !this.config.isEnabled) {
this.expireAll();
}
this.wasEnabled = this.config.isEnabled;
this.codeLensChangeEmitter.fire();
}),
];
if (this.config.isEnabled) {
this.attachToAllVisible();
}
}
/**
* @inheritdoc
*/
public provideCodeLenses(document: vscode.TextDocument) {
if (!this.config.codeLens) {
return [];
}
return this.documents.get(document.uri.toString())?.provideCodeLenses() ?? [];
}
/**
* Attach to all currently visible editors.
*/
private attachToAllVisible() {
for (const editor of vscode.window.visibleTextEditors) {
this.ensure(editor.document.uri.toString(), editor.document);
}
}
/**
* Unattaches to all tests.
*/
private expireAll() {
for (const observer of this.documents.values()) {
observer.dispose();
}
this.documents.clear();
}
/**
* Subscribes to tests for the document URI.
*/
private ensure(key: string, document: vscode.TextDocument) {
const state = this.documents.get(key);
if (!state) {
const observer = new DocumentTestObserver(document, this.diagnostics, this.config);
this.documents.set(key, observer);
observer.onDidChangeCodeLenses(() => this.config.codeLens && this.codeLensChangeEmitter.fire());
}
}
/**
* Expires and removes the watcher for the document.
*/
private expire(key: string) {
const observer = this.documents.get(key);
if (!observer) {
return;
}
observer.dispose();
this.documents.delete(key);
}
/**
* @override
*/
public dispose() {
this.disposables.forEach((d) => d.dispose());
}
}
class DocumentTestObserver implements IDisposable {
private readonly codeLensChangeEmitter = new vscode.EventEmitter<void>();
private readonly observer = vscode.test.createDocumentTestObserver(this.document);
private readonly disposables: IDisposable[];
public readonly onDidChangeCodeLenses = this.codeLensChangeEmitter.event;
private didHaveDiagnostics = this.config.diagnostics;
constructor(
private readonly document: vscode.TextDocument,
private readonly diagnostics: vscode.DiagnosticCollection,
private readonly config: TestingConfig,
) {
this.disposables = [
this.observer,
this.codeLensChangeEmitter,
config.onChange(() => {
if (this.didHaveDiagnostics && !config.diagnostics) {
this.diagnostics.set(document.uri, []);
} else if (!this.didHaveDiagnostics && config.diagnostics) {
this.updateDiagnostics();
}
this.didHaveDiagnostics = config.diagnostics;
}),
this.observer.onDidChangeTest(() => {
this.updateDiagnostics();
this.codeLensChangeEmitter.fire();
}),
];
}
private updateDiagnostics() {
if (!this.config.diagnostics) {
return;
}
const uriString = this.document.uri.toString();
const diagnostics: vscode.Diagnostic[] = [];
for (const test of iterateOverTests(this.observer.tests)) {
for (const message of test.state.messages) {
if (message.location?.uri.toString() === uriString) {
diagnostics.push({
range: message.location.range,
message: message.message.toString(),
severity: testToDiagnosticSeverity(message.severity),
});
}
}
}
this.diagnostics.set(this.document.uri, diagnostics);
}
public provideCodeLenses(): vscode.CodeLens[] {
const lenses: vscode.CodeLens[] = [];
for (const test of iterateOverTests(this.observer.tests)) {
const { debuggable = false, runnable = true } = test;
if (!test.location || !(debuggable || runnable)) {
continue;
}
const summary = summarize(test);
lenses.push({
isResolved: true,
range: test.location.range,
command: {
title: `$(${testStateToIcon[summary.computedState]}) ${getLabelFor(test, summary)}`,
command: 'vscode.runTests',
arguments: [[test]],
tooltip: localize('tooltip.debug', 'Debug {0}', test.label),
},
});
if (debuggable) {
lenses.push({
isResolved: true,
range: test.location.range,
command: {
title: localize('action.debug', 'Debug'),
command: 'vscode.debugTests',
arguments: [[test]],
tooltip: localize('tooltip.debug', 'Debug {0}', test.label),
},
});
}
}
return lenses;
}
/**
* @override
*/
public dispose() {
this.diagnostics.set(this.document.uri, []);
this.disposables.forEach(d => d.dispose());
}
}
function getLabelFor(test: vscode.TestItem, summary: ITestSummary) {
if (summary.duration !== undefined) {
return localize(
'tooltip.runStateWithDuration',
'{0}/{1} Tests Passed in {2}',
summary.passed,
summary.passed + summary.failed,
formatDuration(summary.duration),
);
}
if (summary.passed > 0 || summary.failed > 0) {
return localize('tooltip.runState', '{0}/{1} Tests Passed', summary.passed, summary.failed);
}
if (test.state.runState === vscode.TestRunState.Passed) {
return test.state.duration !== undefined
? localize('state.passedWithDuration', 'Passed in {0}', formatDuration(test.state.duration))
: localize('state.passed', 'Passed');
}
if (isFailedState(test.state.runState)) {
return localize('state.failed', 'Failed');
}
return localize('action.run', 'Run Tests');
}
function formatDuration(duration: number) {
if (duration < 1_000) {
return `${Math.round(duration)}ms`;
}
if (duration < 100_000) {
return `${(duration / 1000).toPrecision(3)}s`;
}
return `${(duration / 1000 / 60).toPrecision(3)}m`;
}
const statePriority: { [K in vscode.TestRunState]: number } = {
[vscode.TestRunState.Running]: 6,
[vscode.TestRunState.Queued]: 5,
[vscode.TestRunState.Errored]: 4,
[vscode.TestRunState.Failed]: 3,
[vscode.TestRunState.Passed]: 2,
[vscode.TestRunState.Skipped]: 1,
[vscode.TestRunState.Unset]: 0,
};
const maxPriority = (a: vscode.TestRunState, b: vscode.TestRunState) =>
statePriority[a] > statePriority[b] ? a : b;
const isFailedState = (s: vscode.TestRunState) =>
s === vscode.TestRunState.Failed || s === vscode.TestRunState.Errored;
interface ITestSummary {
passed: number;
failed: number;
duration: number | undefined;
computedState: vscode.TestRunState;
}
function summarize(test: vscode.TestItem) {
let passed = 0;
let failed = 0;
let duration: number | undefined;
let computedState = test.state.runState;
const queue = test.children ? [test.children] : [];
while (queue.length) {
for (const test of queue.pop()!) {
computedState = maxPriority(computedState, test.state.runState);
if (test.state.runState === vscode.TestRunState.Passed) {
passed++;
if (test.state.duration !== undefined) {
duration = test.state.duration + (duration ?? 0);
}
} else if (isFailedState(test.state.runState)) {
failed++;
if (test.state.duration !== undefined) {
duration = test.state.duration + (duration ?? 0);
}
}
if (test.children) {
queue.push(test.children);
}
}
}
return { passed, failed, duration, computedState };
}
function* iterateOverTests(tests: ReadonlyArray<vscode.TestItem>) {
const queue = [tests];
while (queue.length) {
for (const test of queue.pop()!) {
yield test;
if (test.children) {
queue.push(test.children);
}
}
}
}
const testStateToIcon: { [K in vscode.TestRunState]: string } = {
[vscode.TestRunState.Errored]: 'testing-error-icon',
[vscode.TestRunState.Failed]: 'testing-failed-icon',
[vscode.TestRunState.Passed]: 'testing-passed-icon',
[vscode.TestRunState.Queued]: 'testing-queued-icon',
[vscode.TestRunState.Skipped]: 'testing-skipped-icon',
[vscode.TestRunState.Unset]: 'beaker',
[vscode.TestRunState.Running]: 'loading~spin',
};
const testToDiagnosticSeverity = (severity: vscode.TestMessageSeverity | undefined) => {
switch (severity) {
case vscode.TestMessageSeverity.Hint:
return vscode.DiagnosticSeverity.Hint;
case vscode.TestMessageSeverity.Information:
return vscode.DiagnosticSeverity.Information;
case vscode.TestMessageSeverity.Warning:
return vscode.DiagnosticSeverity.Warning;
case vscode.TestMessageSeverity.Error:
default:
return vscode.DiagnosticSeverity.Error;
}
};
/*---------------------------------------------------------------------------------------------
* 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'/>
/// <reference path='../../../../src/vs/vscode.proposed.d.ts'/>
{
"extends": "../shared.tsconfig.json",
"compilerOptions": {
"outDir": "./out",
},
"include": [
"src/**/*"
]
}
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
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==
......@@ -2090,13 +2090,11 @@ declare module 'vscode' {
readonly onDidChangeTest: Event<T>;
/**
* An event that should be fired when all tests that are currently defined
* have been discovered. The provider should continue to watch for changes
* and fire `onDidChangeTest` until the hierarchy is disposed.
*
* @todo can this be covered by existing progress apis? Or return a promise
* Promise that should be resolved when all tests that are initially
* defined have been discovered. The provider should continue to watch for
* changes and fire `onDidChangeTest` until the hierarchy is disposed.
*/
readonly onDidDiscoverInitialTests: Event<void>;
readonly discoveredInitialTests?: Thenable<unknown>;
/**
* Dispose will be called when there are no longer observers interested
......@@ -2126,7 +2124,7 @@ declare module 'vscode' {
* there is a previous undisposed watcher for the given workspace folder.
*/
// eslint-disable-next-line vscode-dts-provider-naming
createWorkspaceTestHierarchy?(workspace: WorkspaceFolder): TestHierarchy<T>;
createWorkspaceTestHierarchy?(workspace: WorkspaceFolder): TestHierarchy<T> | undefined;
/**
* Requests that tests be provided for the given document. This will
......@@ -2134,7 +2132,7 @@ declare module 'vscode' {
* for instance by code lens UI.
*/
// eslint-disable-next-line vscode-dts-provider-naming
createDocumentTestHierarchy?(document: TextDocument): TestHierarchy<T>;
createDocumentTestHierarchy?(document: TextDocument): TestHierarchy<T> | undefined;
/**
* Starts a test run. This should cause {@link onDidChangeTest} to
......
......@@ -4,13 +4,30 @@
*--------------------------------------------------------------------------------------------*/
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { getTestSubscriptionKey, RunTestsRequest, RunTestsResult, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
import { getTestSubscriptionKey, RunTestsRequest, RunTestsResult, TestDiffOpType, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
import { ITestService } from 'vs/workbench/contrib/testing/common/testService';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { ExtHostContext, ExtHostTestingResource, ExtHostTestingShape, IExtHostContext, MainContext, MainThreadTestingShape } from '../common/extHost.protocol';
import { URI, UriComponents } from 'vs/base/common/uri';
import { CancellationToken } from 'vs/base/common/cancellation';
const reviveDiff = (diff: TestsDiff) => {
for (const entry of diff) {
if (entry[0] === TestDiffOpType.Add || entry[0] === TestDiffOpType.Update) {
const item = entry[1];
if (item.item.location) {
item.item.location.uri = URI.revive(item.item.location.uri);
}
for (const message of item.item.state.messages) {
if (message.location) {
message.location.uri = URI.revive(message.location.uri);
}
}
}
}
};
@extHostNamedCustomer(MainContext.MainThreadTesting)
export class MainThreadTesting extends Disposable implements MainThreadTestingShape {
private readonly proxy: ExtHostTestingShape;
......@@ -76,6 +93,7 @@ export class MainThreadTesting extends Disposable implements MainThreadTestingSh
* @inheritdoc
*/
public $publishDiff(resource: ExtHostTestingResource, uri: UriComponents, diff: TestsDiff): void {
reviveDiff(diff);
this.testService.publishDiff(resource, URI.revive(uri), diff);
}
......
......@@ -19,7 +19,7 @@ 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 { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
import { AbstractIncrementalTestCollection, EMPTY_TEST_RESULT, IncrementalChangeCollector, IncrementalTestCollectionItem, InternalTestItem, RunTestForProviderRequest, RunTestsResult, TestDiffOpType, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
import { AbstractIncrementalTestCollection, EMPTY_TEST_RESULT, IncrementalChangeCollector, IncrementalTestCollectionItem, InternalTestItem, 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()}`;
......@@ -148,7 +148,7 @@ export class ExtHostTesting implements ExtHostTestingShape {
updateDelta(1);
disposable.add(hierarchy);
collection.addRoot(hierarchy.root, id);
hierarchy.onDidDiscoverInitialTests(() => updateDelta(-1));
Promise.resolve(hierarchy.discoveredInitialTests).then(() => updateDelta(-1));
hierarchy.onDidChangeTest(e => collection.onItemChange(e, id));
} catch (e) {
console.error(e);
......@@ -669,7 +669,7 @@ class ExtHostTestItem implements vscode.TestItem, RequiredTestItem {
}
public toJSON() {
const serialized: RequiredTestItem = {
const serialized: RequiredTestItem & TestIdWithProvider = {
label: this.label,
description: this.description,
state: this.state,
......@@ -677,6 +677,9 @@ class ExtHostTestItem implements vscode.TestItem, RequiredTestItem {
runnable: this.runnable,
debuggable: this.debuggable,
children: this.children.map(c => (c as ExtHostTestItem).toJSON()),
providerId: this.#internal.providerId,
testId: this.#internal.id,
};
return serialized;
......@@ -697,6 +700,7 @@ abstract class AbstractTestObserverFactory {
const resourceKey = resourceUri.toString();
const resource = this.resources.get(resourceKey) ?? this.createObserverData(resourceUri);
resource.pendingDeletion?.dispose();
resource.observers++;
return {
......
......@@ -5,9 +5,11 @@
import { localize } from 'vs/nls';
import { registerAction2 } from 'vs/platform/actions/common/actions';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { Registry } from 'vs/platform/registry/common/platform';
import { Extensions as ViewContainerExtensions, IViewContainersRegistry, IViewsRegistry, ViewContainerLocation } from 'vs/workbench/common/views';
import { testingViewIcon } from 'vs/workbench/contrib/testing/browser/icons';
......@@ -15,6 +17,7 @@ import { ITestingCollectionService, TestingCollectionService } from 'vs/workbenc
import { TestingExplorerView } from 'vs/workbench/contrib/testing/browser/testingExplorerView';
import { TestingViewPaneContainer } from 'vs/workbench/contrib/testing/browser/testingViewPaneContainer';
import { Testing } from 'vs/workbench/contrib/testing/common/constants';
import { TestIdWithProvider } from 'vs/workbench/contrib/testing/common/testCollection';
import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys';
import { ITestService } from 'vs/workbench/contrib/testing/common/testService';
import { TestService } from 'vs/workbench/contrib/testing/common/testServiceImpl';
......@@ -70,3 +73,19 @@ registerAction2(Action.TestingViewAsTreeAction);
registerAction2(Action.CancelTestRunAction);
registerAction2(Action.RunSelectedAction);
registerAction2(Action.DebugSelectedAction);
CommandsRegistry.registerCommand({
id: 'vscode.runTests',
handler: async (accessor: ServicesAccessor, tests: TestIdWithProvider[]) => {
const testService = accessor.get(ITestService);
testService.runTests({ debug: false, tests: tests.filter(t => t.providerId && t.testId) });
}
});
CommandsRegistry.registerCommand({
id: 'vscode.debugTests',
handler: async (accessor: ServicesAccessor, tests: TestIdWithProvider[]) => {
const testService = accessor.get(ITestService);
testService.runTests({ debug: true, tests: tests.filter(t => t.providerId && t.testId) });
}
});
......@@ -213,7 +213,7 @@ export class TestingExplorerViewModel extends Disposable {
}
editorService.openEditor({
resource: URI.revive(location.uri),
resource: location.uri,
options: { selection: location.range, preserveFocus: true }
});
}));
......@@ -434,7 +434,7 @@ class TestsRenderer implements ITreeRenderer<ITestTreeElement, FuzzyScore, TestT
const test = element.test;
if (test) {
if (test.item.location) {
label.resource = URI.revive(test.item.location.uri);
label.resource = test.item.location.uri;
}
options.title = 'hover title';
......@@ -529,15 +529,7 @@ class HierarchalElement implements ITestTreeElement {
}
public get location() {
const location = this.test.item.location;
if (!location) {
return;
}
return {
uri: URI.revive(location.uri),
range: location.range,
};
return this.test.item.location;
}
constructor(public readonly test: InternalTestItem, public readonly parentItem: HierarchalFolder | HierarchalElement) {
......
......@@ -121,6 +121,10 @@ export class TestService extends Disposable implements ITestService {
});
}).filter(isDefined);
if (requests.length === 0) {
return EMPTY_TEST_RESULT;
}
this.runningTests.set(req, cancelSource);
this.runStartedEmitter.fire(req);
this.isRunning.set(true);
......
......@@ -217,7 +217,7 @@ export class GettingStartedPage extends Disposable {
const categoriesSlide = assertIsDefined(document.getElementById('gettingStartedSlideCategory'));
const tasksSlide = assertIsDefined(document.getElementById('gettingStartedSlideDetails'));
const tasksContent = assertIsDefined(document.getElementById('gettingStartedDetailsContent'));
const tasksContent = assertIsDefined(document.getElementById('gettingStartedDetailsContent') as HTMLElement);
tasksContent.remove();
if (this.detailImageScrollbar) { this.detailImageScrollbar.dispose(); }
this.detailImageScrollbar = this._register(new DomScrollableElement(tasksContent, { className: 'full-height-scrollable' }));
......
......@@ -22,6 +22,8 @@ const stubTest = (label: string): TestItem => ({
const simplify = (item: TestItem) => {
if ('toJSON' in item) {
item = (item as any).toJSON();
delete (item as any).providerId;
delete (item as any).testId;
}
return { ...item, children: undefined };
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册