Merge remote-tracking branch 'origin/main' into alex/update-playwright

This commit is contained in:
Alexandru Dima 2021-06-09 15:56:09 +02:00
commit a573bc0b8d
No known key found for this signature in database
GPG key ID: 6E58D7B045760DA0
96 changed files with 2167 additions and 1658 deletions

View file

@ -104,6 +104,7 @@
"restrictions": [
"assert",
"sinon",
"sinon-test",
"vs/nls",
"**/vs/base/common/**",
"**/vs/base/test/common/**"
@ -139,6 +140,7 @@
"restrictions": [
"assert",
"sinon",
"sinon-test",
"vs/nls",
"**/vs/base/{common,browser}/**",
"**/vs/base/test/{common,browser}/**"
@ -212,6 +214,7 @@
"restrictions": [
"assert",
"sinon",
"sinon-test",
"vs/nls",
"**/vs/base/common/**",
"**/vs/base/parts/*/common/**",
@ -225,6 +228,7 @@
"restrictions": [
"assert",
"sinon",
"sinon-test",
"vs/nls",
"**/vs/base/{common,browser}/**",
"**/vs/base/parts/*/{common,browser}/**",
@ -289,6 +293,7 @@
"restrictions": [
"assert",
"sinon",
"sinon-test",
"vs/nls",
"**/vs/base/{common,browser}/**",
"**/vs/base/parts/*/{common,browser}/**",
@ -311,6 +316,7 @@
"restrictions": [
"assert",
"sinon",
"sinon-test",
"vs/nls",
"**/vs/base/common/**",
"**/vs/platform/*/common/**",
@ -334,6 +340,7 @@
"restrictions": [
"assert",
"sinon",
"sinon-test",
"vs/nls",
"**/vs/base/{common,browser}/**",
"**/vs/platform/*/{common,browser}/**",
@ -357,6 +364,7 @@
"restrictions": [
"assert",
"sinon",
"sinon-test",
"vs/nls",
"**/vs/base/common/**",
"**/vs/platform/*/common/**",
@ -383,6 +391,7 @@
"restrictions": [
"assert",
"sinon",
"sinon-test",
"vs/nls",
"**/vs/base/{common,browser}/**",
"**/vs/platform/*/{common,browser}/**",
@ -397,6 +406,7 @@
"restrictions": [
"assert",
"sinon",
"sinon-test",
"vs/nls",
"**/vs/base/{common,browser}/**",
"**/vs/base/test/{common,browser}/**",
@ -921,6 +931,7 @@
"**/vs/**",
"assert",
"sinon",
"sinon-test",
"crypto",
"vscode"
]
@ -952,6 +963,7 @@
"**/vs/**",
"assert",
"sinon",
"sinon-test",
"crypto",
"xterm*"
]
@ -962,6 +974,7 @@
"**/vs/**",
"assert",
"sinon",
"sinon-test",
"crypto",
"xterm*"
]
@ -989,6 +1002,7 @@
"vscode-dts-cancellation": "warn",
"vscode-dts-use-thenable": "warn",
"vscode-dts-region-comments": "warn",
"vscode-dts-vscode-in-comments": "warn",
"vscode-dts-provider-naming": [
"warn",
{

View file

@ -7,19 +7,17 @@ on:
jobs:
main:
runs-on: ubuntu-latest
if: contains(github.event.issue.labels.*.name, '*english-please')
steps:
- name: Checkout Actions
if: contains(github.event.issue.labels.*.name, '*english-please')
uses: actions/checkout@v2
with:
repository: "microsoft/vscode-github-triage-actions"
ref: stable
path: ./actions
- name: Install Actions
if: contains(github.event.issue.labels.*.name, '*english-please')
run: npm install --production --prefix ./actions
- name: Run English Please
if: contains(github.event.issue.labels.*.name, '*english-please')
uses: ./actions/english-please
with:
appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}}

View file

@ -33,5 +33,5 @@ jobs:
with:
languages: typescript
repo-token: ${{ secrets.GITHUB_TOKEN }}
typescriptVersion: 0.6.0-next.8
typescriptVersion: 0.6.0-dev.1
continue-on-error: true

View file

@ -51,7 +51,7 @@ module.exports.indentationFilter = [
'!test/monaco/out/**',
'!test/smoke/out/**',
'!extensions/typescript-language-features/test-workspace/**',
'!extensions/notebook-markdown-extensions/notebook-out/**',
'!extensions/markdown-math/notebook-out/**',
'!extensions/vscode-api-tests/testWorkspace/**',
'!extensions/vscode-api-tests/testWorkspace2/**',
'!extensions/vscode-custom-editor-tests/test-workspace/**',
@ -89,7 +89,7 @@ module.exports.indentationFilter = [
'!**/*.dockerfile',
'!extensions/markdown-language-features/media/*.js',
'!extensions/markdown-language-features/notebook-out/*.js',
'!extensions/notebook-markdown-extensions/notebook-out/*.js',
'!extensions/markdown-math/notebook-out/*.js',
'!extensions/simple-browser/media/*.js',
];
@ -119,7 +119,7 @@ module.exports.copyrightFilter = [
'!resources/completions/**',
'!extensions/configuration-editing/build/inline-allOf.ts',
'!extensions/markdown-language-features/media/highlight.css',
'!extensions/notebook-markdown-extensions/notebook-out/**',
'!extensions/markdown-math/notebook-out/**',
'!extensions/html-language-features/server/src/modes/typescript/*',
'!extensions/*/server/bin/*',
'!src/vs/editor/test/node/classification/typescript-test.ts',

View file

@ -50,6 +50,7 @@ const compilations = [
'json-language-features/server/tsconfig.json',
'markdown-language-features/preview-src/tsconfig.json',
'markdown-language-features/tsconfig.json',
'markdown-math/tsconfig.json',
'merge-conflict/tsconfig.json',
'microsoft-authentication/tsconfig.json',
'npm/tsconfig.json',

View file

@ -0,0 +1,45 @@
"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
module.exports = new class ApiVsCodeInComments {
constructor() {
this.meta = {
messages: {
comment: `Don't use the term 'vs code' in comments`
}
};
}
create(context) {
const sourceCode = context.getSourceCode();
return {
['Program']: (_node) => {
for (const comment of sourceCode.getAllComments()) {
if (comment.type !== 'Block') {
continue;
}
if (!comment.range) {
continue;
}
const startIndex = comment.range[0] + '/*'.length;
const re = /vs code/ig;
let match;
while ((match = re.exec(comment.value))) {
// Allow using 'VS Code' in quotes
if (comment.value[match.index - 1] === `'` && comment.value[match.index + match[0].length] === `'`) {
continue;
}
// Types for eslint seem incorrect
const start = sourceCode.getLocFromIndex(startIndex + match.index);
const end = sourceCode.getLocFromIndex(startIndex + match.index + match[0].length);
context.report({
messageId: 'comment',
loc: { start, end }
});
}
}
}
};
}
};

View file

@ -0,0 +1,53 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as eslint from 'eslint';
import type * as estree from 'estree';
export = new class ApiVsCodeInComments implements eslint.Rule.RuleModule {
readonly meta: eslint.Rule.RuleMetaData = {
messages: {
comment: `Don't use the term 'vs code' in comments`
}
};
create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener {
const sourceCode = context.getSourceCode();
return {
['Program']: (_node: any) => {
for (const comment of sourceCode.getAllComments()) {
if (comment.type !== 'Block') {
continue;
}
if (!comment.range) {
continue;
}
const startIndex = comment.range[0] + '/*'.length;
const re = /vs code/ig;
let match: RegExpExecArray | null;
while ((match = re.exec(comment.value))) {
// Allow using 'VS Code' in quotes
if (comment.value[match.index - 1] === `'` && comment.value[match.index + match[0].length] === `'`) {
continue;
}
// Types for eslint seem incorrect
const start = sourceCode.getLocFromIndex(startIndex + match.index) as any as estree.Position;
const end = sourceCode.getLocFromIndex(startIndex + match.index + match[0].length) as any as estree.Position;
context.report({
messageId: 'comment',
loc: { start, end }
});
}
}
}
};
}
};

View file

@ -338,7 +338,7 @@ const webpackMediaConfigFiles = [
// Additional projects to run esbuild on. These typically build code for webviews
const esbuildMediaScripts = [
'markdown-language-features/esbuild.js',
'notebook-markdown-extensions/esbuild.js',
'markdown-math/esbuild.js',
];
async function webpackExtensions(taskName, isWatch, webpackConfigLocations) {
const webpack = require('webpack');

View file

@ -417,7 +417,7 @@ const webpackMediaConfigFiles = [
// Additional projects to run esbuild on. These typically build code for webviews
const esbuildMediaScripts = [
'markdown-language-features/esbuild.js',
'notebook-markdown-extensions/esbuild.js',
'markdown-math/esbuild.js',
];
export async function webpackExtensions(taskName: string, isWatch: boolean, webpackConfigLocations: { configPath: string, outputRoot?: string }[]) {

View file

@ -148,9 +148,9 @@ fsevents@~2.3.1:
integrity sha512-YR47Eg4hChJGAB1O3yEAOkGO+rlzutoICGqGo9EZ4lKWokzZRSyIW1QmTzqjtw8MJdj9srP869CuWw/hyzSiBw==
glob-parent@^5.1.1, glob-parent@~5.1.0:
version "5.1.1"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229"
integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==
version "5.1.2"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
dependencies:
is-glob "^4.0.1"

View file

@ -28,9 +28,9 @@ exports.dirs = [
'extensions/json-language-features',
'extensions/json-language-features/server',
'extensions/markdown-language-features',
'extensions/markdown-math',
'extensions/merge-conflict',
'extensions/microsoft-authentication',
'extensions/notebook-markdown-extensions',
'extensions/npm',
'extensions/php-language-features',
'extensions/search-result',

View file

@ -25,7 +25,7 @@ function update(options) {
throw new Error(`${location} doesn't exist.`);
}
let locExtFolder = idOrPath;
if (/^\w{2}(-\w+)?$/.test(idOrPath)) {
if (/^\w{2,3}(-\w+)?$/.test(idOrPath)) {
locExtFolder = path.join('..', 'vscode-loc', 'i18n', `vscode-language-pack-${idOrPath}`);
}
let locExtStat = fs.statSync(locExtFolder);
@ -88,7 +88,7 @@ function update(options) {
for (let tp of translationPaths) {
localization.translations.push({ id: tp.id, path: `./translations/${tp.resourceName}` });
}
fs.writeFileSync(path.join(locExtFolder, 'package.json'), JSON.stringify(packageJSON, null, '\t'));
fs.writeFileSync(path.join(locExtFolder, 'package.json'), JSON.stringify(packageJSON, null, '\t') + '\n');
}
});
});

View file

@ -1488,9 +1488,9 @@ node-fetch@^2.6.0:
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
normalize-url@^4.1.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129"
integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==
version "4.5.1"
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a"
integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==
nth-check@~1.0.1:
version "1.0.2"

View file

@ -10,14 +10,164 @@ export function activate() {
html: true
});
const style = document.createElement('style');
style.classList.add('markdown-style');
style.textContent = `
.emptyMarkdownCell::before {
content: "${document.documentElement.style.getPropertyValue('--notebook-cell-markup-empty-content')}";
font-style: italic;
opacity: 0.6;
}
img {
max-width: 100%;
max-height: 100%;
}
a {
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
a:focus,
input:focus,
select:focus,
textarea:focus {
outline: 1px solid -webkit-focus-ring-color;
outline-offset: -1px;
}
hr {
border: 0;
height: 2px;
border-bottom: 2px solid;
}
h1 {
font-size: 26px;
line-height: 31px;
margin: 0;
margin-bottom: 13px;
}
h2 {
font-size: 19px;
margin: 0;
margin-bottom: 10px;
}
h1,
h2,
h3 {
font-weight: normal;
}
div {
width: 100%;
}
/* Adjust margin of first item in markdown cell */
*:first-child {
margin-top: 0px;
}
/* h1 tags don't need top margin */
h1:first-child {
margin-top: 0;
}
/* Removes bottom margin when only one item exists in markdown cell */
*:only-child,
*:last-child {
margin-bottom: 0;
padding-bottom: 0;
}
/* makes all markdown cells consistent */
div {
min-height: var(--notebook-markdown-min-height);
}
table {
border-collapse: collapse;
border-spacing: 0;
}
table th,
table td {
border: 1px solid;
}
table > thead > tr > th {
text-align: left;
border-bottom: 1px solid;
}
table > thead > tr > th,
table > thead > tr > td,
table > tbody > tr > th,
table > tbody > tr > td {
padding: 5px 10px;
}
table > tbody > tr + tr > td {
border-top: 1px solid;
}
blockquote {
margin: 0 7px 0 5px;
padding: 0 16px 0 10px;
border-left-width: 5px;
border-left-style: solid;
}
code,
.code {
font-size: 1em;
line-height: 1.357em;
}
.code {
white-space: pre-wrap;
}
`;
document.head.append(style);
return {
renderOutputItem: (outputInfo: { text(): string }, element: HTMLElement) => {
const rendered = markdownIt.render(outputInfo.text());
element.innerHTML = rendered;
let previewNode: HTMLElement;
if (!element.shadowRoot) {
const previewRoot = element.attachShadow({ mode: 'open' });
// Insert styles into markdown preview shadow dom so that they are applied
for (const markdownStyleNode of document.getElementsByClassName('markdown-style')) {
element.insertAdjacentElement('beforebegin', markdownStyleNode.cloneNode(true) as Element);
// Insert styles into markdown preview shadow dom so that they are applied.
// First add default webview style
const defaultStyles = document.getElementById('_defaultStyles') as HTMLStyleElement;
previewRoot.appendChild(defaultStyles.cloneNode(true));
// And then contributed styles
for (const markdownStyleNode of document.getElementsByClassName('markdown-style')) {
previewRoot.appendChild(markdownStyleNode.cloneNode(true));
}
previewNode = document.createElement('div');
previewNode.id = 'preview';
previewRoot.appendChild(previewNode);
} else {
previewNode = element.shadowRoot.getElementById('preview')!;
}
const text = outputInfo.text();
if (text.trim().length === 0) {
previewNode.innerText = '';
previewNode.classList.add('emptyMarkdownCell');
} else {
previewNode.classList.remove('emptyMarkdownCell');
const rendered = markdownIt.render(text);
previewNode.innerHTML = rendered;
}
},
extendMarkdownIt: (f: (md: typeof markdownIt) => void) => {

View file

@ -26,6 +26,7 @@
"onCommand:markdown.showSource",
"onCommand:markdown.showPreviewSecuritySelector",
"onCommand:markdown.api.render",
"onCommand:markdown.api.reloadPlugins",
"onWebviewPanel:markdown.preview",
"onCustomEditor:vscode.markdown.preview.editor"
],

View file

@ -3,11 +3,13 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export { OpenDocumentLinkCommand } from './openDocumentLink';
export { ShowPreviewCommand, ShowPreviewToSideCommand, ShowLockedPreviewToSideCommand } from './showPreview';
export { ShowSourceCommand } from './showSource';
export { RefreshPreviewCommand } from './refreshPreview';
export { ShowPreviewSecuritySelectorCommand } from './showPreviewSecuritySelector';
export { MoveCursorToPositionCommand } from './moveCursorToPosition';
export { ToggleLockCommand } from './toggleLock';
export { OpenDocumentLinkCommand } from './openDocumentLink';
export { RefreshPreviewCommand } from './refreshPreview';
export { ReloadPlugins } from './reloadPlugins';
export { RenderDocument } from './renderDocument';
export { ShowLockedPreviewToSideCommand, ShowPreviewCommand, ShowPreviewToSideCommand } from './showPreview';
export { ShowPreviewSecuritySelectorCommand } from './showPreviewSecuritySelector';
export { ShowSourceCommand } from './showSource';
export { ToggleLockCommand } from './toggleLock';

View file

@ -0,0 +1,23 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Command } from '../commandManager';
import { MarkdownPreviewManager } from '../features/previewManager';
import { MarkdownEngine } from '../markdownEngine';
export class ReloadPlugins implements Command {
public readonly id = 'markdown.api.reloadPlugins';
public constructor(
private readonly webviewManager: MarkdownPreviewManager,
private readonly engine: MarkdownEngine,
) { }
public execute(): void {
this.engine.reloadPlugins();
this.engine.cleanCache();
this.webviewManager.refresh();
}
}

View file

@ -80,6 +80,7 @@ function registerMarkdownCommands(
commandManager.register(new commands.OpenDocumentLinkCommand(engine));
commandManager.register(new commands.ToggleLockCommand(previewManager));
commandManager.register(new commands.RenderDocument(engine));
commandManager.register(new commands.ReloadPlugins(previewManager, engine));
return commandManager;
}

View file

@ -67,6 +67,7 @@ interface RenderEnv {
}
export class MarkdownEngine {
private md?: Promise<MarkdownIt>;
private _slugCount = new Map<string, number>();
@ -129,6 +130,10 @@ export class MarkdownEngine {
return md;
}
public reloadPlugins() {
this.md = undefined;
}
private tokenizeDocument(
document: SkinnyTextDocument,
config: MarkdownItConfig,

View file

@ -1,3 +1,3 @@
# Markdown Notebook Math support
# Markdown Math support
**Notice:** This extension is bundled with Visual Studio Code. It can be disabled but not uninstalled.

View file

@ -0,0 +1,17 @@
/*---------------------------------------------------------------------------------------------
* 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/extension.ts'
}
});

View file

@ -0,0 +1,20 @@
/*---------------------------------------------------------------------------------------------
* 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',
}
});

View file

Before

Width:  |  Height:  |  Size: 903 B

After

Width:  |  Height:  |  Size: 903 B

View file

@ -1,11 +1,10 @@
{
"name": "notebook-markdown-extensions",
"name": "markdown-math",
"displayName": "%displayName%",
"description": "%description%",
"version": "1.0.0",
"icon": "icon.png",
"publisher": "vscode",
"enableProposedApi": true,
"license": "MIT",
"aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217",
"engines": {
@ -20,16 +19,36 @@
"supported": true
}
},
"main": "./out/extension",
"browser": "./dist/browser/extension",
"activationEvents": [],
"contributes": {
"notebookRenderer": [
{
"id": "markdownItRenderer-katex",
"displayName": "Markdown it katex renderer",
"displayName": "Markdown it KaTeX renderer",
"entrypoint": {
"extends": "markdownItRenderer",
"path": "./notebook-out/katex.js"
}
}
],
"markdown.markdownItPlugins": true,
"markdown.previewStyles": [
"./node_modules/katex/dist/katex.min.css",
"./preview-styles/index.css"
],
"configuration": [
{
"title": "Markdown",
"properties": {
"markdown.math.enabled": {
"type": "boolean",
"default": true,
"description": "%config.markdown.math.enabled%"
}
}
}
]
},
"scripts": {
@ -37,10 +56,11 @@
"watch": "npm run build-notebook",
"build-notebook": "node ./esbuild"
},
"dependencies": {
"@iktakahiro/markdown-it-katex": "https://github.com/mjbvz/markdown-it-katex.git"
},
"devDependencies": {
"@iktakahiro/markdown-it-katex": "https://github.com/mjbvz/markdown-it-katex.git",
"@types/markdown-it": "^0.0.0",
"markdown-it": "^12.0.4"
"@types/markdown-it": "^0.0.0"
},
"repository": {
"type": "git",

View file

@ -0,0 +1,5 @@
{
"displayName": "Markdown Math",
"description": "Adds math support to markdown in notebooks.",
"config.markdown.math.enabled": "Enable/disable rendering math in the built-in markdown preview."
}

View file

@ -0,0 +1,8 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.katex-error {
color: var(--vscode-editorError-foreground);
}

View file

@ -0,0 +1,31 @@
/*---------------------------------------------------------------------------------------------
* 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';
const enabledSetting = 'markdown.math.enabled';
export function activate(context: vscode.ExtensionContext) {
function isEnabled(): boolean {
const config = vscode.workspace.getConfiguration('markdown');
console.log(config.get<boolean>('math.enabled', true));
return config.get<boolean>('math.enabled', true);
}
vscode.workspace.onDidChangeConfiguration(e => {
if (e.affectsConfiguration(enabledSetting)) {
vscode.commands.executeCommand('markdown.api.reloadPlugins');
}
}, undefined, context.subscriptions);
return {
extendMarkdownIt(md: any) {
if (isEnabled()) {
const katex = require('@iktakahiro/markdown-it-katex');
return md.use(katex);
}
return md;
}
};
}

View file

@ -0,0 +1 @@
/// <reference path='../../../src/vs/vscode.d.ts'/>

View file

@ -0,0 +1,17 @@
{
"extends": "../tsconfig.base.json",
"compilerOptions": {
"outDir": "./out",
"experimentalDecorators": true,
"lib": [
"es6",
"es2015.promise",
"es2019.array",
"es2020.string",
"dom"
]
},
"include": [
"src/**/*"
]
}

View file

@ -0,0 +1,26 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@iktakahiro/markdown-it-katex@https://github.com/mjbvz/markdown-it-katex.git":
version "4.0.1"
resolved "https://github.com/mjbvz/markdown-it-katex.git#2bf0b89c6c22ef0b585f55ccab66d1f7c5356bea"
dependencies:
katex "^0.13.0"
"@types/markdown-it@^0.0.0":
version "0.0.0"
resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-0.0.0.tgz#8f6acaa5e3245e275f684e95deb3e518d1c6ab16"
integrity sha1-j2rKpeMkXidfaE6V3rPlGNHGqxY=
commander@^6.0.0:
version "6.2.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==
katex@^0.13.0:
version "0.13.0"
resolved "https://registry.yarnpkg.com/katex/-/katex-0.13.0.tgz#62900e56c1ad8fdf7da23399e50d7a7b690b39ab"
integrity sha512-6cHbzbegYgS9vvVGuH8UA+o97X+ZshtboSqJJCdq7trBYzuD75JNwr7Ef606xkUjecPPhFnyB+afx1dVafielg==
dependencies:
commander "^6.0.0"

View file

@ -1,4 +0,0 @@
{
"displayName": "Markdown Notebook math",
"description": "Provides rich language support for Markdown."
}

View file

@ -1,64 +0,0 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@iktakahiro/markdown-it-katex@https://github.com/mjbvz/markdown-it-katex.git":
version "4.0.1"
resolved "https://github.com/mjbvz/markdown-it-katex.git#2bf0b89c6c22ef0b585f55ccab66d1f7c5356bea"
dependencies:
katex "^0.13.0"
"@types/markdown-it@^0.0.0":
version "0.0.0"
resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-0.0.0.tgz#8f6acaa5e3245e275f684e95deb3e518d1c6ab16"
integrity sha1-j2rKpeMkXidfaE6V3rPlGNHGqxY=
argparse@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
commander@^6.0.0:
version "6.2.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==
entities@~2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5"
integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==
katex@^0.13.0:
version "0.13.0"
resolved "https://registry.yarnpkg.com/katex/-/katex-0.13.0.tgz#62900e56c1ad8fdf7da23399e50d7a7b690b39ab"
integrity sha512-6cHbzbegYgS9vvVGuH8UA+o97X+ZshtboSqJJCdq7trBYzuD75JNwr7Ef606xkUjecPPhFnyB+afx1dVafielg==
dependencies:
commander "^6.0.0"
linkify-it@^3.0.1:
version "3.0.2"
resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-3.0.2.tgz#f55eeb8bc1d3ae754049e124ab3bb56d97797fb8"
integrity sha512-gDBO4aHNZS6coiZCKVhSNh43F9ioIL4JwRjLZPkoLIY4yZFwg264Y5lu2x6rb1Js42Gh6Yqm2f6L2AJcnkzinQ==
dependencies:
uc.micro "^1.0.1"
markdown-it@^12.0.4:
version "12.0.4"
resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-12.0.4.tgz#eec8247d296327eac3ba9746bdeec9cfcc751e33"
integrity sha512-34RwOXZT8kyuOJy25oJNJoulO8L0bTHYWXcdZBYZqFnjIy3NgjeoM3FmPXIOFQ26/lSHYMr8oc62B6adxXcb3Q==
dependencies:
argparse "^2.0.1"
entities "~2.1.0"
linkify-it "^3.0.1"
mdurl "^1.0.1"
uc.micro "^1.0.5"
mdurl@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=
uc.micro@^1.0.1, uc.micro@^1.0.5:
version "1.0.6"
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"
integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==

View file

@ -703,7 +703,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType
if (isWeb()) {
// On web, treat absolute paths as pointing to standard lib files
if (filepath.startsWith('/')) {
return vscode.Uri.joinPath(this.context.extensionUri, 'node_modules', 'typescript', 'lib', filepath.slice(1));
return vscode.Uri.joinPath(this.context.extensionUri, 'dist', 'browser', 'typescript', filepath.slice(1));
}
}

View file

@ -21,6 +21,7 @@ suite('Notebook Editor', function () {
};
const disposables: vscode.Disposable[] = [];
const testDisposables: vscode.Disposable[] = [];
suiteTeardown(async function () {
utils.assertNoRpc();
@ -38,6 +39,10 @@ suite('Notebook Editor', function () {
disposables.push(vscode.workspace.registerNotebookSerializer('notebook.nbdtest', contentSerializer));
});
teardown(async function () {
utils.disposeAll(testDisposables);
testDisposables.length = 0;
});
test('showNotebookDocment', async function () {
@ -74,4 +79,48 @@ suite('Notebook Editor', function () {
assert.ok(await openedEditor);
assert.strictEqual(editor.document.uri.toString(), resource.toString());
});
test('Active/Visible Editor', async function () {
const firstEditorOpen = utils.asPromise(vscode.window.onDidChangeActiveNotebookEditor);
const resource = await utils.createRandomFile(undefined, undefined, '.nbdtest');
const firstEditor = await vscode.window.showNotebookDocument(resource);
await firstEditorOpen;
assert.strictEqual(vscode.window.activeNotebookEditor, firstEditor);
assert.strictEqual(vscode.window.visibleNotebookEditors.includes(firstEditor), true);
const secondEditor = await vscode.window.showNotebookDocument(resource, { viewColumn: vscode.ViewColumn.Beside });
assert.strictEqual(secondEditor === vscode.window.activeNotebookEditor, true);
assert.notStrictEqual(firstEditor, secondEditor);
assert.strictEqual(vscode.window.visibleNotebookEditors.includes(secondEditor), true);
assert.strictEqual(vscode.window.visibleNotebookEditors.includes(firstEditor), true);
assert.strictEqual(vscode.window.visibleNotebookEditors.length, 2);
});
test('Notebook Editor Event - onDidChangeVisibleNotebookEditors on open/close', async function () {
const openedEditor = utils.asPromise(vscode.window.onDidChangeVisibleNotebookEditors);
const resource = await utils.createRandomFile(undefined, undefined, '.nbdtest');
await vscode.window.showNotebookDocument(resource);
assert.ok(await openedEditor);
const firstEditorClose = utils.asPromise(vscode.window.onDidChangeVisibleNotebookEditors);
await utils.closeAllEditors();
await firstEditorClose;
});
test('Notebook Editor Event - onDidChangeVisibleNotebookEditors on two editor groups', async function () {
const resource = await utils.createRandomFile(undefined, undefined, '.nbdtest');
let count = 0;
testDisposables.push(vscode.window.onDidChangeVisibleNotebookEditors(() => {
count = vscode.window.visibleNotebookEditors.length;
}));
await vscode.window.showNotebookDocument(resource, { viewColumn: vscode.ViewColumn.Active });
assert.strictEqual(count, 1);
await vscode.window.showNotebookDocument(resource, { viewColumn: vscode.ViewColumn.Beside });
assert.strictEqual(count, 2);
await utils.closeAllEditors();
assert.strictEqual(count, 0);
});
});

View file

@ -56,18 +56,13 @@ class Kernel {
}
protected async _runCell(cell: vscode.NotebookCell) {
// create a single output with exec order 1 and output is plain/text
// of either the cell itself or (iff empty) the cell's document's uri
const task = this.controller.createNotebookCellExecution(cell);
task.start();
task.executionOrder = 1;
if (cell.notebook.uri.path.endsWith('customRenderer.vsctestnb')) {
await task.replaceOutput([new vscode.NotebookCellOutput([
vscode.NotebookCellOutputItem.text('test', 'text/custom')
])]);
return;
}
await task.replaceOutput([new vscode.NotebookCellOutput([
vscode.NotebookCellOutputItem.text('my output', 'text/plain')
vscode.NotebookCellOutputItem.text(cell.document.getText() || cell.document.uri.toString(), 'text/plain')
])]);
task.end(true);
}
@ -161,40 +156,12 @@ suite('Notebook API tests', function () {
suiteDisposables.push(vscode.workspace.registerNotebookContentProvider('notebookCoreTest', apiTestContentProvider));
});
let kernel1: Kernel;
let kernel2: Kernel;
let defaultKernel: Kernel;
setup(async function () {
kernel1 = new Kernel('mainKernel', 'Notebook Primary Test Kernel');
const listener = vscode.workspace.onDidOpenNotebookDocument(async notebook => {
if (notebook.notebookType === kernel1.controller.notebookType) {
await vscode.commands.executeCommand('notebook.selectKernel', {
extension: 'vscode.vscode-api-tests',
id: kernel1.controller.id
});
}
});
kernel2 = new class extends Kernel {
constructor() {
super('secondaryKernel', 'Notebook Secondary Test Kernel');
this.controller.supportsExecutionOrder = false;
}
override async _runCell(cell: vscode.NotebookCell) {
const task = this.controller.createNotebookCellExecution(cell);
task.start();
await task.replaceOutput([new vscode.NotebookCellOutput([
vscode.NotebookCellOutputItem.text('my second output', 'text/plain')
])]);
task.end(true);
}
};
testDisposables.push(kernel1.controller, listener, kernel2.controller);
// there should be ONE default kernel in this suite
defaultKernel = new Kernel('mainKernel', 'Notebook Default Kernel');
testDisposables.push(defaultKernel.controller);
await saveAllFilesAndCloseAll();
});
@ -204,35 +171,6 @@ suite('Notebook API tests', function () {
await saveAllFilesAndCloseAll();
});
test('editor onDidChangeVisibleNotebookEditors-event', async function () {
const resource = await createRandomNotebookFile();
const firstEditorOpen = asPromise(vscode.window.onDidChangeVisibleNotebookEditors);
await vscode.window.showNotebookDocument(resource);
await firstEditorOpen;
const firstEditorClose = asPromise(vscode.window.onDidChangeVisibleNotebookEditors);
await closeAllEditors();
await firstEditorClose;
});
test('editor onDidChangeVisibleNotebookEditors-event 2', async function () {
const resource = await createRandomNotebookFile();
let count = 0;
const disposables: vscode.Disposable[] = [];
disposables.push(vscode.window.onDidChangeVisibleNotebookEditors(() => {
count = vscode.window.visibleNotebookEditors.length;
}));
await vscode.window.showNotebookDocument(resource, { viewColumn: vscode.ViewColumn.Active });
assert.strictEqual(count, 1);
await vscode.window.showNotebookDocument(resource, { viewColumn: vscode.ViewColumn.Beside });
assert.strictEqual(count, 2);
await closeAllEditors();
assert.strictEqual(count, 0);
});
test('correct cell selection on undo/redo of cell creation', async function () {
const notebook = await openRandomNotebookDocument();
await vscode.window.showNotebookDocument(notebook);
@ -252,23 +190,21 @@ suite('Notebook API tests', function () {
assert.strictEqual(selectionRedo[0].end, 2);
});
test('editor editing event 2', async function () {
// const notebook = await openRandomNotebookDocument();
// await vscode.window.showNotebookDocument(notebook);
const resource = await createRandomNotebookFile();
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
test('editor editing event', async function () {
const notebook = await openRandomNotebookDocument();
const editor = await vscode.window.showNotebookDocument(notebook);
const cellsChangeEvent = asPromise<vscode.NotebookCellsChangeEvent>(vscode.notebooks.onDidChangeNotebookCells);
await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
const cellChangeEventRet = await cellsChangeEvent;
assert.strictEqual(cellChangeEventRet.document, vscode.window.activeNotebookEditor?.document);
assert.strictEqual(cellChangeEventRet.document, editor.document);
assert.strictEqual(cellChangeEventRet.changes.length, 1);
assert.deepStrictEqual(cellChangeEventRet.changes[0], {
start: 1,
deletedCount: 0,
deletedItems: [],
items: [
vscode.window.activeNotebookEditor!.document.cellAt(1)
editor.document.cellAt(1)
]
});
@ -280,8 +216,8 @@ suite('Notebook API tests', function () {
await vscode.commands.executeCommand('notebook.cell.execute');
const cellOutputsAddedRet = await cellOutputChange;
assert.deepStrictEqual(cellOutputsAddedRet, {
document: vscode.window.activeNotebookEditor!.document,
cells: [vscode.window.activeNotebookEditor!.document.cellAt(0)]
document: editor.document,
cells: [editor.document.cellAt(0)]
});
assert.strictEqual(cellOutputsAddedRet.cells[0].outputs.length, 1);
@ -289,8 +225,8 @@ suite('Notebook API tests', function () {
await vscode.commands.executeCommand('notebook.cell.clearOutputs');
const cellOutputsCleardRet = await cellOutputClear;
assert.deepStrictEqual(cellOutputsCleardRet, {
document: vscode.window.activeNotebookEditor!.document,
cells: [vscode.window.activeNotebookEditor!.document.cellAt(0)]
document: editor.document,
cells: [editor.document.cellAt(0)]
});
assert.strictEqual(cellOutputsAddedRet.cells[0].outputs.length, 0);
@ -304,111 +240,45 @@ suite('Notebook API tests', function () {
// });
});
test('editor move cell event', async function () {
const resource = await createRandomNotebookFile();
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
// const notebook = await openRandomNotebookDocument();
// await vscode.window.showNotebookDocument(notebook);
await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove');
await vscode.commands.executeCommand('notebook.focusTop');
const activeCell = getFocusedCell(vscode.window.activeNotebookEditor);
assert.strictEqual(vscode.window.activeNotebookEditor!.document.getCells().indexOf(activeCell!), 0);
const moveChange = asPromise(vscode.notebooks.onDidChangeNotebookCells);
await vscode.commands.executeCommand('notebook.cell.moveDown');
await moveChange;
await saveAllEditors();
await closeAllEditors();
// await vscode.window.showNotebookDocument(notebook);
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
const firstEditor = vscode.window.activeNotebookEditor;
assert.strictEqual(firstEditor?.document.cellCount, 2);
});
test('notebook editor active/visible', async function () {
const resource = await createRandomNotebookFile();
const firstEditor = await vscode.window.showNotebookDocument(resource, { viewColumn: vscode.ViewColumn.Active });
assert.strictEqual(firstEditor === vscode.window.activeNotebookEditor, true);
assert.strictEqual(vscode.window.visibleNotebookEditors.includes(firstEditor), true);
const secondEditor = await vscode.window.showNotebookDocument(resource, { viewColumn: vscode.ViewColumn.Beside });
assert.strictEqual(secondEditor === vscode.window.activeNotebookEditor, true);
assert.notStrictEqual(firstEditor, secondEditor);
assert.strictEqual(vscode.window.visibleNotebookEditors.includes(secondEditor), true);
assert.strictEqual(vscode.window.visibleNotebookEditors.includes(firstEditor), true);
assert.strictEqual(vscode.window.visibleNotebookEditors.length, 2);
const untitledEditorChange = asPromise(vscode.window.onDidChangeActiveNotebookEditor);
await vscode.commands.executeCommand('workbench.action.files.newUntitledFile');
await untitledEditorChange;
assert.strictEqual(firstEditor && vscode.window.visibleNotebookEditors.indexOf(firstEditor) >= 0, true);
assert.notStrictEqual(firstEditor, vscode.window.activeNotebookEditor);
assert.strictEqual(secondEditor && vscode.window.visibleNotebookEditors.indexOf(secondEditor) < 0, true);
assert.notStrictEqual(secondEditor, vscode.window.activeNotebookEditor);
assert.strictEqual(vscode.window.visibleNotebookEditors.length, 1);
const activeEditorClose = asPromise(vscode.window.onDidChangeActiveNotebookEditor);
await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
await activeEditorClose;
assert.strictEqual(secondEditor, vscode.window.activeNotebookEditor);
assert.strictEqual(vscode.window.visibleNotebookEditors.length, 2);
assert.strictEqual(secondEditor && vscode.window.visibleNotebookEditors.indexOf(secondEditor) >= 0, true);
});
test('notebook active editor change', async function () {
const firstEditorOpen = asPromise(vscode.window.onDidChangeActiveNotebookEditor);
const notebook = await openRandomNotebookDocument();
await vscode.window.showNotebookDocument(notebook);
await firstEditorOpen;
const firstEditorDeactivate = asPromise(vscode.window.onDidChangeActiveNotebookEditor);
await vscode.commands.executeCommand('workbench.action.splitEditor');
await firstEditorDeactivate;
});
test('edit API batch edits', async function () {
const resource = await createRandomNotebookFile();
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
const notebook = await openRandomNotebookDocument();
const editor = await vscode.window.showNotebookDocument(notebook);
const cellsChangeEvent = asPromise<vscode.NotebookCellsChangeEvent>(vscode.notebooks.onDidChangeNotebookCells);
const cellMetadataChangeEvent = asPromise<vscode.NotebookCellMetadataChangeEvent>(vscode.notebooks.onDidChangeCellMetadata);
const version = vscode.window.activeNotebookEditor!.document.version;
await vscode.window.activeNotebookEditor!.edit(editBuilder => {
const version = editor.document.version;
await editor.edit(editBuilder => {
editBuilder.replaceCells(1, 0, [{ kind: vscode.NotebookCellKind.Code, languageId: 'javascript', value: 'test 2', outputs: [], metadata: undefined }]);
editBuilder.replaceCellMetadata(0, { inputCollapsed: false });
});
await cellsChangeEvent;
await cellMetadataChangeEvent;
assert.strictEqual(version + 1, vscode.window.activeNotebookEditor!.document.version);
assert.strictEqual(version + 1, editor.document.version);
});
test('edit API batch edits undo/redo', async function () {
const notebook = await openRandomNotebookDocument();
await vscode.window.showNotebookDocument(notebook);
const editor = await vscode.window.showNotebookDocument(notebook);
const cellsChangeEvent = asPromise<vscode.NotebookCellsChangeEvent>(vscode.notebooks.onDidChangeNotebookCells);
const cellMetadataChangeEvent = asPromise<vscode.NotebookCellMetadataChangeEvent>(vscode.notebooks.onDidChangeCellMetadata);
const version = vscode.window.activeNotebookEditor!.document.version;
await vscode.window.activeNotebookEditor!.edit(editBuilder => {
const version = editor.document.version;
await editor.edit(editBuilder => {
editBuilder.replaceCells(1, 0, [{ kind: vscode.NotebookCellKind.Code, languageId: 'javascript', value: 'test 2', outputs: [], metadata: undefined }]);
editBuilder.replaceCellMetadata(0, { inputCollapsed: false });
});
await cellsChangeEvent;
await cellMetadataChangeEvent;
assert.strictEqual(vscode.window.activeNotebookEditor!.document.cellCount, 3);
assert.strictEqual(vscode.window.activeNotebookEditor!.document.cellAt(0)?.metadata.inputCollapsed, false);
assert.strictEqual(version + 1, vscode.window.activeNotebookEditor!.document.version);
assert.strictEqual(editor.document.cellCount, 3);
assert.strictEqual(editor.document.cellAt(0)?.metadata.inputCollapsed, false);
assert.strictEqual(version + 1, editor.document.version);
await vscode.commands.executeCommand('undo');
assert.strictEqual(version + 2, vscode.window.activeNotebookEditor!.document.version);
assert.strictEqual(vscode.window.activeNotebookEditor!.document.cellAt(0)?.metadata.inputCollapsed, undefined);
assert.strictEqual(vscode.window.activeNotebookEditor!.document.cellCount, 2);
assert.strictEqual(version + 2, editor.document.version);
assert.strictEqual(editor.document.cellAt(0)?.metadata.inputCollapsed, undefined);
assert.strictEqual(editor.document.cellCount, 2);
});
test('#98841, initialzation should not emit cell change events.', async function () {
@ -422,76 +292,61 @@ suite('Notebook API tests', function () {
await vscode.window.showNotebookDocument(notebook);
assert.strictEqual(count, 0);
});
// });
// suite('notebook workflow', () => {
test('notebook open', async function () {
const notebook = await openRandomNotebookDocument();
await vscode.window.showNotebookDocument(notebook);
assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first');
assert.strictEqual(getFocusedCell(vscode.window.activeNotebookEditor)?.document.getText(), 'test');
assert.strictEqual(getFocusedCell(vscode.window.activeNotebookEditor)?.document.languageId, 'typescript');
const editor = await vscode.window.showNotebookDocument(notebook);
assert.strictEqual(vscode.window.activeNotebookEditor === editor, true, 'notebook first');
assert.strictEqual(getFocusedCell(editor)?.document.getText(), 'test');
assert.strictEqual(getFocusedCell(editor)?.document.languageId, 'typescript');
const secondCell = vscode.window.activeNotebookEditor!.document.cellAt(1);
assert.strictEqual(secondCell!.outputs.length, 1);
assert.deepStrictEqual(secondCell!.outputs[0].metadata, { testOutputMetadata: true, ['text/plain']: { testOutputItemMetadata: true } });
assert.strictEqual(secondCell!.outputs[0].items.length, 1);
assert.strictEqual(secondCell!.outputs[0].items[0].mime, 'text/plain');
assert.strictEqual(new TextDecoder().decode(secondCell!.outputs[0].items[0].data), 'Hello World');
assert.strictEqual(secondCell!.executionSummary?.executionOrder, 5);
assert.strictEqual(secondCell!.executionSummary?.success, true);
await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
assert.strictEqual(getFocusedCell(vscode.window.activeNotebookEditor)?.document.getText(), '');
await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove');
const activeCell = getFocusedCell(vscode.window.activeNotebookEditor);
assert.notStrictEqual(getFocusedCell(vscode.window.activeNotebookEditor), undefined);
assert.strictEqual(activeCell!.document.getText(), '');
assert.strictEqual(vscode.window.activeNotebookEditor!.document.cellCount, 4);
assert.strictEqual(vscode.window.activeNotebookEditor!.document.getCells().indexOf(activeCell!), 1);
await vscode.commands.executeCommand('workbench.action.files.save');
await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
const secondCell = editor.document.cellAt(1);
assert.strictEqual(secondCell.outputs.length, 1);
assert.deepStrictEqual(secondCell.outputs[0].metadata, { testOutputMetadata: true, ['text/plain']: { testOutputItemMetadata: true } });
assert.strictEqual(secondCell.outputs[0].items.length, 1);
assert.strictEqual(secondCell.outputs[0].items[0].mime, 'text/plain');
assert.strictEqual(new TextDecoder().decode(secondCell.outputs[0].items[0].data), 'Hello World');
assert.strictEqual(secondCell.executionSummary?.executionOrder, 5);
assert.strictEqual(secondCell.executionSummary?.success, true);
});
test('notebook cell actions', async function () {
const notebook = await openRandomNotebookDocument();
await vscode.window.showNotebookDocument(notebook);
const editor = await vscode.window.showNotebookDocument(notebook);
assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first');
assert.strictEqual(getFocusedCell(vscode.window.activeNotebookEditor)?.document.getText(), 'test');
assert.strictEqual(getFocusedCell(vscode.window.activeNotebookEditor)?.document.languageId, 'typescript');
assert.strictEqual(vscode.window.activeNotebookEditor === editor, true, 'notebook first');
assert.strictEqual(getFocusedCell(editor)?.document.getText(), 'test');
assert.strictEqual(getFocusedCell(editor)?.document.languageId, 'typescript');
// ---- insert cell below and focus ---- //
await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
assert.strictEqual(getFocusedCell(vscode.window.activeNotebookEditor)?.document.getText(), '');
assert.strictEqual(getFocusedCell(editor)?.document.getText(), '');
// ---- insert cell above and focus ---- //
await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove');
let activeCell = getFocusedCell(vscode.window.activeNotebookEditor);
assert.notStrictEqual(getFocusedCell(vscode.window.activeNotebookEditor), undefined);
let activeCell = getFocusedCell(editor);
assert.notStrictEqual(getFocusedCell(editor), undefined);
assert.strictEqual(activeCell!.document.getText(), '');
assert.strictEqual(vscode.window.activeNotebookEditor!.document.cellCount, 4);
assert.strictEqual(vscode.window.activeNotebookEditor!.document.getCells().indexOf(activeCell!), 1);
assert.strictEqual(editor.document.cellCount, 4);
assert.strictEqual(editor.document.getCells().indexOf(activeCell!), 1);
// ---- focus bottom ---- //
await vscode.commands.executeCommand('notebook.focusBottom');
activeCell = getFocusedCell(vscode.window.activeNotebookEditor);
assert.strictEqual(vscode.window.activeNotebookEditor!.document.getCells().indexOf(activeCell!), 3);
activeCell = getFocusedCell(editor);
assert.strictEqual(editor.document.getCells().indexOf(activeCell!), 3);
// ---- focus top and then copy down ---- //
await vscode.commands.executeCommand('notebook.focusTop');
activeCell = getFocusedCell(vscode.window.activeNotebookEditor);
assert.strictEqual(vscode.window.activeNotebookEditor!.document.getCells().indexOf(activeCell!), 0);
activeCell = getFocusedCell(editor);
assert.strictEqual(editor.document.getCells().indexOf(activeCell!), 0);
await vscode.commands.executeCommand('notebook.cell.copyDown');
activeCell = getFocusedCell(vscode.window.activeNotebookEditor);
assert.strictEqual(vscode.window.activeNotebookEditor!.document.getCells().indexOf(activeCell!), 1);
activeCell = getFocusedCell(editor);
assert.strictEqual(editor.document.getCells().indexOf(activeCell!), 1);
assert.strictEqual(activeCell?.document.getText(), 'test');
{
const focusedCell = getFocusedCell(vscode.window.activeNotebookEditor);
const focusedCell = getFocusedCell(editor);
assert.strictEqual(focusedCell !== undefined, true);
// delete focused cell
const edit = new vscode.WorkspaceEdit();
@ -499,45 +354,44 @@ suite('Notebook API tests', function () {
await vscode.workspace.applyEdit(edit);
}
activeCell = getFocusedCell(vscode.window.activeNotebookEditor);
assert.strictEqual(vscode.window.activeNotebookEditor!.document.getCells().indexOf(activeCell!), 1);
activeCell = getFocusedCell(editor);
assert.strictEqual(editor.document.getCells().indexOf(activeCell!), 1);
assert.strictEqual(activeCell?.document.getText(), '');
// ---- focus top and then copy up ---- //
await vscode.commands.executeCommand('notebook.focusTop');
await vscode.commands.executeCommand('notebook.cell.copyUp');
assert.strictEqual(vscode.window.activeNotebookEditor!.document.cellCount, 5);
assert.strictEqual(vscode.window.activeNotebookEditor!.document.cellAt(0).document.getText(), 'test');
assert.strictEqual(vscode.window.activeNotebookEditor!.document.cellAt(1).document.getText(), 'test');
assert.strictEqual(vscode.window.activeNotebookEditor!.document.cellAt(2).document.getText(), '');
assert.strictEqual(vscode.window.activeNotebookEditor!.document.cellAt(3).document.getText(), '');
activeCell = getFocusedCell(vscode.window.activeNotebookEditor);
assert.strictEqual(vscode.window.activeNotebookEditor!.document.getCells().indexOf(activeCell!), 0);
assert.strictEqual(editor.document.cellCount, 5);
assert.strictEqual(editor.document.cellAt(0).document.getText(), 'test');
assert.strictEqual(editor.document.cellAt(1).document.getText(), 'test');
assert.strictEqual(editor.document.cellAt(2).document.getText(), '');
assert.strictEqual(editor.document.cellAt(3).document.getText(), '');
activeCell = getFocusedCell(editor);
assert.strictEqual(editor.document.getCells().indexOf(activeCell!), 0);
// ---- move up and down ---- //
await vscode.commands.executeCommand('notebook.cell.moveDown');
assert.strictEqual(vscode.window.activeNotebookEditor!.document.getCells().indexOf(getFocusedCell(vscode.window.activeNotebookEditor)!), 1,
`first move down, active cell ${getFocusedCell(vscode.window.activeNotebookEditor)!.document.uri.toString()}, ${getFocusedCell(vscode.window.activeNotebookEditor)!.document.getText()}`);
assert.strictEqual(editor.document.getCells().indexOf(getFocusedCell(editor)!), 1,
`first move down, active cell ${getFocusedCell(editor)!.document.uri.toString()}, ${getFocusedCell(editor)!.document.getText()}`);
await vscode.commands.executeCommand('workbench.action.files.save');
await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
});
test('move cells will not recreate cells in ExtHost', async function () {
test('editor move command - event and move cells will not recreate cells in ExtHost (#98126)', async function () {
const notebook = await openRandomNotebookDocument();
await vscode.window.showNotebookDocument(notebook);
await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove');
await vscode.commands.executeCommand('notebook.focusTop');
const editor = await vscode.window.showNotebookDocument(notebook);
const activeCell = getFocusedCell(vscode.window.activeNotebookEditor);
assert.strictEqual(vscode.window.activeNotebookEditor!.document.getCells().indexOf(activeCell!), 0);
await vscode.commands.executeCommand('notebook.cell.moveDown');
const activeCell = getFocusedCell(editor);
assert.strictEqual(activeCell?.index, 0);
const moveChange = asPromise(vscode.notebooks.onDidChangeNotebookCells);
await vscode.commands.executeCommand('notebook.cell.moveDown');
assert.ok(await moveChange);
const newActiveCell = getFocusedCell(vscode.window.activeNotebookEditor);
const newActiveCell = getFocusedCell(editor);
assert.strictEqual(newActiveCell?.index, 1);
assert.deepStrictEqual(activeCell, newActiveCell);
});
@ -674,18 +528,35 @@ suite('Notebook API tests', function () {
const cell = editor.document.cellAt(0);
const alternativeKernel = new class extends Kernel {
constructor() {
super('secondaryKernel', 'Notebook Secondary Test Kernel');
this.controller.supportsExecutionOrder = false;
}
override async _runCell(cell: vscode.NotebookCell) {
const task = this.controller.createNotebookCellExecution(cell);
task.start();
await task.replaceOutput([new vscode.NotebookCellOutput([
vscode.NotebookCellOutputItem.text('my second output', 'text/plain')
])]);
task.end(true);
}
};
testDisposables.push(alternativeKernel.controller);
await withEvent<vscode.NotebookCellOutputsChangeEvent>(vscode.notebooks.onDidChangeCellOutputs, async (event) => {
await assertKernel(kernel1, notebook);
await assertKernel(defaultKernel, notebook);
await vscode.commands.executeCommand('notebook.cell.execute');
await event;
assert.strictEqual(cell.outputs.length, 1, 'should execute'); // runnable, it worked
assert.strictEqual(cell.outputs[0].items.length, 1);
assert.strictEqual(cell.outputs[0].items[0].mime, 'text/plain');
assert.deepStrictEqual(new TextDecoder().decode(cell.outputs[0].items[0].data), 'my output');
assert.deepStrictEqual(new TextDecoder().decode(cell.outputs[0].items[0].data), cell.document.getText());
});
await withEvent<vscode.NotebookCellOutputsChangeEvent>(vscode.notebooks.onDidChangeCellOutputs, async (event) => {
await assertKernel(kernel2, notebook);
await assertKernel(alternativeKernel, notebook);
await vscode.commands.executeCommand('notebook.cell.execute');
await event;
assert.strictEqual(cell.outputs.length, 1, 'should execute'); // runnable, it worked
@ -1134,74 +1005,44 @@ suite('Notebook API tests', function () {
assert.strictEqual(cell.executionSummary?.timing?.endTime, 20);
});
});
suite('statusbar', () => {
const emitter = new vscode.EventEmitter<vscode.NotebookCell>();
const onDidCallProvide = emitter.event;
const suiteDisposables: vscode.Disposable[] = [];
suiteTeardown(async function () {
assertNoRpc();
suite('statusbar', () => {
const emitter = new vscode.EventEmitter<vscode.NotebookCell>();
const onDidCallProvide = emitter.event;
suiteSetup(() => {
vscode.notebooks.registerNotebookCellStatusBarItemProvider('notebookCoreTest', {
async provideCellStatusBarItems(cell: vscode.NotebookCell, _token: vscode.CancellationToken): Promise<vscode.NotebookCellStatusBarItem[]> {
emitter.fire(cell);
return [];
}
});
});
await revertAllDirty();
await closeAllEditors();
test('provideCellStatusBarItems called on metadata change', async function () {
const provideCalled = asPromise(onDidCallProvide);
const resource = await createRandomNotebookFile();
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
await provideCalled;
const edit = new vscode.WorkspaceEdit();
edit.replaceNotebookCellMetadata(resource, 0, { inputCollapsed: true });
vscode.workspace.applyEdit(edit);
await provideCalled;
});
disposeAll(suiteDisposables);
suiteDisposables.length = 0;
});
// });
suiteSetup(() => {
suiteDisposables.push(vscode.notebooks.registerNotebookCellStatusBarItemProvider('notebookCoreTest', {
async provideCellStatusBarItems(cell: vscode.NotebookCell, _token: vscode.CancellationToken): Promise<vscode.NotebookCellStatusBarItem[]> {
emitter.fire(cell);
return [];
}
}));
// suite('webview', () => {
// for web, `asWebUri` gets `https`?
// test('asWebviewUri', async function () {
// if (vscode.env.uiKind === vscode.UIKind.Web) {
// return;
// }
suiteDisposables.push(vscode.workspace.registerNotebookContentProvider('notebookCoreTest', apiTestContentProvider));
});
// const resource = await createRandomNotebookFile();
// await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
// assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first');
// const uri = vscode.window.activeNotebookEditor!.asWebviewUri(vscode.Uri.file('./hello.png'));
// assert.strictEqual(uri.scheme, 'vscode-webview-resource');
// await closeAllEditors();
// });
test('provideCellStatusBarItems called on metadata change', async function () {
const provideCalled = asPromise(onDidCallProvide);
const resource = await createRandomNotebookFile();
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
await provideCalled;
// 404 on web
// test('custom renderer message', async function () {
// if (vscode.env.uiKind === vscode.UIKind.Web) {
// return;
// }
// const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './customRenderer.vsctestnb'));
// await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
// const editor = vscode.window.activeNotebookEditor;
// const promise = new Promise(resolve => {
// const messageEmitter = editor?.onDidReceiveMessage(e => {
// if (e.type === 'custom_renderer_initialize') {
// resolve();
// messageEmitter?.dispose();
// }
// });
// });
// await vscode.commands.executeCommand('notebook.cell.execute');
// await promise;
// await closeAllEditors();
// });
const edit = new vscode.WorkspaceEdit();
edit.replaceNotebookCellMetadata(resource, 0, { inputCollapsed: true });
vscode.workspace.applyEdit(edit);
await provideCalled;
});
});
suite('Notebook API tests (metadata)', function () {

View file

@ -103,7 +103,8 @@
"@types/minimist": "^1.2.1",
"@types/mocha": "^8.2.0",
"@types/node": "14.x",
"@types/sinon": "^1.16.36",
"@types/sinon": "^10.0.2",
"@types/sinon-test": "^2.4.2",
"@types/trusted-types": "^1.0.6",
"@types/vscode-windows-registry": "^1.0.0",
"@types/webpack": "^4.41.25",
@ -183,7 +184,8 @@
"rcedit": "^1.1.0",
"request": "^2.85.0",
"rimraf": "^2.2.8",
"sinon": "^1.17.2",
"sinon": "^11.1.1",
"sinon-test": "^3.1.0",
"source-map": "0.6.1",
"source-map-support": "^0.3.2",
"style-loader": "^1.0.0",
@ -196,7 +198,7 @@
"vinyl-fs": "^3.0.0",
"vscode-debugprotocol": "1.47.0",
"vscode-nls-dev": "^3.3.1",
"vscode-telemetry-extractor": "^1.7.0",
"vscode-telemetry-extractor": "^1.8.0",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.12",
"webpack-stream": "^5.2.1",

View file

@ -46,7 +46,7 @@ if %errorlevel% neq 0 exit /b %errorlevel%
:: Tests in the extension host
set ALL_PLATFORMS_API_TESTS_EXTRA_ARGS=--disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-keytar --disable-extensions --disable-workspace-trust --user-data-dir=%VSCODEUSERDATADIR%
set ALL_PLATFORMS_API_TESTS_EXTRA_ARGS=--disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-sandbox --no-cached-data --disable-updates --disable-keytar --disable-extensions --disable-workspace-trust --user-data-dir=%VSCODEUSERDATADIR%
call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-api-tests\testWorkspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\singlefolder-tests %ALL_PLATFORMS_API_TESTS_EXTRA_ARGS%
if %errorlevel% neq 0 exit /b %errorlevel%

View file

@ -193,6 +193,24 @@ export class ActionRunner extends Disposable implements IActionRunner {
export class Separator extends Action {
/**
* Joins all non-empty lists of actions with separators.
*/
public static join(...actionLists: readonly IAction[][]) {
let out: IAction[] = [];
for (const list of actionLists) {
if (!list.length) {
// skip
} else if (out.length) {
out = [...out, new Separator(), ...list];
} else {
out = list;
}
}
return out;
}
static readonly ID = 'vs.actions.separator';
constructor(label?: string) {

View file

@ -30,7 +30,7 @@ export namespace Iterable {
return iterable[Symbol.iterator]().next().value;
}
export function some<T>(iterable: Iterable<T>, predicate: (t: T) => boolean): boolean {
export function some<T>(iterable: Iterable<T>, predicate: (t: T) => unknown): boolean {
for (const element of iterable) {
if (predicate(element)) {
return true;

View file

@ -161,7 +161,7 @@ suite('Decorators', () => {
clock.tick(200);
assert.deepStrictEqual(spy.args, [[1], [5]]);
spy.reset();
spy.resetHistory();
t.report(4);
t.report(5);

View file

@ -19,7 +19,7 @@ import { ModesContentHoverWidget } from 'vs/editor/contrib/hover/modesContentHov
import { ModesGlyphHoverWidget } from 'vs/editor/contrib/hover/modesGlyphHover';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { editorHoverBackground, editorHoverBorder, editorHoverHighlight, textCodeBlockBackground, textLinkForeground, editorHoverStatusBarBackground, editorHoverForeground } from 'vs/platform/theme/common/colorRegistry';
import { editorHoverBackground, editorHoverBorder, editorHoverHighlight, textCodeBlockBackground, textLinkForeground, editorHoverStatusBarBackground, editorHoverForeground, textLinkActiveForeground } from 'vs/platform/theme/common/colorRegistry';
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
import { GotoDefinitionAtPositionEditorContribution } from 'vs/editor/contrib/gotoSymbol/link/goToDefinitionAtPosition';
@ -327,6 +327,10 @@ registerThemingParticipant((theme, collector) => {
if (link) {
collector.addRule(`.monaco-editor .monaco-hover a { color: ${link}; }`);
}
const linkHover = theme.getColor(textLinkActiveForeground);
if (linkHover) {
collector.addRule(`.monaco-editor .monaco-hover a:hover { color: ${linkHover}; }`);
}
const hoverForeground = theme.getColor(editorHoverForeground);
if (hoverForeground) {
collector.addRule(`.monaco-editor .monaco-hover { color: ${hoverForeground}; }`);

View file

@ -24,6 +24,9 @@ interface CurrentChord {
label: string | null;
}
// Skip logging for high-frequency text editing commands
const HIGH_FREQ_COMMANDS = /^(cursor|delete)/;
export abstract class AbstractKeybindingService extends Disposable implements IKeybindingService {
public _serviceBrand: undefined;
@ -263,7 +266,9 @@ export abstract class AbstractKeybindingService extends Disposable implements IK
} else {
this._commandService.executeCommand(resolveResult.commandId, resolveResult.commandArgs).then(undefined, err => this._notificationService.warn(err));
}
this._telemetryService.publicLog2<WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification>('workbenchActionExecuted', { id: resolveResult.commandId, from: 'keybinding' });
if (!HIGH_FREQ_COMMANDS.test(resolveResult.commandId)) {
this._telemetryService.publicLog2<WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification>('workbenchActionExecuted', { id: resolveResult.commandId, from: 'keybinding' });
}
}
return shouldPreventDefault;

View file

@ -9,10 +9,13 @@ import ErrorTelemetry from 'vs/platform/telemetry/browser/errorTelemetry';
import { NullAppender, ITelemetryAppender } from 'vs/platform/telemetry/common/telemetryUtils';
import * as Errors from 'vs/base/common/errors';
import * as sinon from 'sinon';
import * as sinonTest from 'sinon-test';
import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
const sinonTestFn = sinonTest(sinon);
class TestTelemetryAppender implements ITelemetryAppender {
public events: any[];
@ -85,7 +88,7 @@ class ErrorTestingSettings {
suite('TelemetryService', () => {
test('Disposing', sinon.test(function () {
test('Disposing', sinonTestFn(function () {
let testAppender = new TestTelemetryAppender();
let service = new TelemetryService({ appender: testAppender }, undefined!);
@ -98,7 +101,7 @@ suite('TelemetryService', () => {
}));
// event reporting
test('Simple event', sinon.test(function () {
test('Simple event', sinonTestFn(function () {
let testAppender = new TestTelemetryAppender();
let service = new TelemetryService({ appender: testAppender }, undefined!);
@ -111,7 +114,7 @@ suite('TelemetryService', () => {
});
}));
test('Event with data', sinon.test(function () {
test('Event with data', sinonTestFn(function () {
let testAppender = new TestTelemetryAppender();
let service = new TelemetryService({ appender: testAppender }, undefined!);
@ -193,7 +196,7 @@ suite('TelemetryService', () => {
});
});
test('enableTelemetry on by default', sinon.test(function () {
test('enableTelemetry on by default', sinonTestFn(function () {
let testAppender = new TestTelemetryAppender();
let service = new TelemetryService({ appender: testAppender }, undefined!);
@ -224,7 +227,7 @@ suite('TelemetryService', () => {
}
}
test('Error events', sinon.test(async function (this: any) {
test('Error events', sinonTestFn(async function (this: any) {
let origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler();
Errors.setUnexpectedErrorHandler(() => { });
@ -256,7 +259,7 @@ suite('TelemetryService', () => {
}
}));
// test('Unhandled Promise Error events', sinon.test(function() {
// test('Unhandled Promise Error events', sinonTestFn(function() {
//
// let origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler();
// Errors.setUnexpectedErrorHandler(() => {});
@ -285,7 +288,7 @@ suite('TelemetryService', () => {
// }
// }));
test('Handle global errors', sinon.test(async function (this: any) {
test('Handle global errors', sinonTestFn(async function (this: any) {
let errorStub = sinon.stub();
window.onerror = errorStub;
@ -313,7 +316,7 @@ suite('TelemetryService', () => {
service.dispose();
}));
test('Error Telemetry removes PII from filename with spaces', sinon.test(async function (this: any) {
test('Error Telemetry removes PII from filename with spaces', sinonTestFn(async function (this: any) {
let errorStub = sinon.stub();
window.onerror = errorStub;
let settings = new ErrorTestingSettings();
@ -336,7 +339,7 @@ suite('TelemetryService', () => {
service.dispose();
}));
test('Uncaught Error Telemetry removes PII from filename', sinon.test(function (this: any) {
test('Uncaught Error Telemetry removes PII from filename', sinonTestFn(function (this: any) {
let clock = this.clock;
let errorStub = sinon.stub();
window.onerror = errorStub;
@ -368,7 +371,7 @@ suite('TelemetryService', () => {
});
}));
test('Unexpected Error Telemetry removes PII', sinon.test(async function (this: any) {
test('Unexpected Error Telemetry removes PII', sinonTestFn(async function (this: any) {
let origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler();
Errors.setUnexpectedErrorHandler(() => { });
try {
@ -399,7 +402,7 @@ suite('TelemetryService', () => {
}
}));
test('Uncaught Error Telemetry removes PII', sinon.test(async function (this: any) {
test('Uncaught Error Telemetry removes PII', sinonTestFn(async function (this: any) {
let errorStub = sinon.stub();
window.onerror = errorStub;
let settings = new ErrorTestingSettings();
@ -426,7 +429,7 @@ suite('TelemetryService', () => {
service.dispose();
}));
test('Unexpected Error Telemetry removes PII but preserves Code file path', sinon.test(async function (this: any) {
test('Unexpected Error Telemetry removes PII but preserves Code file path', sinonTestFn(async function (this: any) {
let origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler();
Errors.setUnexpectedErrorHandler(() => { });
@ -462,7 +465,7 @@ suite('TelemetryService', () => {
}
}));
test('Uncaught Error Telemetry removes PII but preserves Code file path', sinon.test(async function (this: any) {
test('Uncaught Error Telemetry removes PII but preserves Code file path', sinonTestFn(async function (this: any) {
let errorStub = sinon.stub();
window.onerror = errorStub;
let settings = new ErrorTestingSettings();
@ -491,7 +494,7 @@ suite('TelemetryService', () => {
service.dispose();
}));
test('Unexpected Error Telemetry removes PII but preserves Code file path with node modules', sinon.test(async function (this: any) {
test('Unexpected Error Telemetry removes PII but preserves Code file path with node modules', sinonTestFn(async function (this: any) {
let origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler();
Errors.setUnexpectedErrorHandler(() => { });
@ -523,7 +526,7 @@ suite('TelemetryService', () => {
}
}));
test('Uncaught Error Telemetry removes PII but preserves Code file path', sinon.test(async function (this: any) {
test('Uncaught Error Telemetry removes PII but preserves Code file path', sinonTestFn(async function (this: any) {
let errorStub = sinon.stub();
window.onerror = errorStub;
let settings = new ErrorTestingSettings();
@ -549,7 +552,7 @@ suite('TelemetryService', () => {
}));
test('Unexpected Error Telemetry removes PII but preserves Code file path when PIIPath is configured', sinon.test(async function (this: any) {
test('Unexpected Error Telemetry removes PII but preserves Code file path when PIIPath is configured', sinonTestFn(async function (this: any) {
let origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler();
Errors.setUnexpectedErrorHandler(() => { });
@ -585,7 +588,7 @@ suite('TelemetryService', () => {
}
}));
test('Uncaught Error Telemetry removes PII but preserves Code file path when PIIPath is configured', sinon.test(async function (this: any) {
test('Uncaught Error Telemetry removes PII but preserves Code file path when PIIPath is configured', sinonTestFn(async function (this: any) {
let errorStub = sinon.stub();
window.onerror = errorStub;
let settings = new ErrorTestingSettings();
@ -614,7 +617,7 @@ suite('TelemetryService', () => {
service.dispose();
}));
test('Unexpected Error Telemetry removes PII but preserves Missing Model error message', sinon.test(async function (this: any) {
test('Unexpected Error Telemetry removes PII but preserves Missing Model error message', sinonTestFn(async function (this: any) {
let origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler();
Errors.setUnexpectedErrorHandler(() => { });
@ -650,7 +653,7 @@ suite('TelemetryService', () => {
}
}));
test('Uncaught Error Telemetry removes PII but preserves Missing Model error message', sinon.test(async function (this: any) {
test('Uncaught Error Telemetry removes PII but preserves Missing Model error message', sinonTestFn(async function (this: any) {
let errorStub = sinon.stub();
window.onerror = errorStub;
let settings = new ErrorTestingSettings();
@ -680,7 +683,7 @@ suite('TelemetryService', () => {
service.dispose();
}));
test('Unexpected Error Telemetry removes PII but preserves No Such File error message', sinon.test(async function (this: any) {
test('Unexpected Error Telemetry removes PII but preserves No Such File error message', sinonTestFn(async function (this: any) {
let origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler();
Errors.setUnexpectedErrorHandler(() => { });
@ -716,7 +719,7 @@ suite('TelemetryService', () => {
}
}));
test('Uncaught Error Telemetry removes PII but preserves No Such File error message', sinon.test(async function (this: any) {
test('Uncaught Error Telemetry removes PII but preserves No Such File error message', sinonTestFn(async function (this: any) {
let origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler();
Errors.setUnexpectedErrorHandler(() => { });
@ -754,7 +757,7 @@ suite('TelemetryService', () => {
}
}));
test('Telemetry Service sends events when enableTelemetry is on', sinon.test(function () {
test('Telemetry Service sends events when enableTelemetry is on', sinonTestFn(function () {
let testAppender = new TestTelemetryAppender();
let service = new TelemetryService({ appender: testAppender }, undefined!);

136
src/vs/vscode.d.ts vendored
View file

@ -1989,7 +1989,7 @@ declare module 'vscode' {
* { language: 'typescript', scheme: 'file' }
*
* @example <caption>A language filter that applies to all package.json paths</caption>
* { language: 'json', scheme: 'untitled', pattern: '**/package.json' }
* { language: 'json', pattern: '**/package.json' }
*/
export interface DocumentFilter {
@ -2815,9 +2815,9 @@ declare module 'vscode' {
}
/**
* The inline values provider interface defines the contract between extensions and the VS Code debugger inline values feature.
* The inline values provider interface defines the contract between extensions and the editor's debugger inline values feature.
* In this contract the provider returns inline value information for a given document range
* and VS Code shows this information in the editor at the end of lines.
* and the editor shows this information in the editor at the end of lines.
*/
export interface InlineValuesProvider {
@ -2829,7 +2829,7 @@ declare module 'vscode' {
/**
* Provide "inline value" information for a given document and range.
* VS Code calls this method whenever debugging stops in the given document.
* The editor calls this method whenever debugging stops in the given document.
* The returned inline values information is rendered in the editor at the end of lines.
*
* @param document The document for which the inline values information is needed.
@ -3646,7 +3646,7 @@ declare module 'vscode' {
* ```
*
* @see {@link SemanticTokensBuilder} for a helper to encode tokens as integers.
* *NOTE*: When doing edits, it is possible that multiple edits occur until VS Code decides to invoke the semantic tokens provider.
* *NOTE*: When doing edits, it is possible that multiple edits occur until the editor decides to invoke the semantic tokens provider.
* *NOTE*: If the provider cannot temporarily compute semantic tokens, it can indicate this by throwing an error with the message 'Busy'.
*/
provideDocumentSemanticTokens(document: TextDocument, token: CancellationToken): ProviderResult<SemanticTokens>;
@ -5556,7 +5556,7 @@ declare module 'vscode' {
/**
* Role of the widget which defines how a screen reader interacts with it.
* The role should be set in special cases when for example a tree-like element behaves like a checkbox.
* If role is not specified VS Code will pick the appropriate role automatically.
* If role is not specified the editor will pick the appropriate role automatically.
* More about aria roles can be found here https://w3c.github.io/aria/#widget_roles
*/
role?: string;
@ -5648,7 +5648,7 @@ declare module 'vscode' {
* The command must be {@link commands.getCommands known}.
*
* Note that if this is a {@link Command `Command`} object, only the {@link Command.command `command`} and {@link Command.arguments `arguments`}
* are used by VS Code.
* are used by the editor.
*/
command: string | Command | undefined;
@ -5959,13 +5959,13 @@ declare module 'vscode' {
export enum ExtensionMode {
/**
* The extension is installed normally (for example, from the marketplace
* or VSIX) in VS Code.
* or VSIX) in the editor.
*/
Production = 1,
/**
* The extension is running from an `--extensionDevelopmentPath` provided
* when launching VS Code.
* when launching the editor.
*/
Development = 2,
@ -6549,7 +6549,7 @@ declare module 'vscode' {
constructor(commandLine: string, options?: ShellExecutionOptions);
/**
* Creates a shell execution with a command and arguments. For the real execution VS Code will
* Creates a shell execution with a command and arguments. For the real execution the editor will
* construct a command line from the command and the arguments. This is subject to interpretation
* especially when it comes to quoting. If full control over the command line is needed please
* use the constructor that creates a `ShellExecution` with the full command line.
@ -6865,7 +6865,7 @@ declare module 'vscode' {
export function fetchTasks(filter?: TaskFilter): Thenable<Task[]>;
/**
* Executes a task that is managed by VS Code. The returned
* Executes a task that is managed by the editor. The returned
* task execution can be used to terminate the task.
*
* @throws When running a ShellExecution or a ProcessExecution
@ -7281,7 +7281,7 @@ declare module 'vscode' {
* @param scheme The scheme of the filesystem, for example `file` or `git`.
*
* @return `true` if the file system supports writing, `false` if it does not
* support writing (i.e. it is readonly), and `undefined` if VS Code does not
* support writing (i.e. it is readonly), and `undefined` if the editor does not
* know about the filesystem.
*/
isWritableFileSystem(scheme: string): boolean | undefined;
@ -7362,7 +7362,7 @@ declare module 'vscode' {
* Webviews are sandboxed from normal extension process, so all communication with the webview must use
* message passing. To send a message from the extension to the webview, use {@link Webview.postMessage `postMessage`}.
* To send message from the webview back to an extension, use the `acquireVsCodeApi` function inside the webview
* to get a handle to VS Code's api and then call `.postMessage()`:
* to get a handle to the editor's api and then call `.postMessage()`:
*
* ```html
* <script>
@ -7384,7 +7384,7 @@ declare module 'vscode' {
/**
* Fired when the webview content posts a message.
*
* Webview content can post strings or json serializable objects back to a VS Code extension. They cannot
* Webview content can post strings or json serializable objects back to an extension. They cannot
* post `Blob`, `File`, `ImageData` and other DOM specific objects since the extension that receives the
* message does not run in a browser environment.
*/
@ -7452,7 +7452,7 @@ declare module 'vscode' {
*
* Normally the webview panel's html context is created when the panel becomes visible
* and destroyed when it is hidden. Extensions that have complex state
* or UI can set the `retainContextWhenHidden` to make VS Code keep the webview
* or UI can set the `retainContextWhenHidden` to make the editor keep the webview
* context around, even when the webview moves to a background tab. When a webview using
* `retainContextWhenHidden` becomes hidden, its scripts and other dynamic content are suspended.
* When the panel becomes visible again, the context is automatically restored
@ -7562,7 +7562,7 @@ declare module 'vscode' {
* There are two types of webview persistence:
*
* - Persistence within a session.
* - Persistence across sessions (across restarts of VS Code).
* - Persistence across sessions (across restarts of the editor).
*
* A `WebviewPanelSerializer` is only required for the second case: persisting a webview across sessions.
*
@ -7582,8 +7582,8 @@ declare module 'vscode' {
* setState({ value: oldState.value + 1 })
* ```
*
* A `WebviewPanelSerializer` extends this persistence across restarts of VS Code. When the editor is shutdown,
* VS Code will save off the state from `setState` of all webviews that have a serializer. When the
* A `WebviewPanelSerializer` extends this persistence across restarts of the editor. When the editor is shutdown,
* it will save off the state from `setState` of all webviews that have a serializer. When the
* webview first becomes visible after the restart, this state is passed to `deserializeWebviewPanel`.
* The extension can then restore the old `WebviewPanel` from this state.
*
@ -7678,7 +7678,7 @@ declare module 'vscode' {
/**
* Persisted state from the webview content.
*
* To save resources, VS Code normally deallocates webview documents (the iframe content) that are not visible.
* To save resources, the editor normally deallocates webview documents (the iframe content) that are not visible.
* For example, when the user collapse a view or switches to another top level activity in the sidebar, the
* `WebviewView` itself is kept alive but the webview's underlying document is deallocated. It is recreated when
* the view becomes visible again.
@ -7701,7 +7701,7 @@ declare module 'vscode' {
* setState({ value: oldState.value + 1 })
* ```
*
* VS Code ensures that the persisted state is saved correctly when a webview is hidden and across
* The editor ensures that the persisted state is saved correctly when a webview is hidden and across
* editor restarts.
*/
readonly state: T | undefined;
@ -7731,7 +7731,7 @@ declare module 'vscode' {
* Provider for text based custom editors.
*
* Text based custom editors use a {@link TextDocument `TextDocument`} as their data model. This considerably simplifies
* implementing a custom editor as it allows VS Code to handle many common operations such as
* implementing a custom editor as it allows the editor to handle many common operations such as
* undo and backup. The provider is responsible for synchronizing text changes between the webview and the `TextDocument`.
*/
export interface CustomTextEditorProvider {
@ -7762,7 +7762,7 @@ declare module 'vscode' {
* Represents a custom document used by a {@link CustomEditorProvider `CustomEditorProvider`}.
*
* Custom documents are only used within a given `CustomEditorProvider`. The lifecycle of a `CustomDocument` is
* managed by VS Code. When no more references remain to a `CustomDocument`, it is disposed of.
* managed by the editor. When no more references remain to a `CustomDocument`, it is disposed of.
*/
interface CustomDocument {
/**
@ -7773,14 +7773,14 @@ declare module 'vscode' {
/**
* Dispose of the custom document.
*
* This is invoked by VS Code when there are no more references to a given `CustomDocument` (for example when
* This is invoked by the editor when there are no more references to a given `CustomDocument` (for example when
* all editors associated with the document have been closed.)
*/
dispose(): void;
}
/**
* Event triggered by extensions to signal to VS Code that an edit has occurred on an {@link CustomDocument `CustomDocument`}.
* Event triggered by extensions to signal to the editor that an edit has occurred on an {@link CustomDocument `CustomDocument`}.
*
* @see {@link CustomEditorProvider.onDidChangeCustomDocument `CustomEditorProvider.onDidChangeCustomDocument`}.
*/
@ -7794,18 +7794,18 @@ declare module 'vscode' {
/**
* Undo the edit operation.
*
* This is invoked by VS Code when the user undoes this edit. To implement `undo`, your
* This is invoked by the editor when the user undoes this edit. To implement `undo`, your
* extension should restore the document and editor to the state they were in just before this
* edit was added to VS Code's internal edit stack by `onDidChangeCustomDocument`.
* edit was added to the editor's internal edit stack by `onDidChangeCustomDocument`.
*/
undo(): Thenable<void> | void;
/**
* Redo the edit operation.
*
* This is invoked by VS Code when the user redoes this edit. To implement `redo`, your
* This is invoked by the editor when the user redoes this edit. To implement `redo`, your
* extension should restore the document and editor to the state they were in just after this
* edit was added to VS Code's internal edit stack by `onDidChangeCustomDocument`.
* edit was added to the editor's internal edit stack by `onDidChangeCustomDocument`.
*/
redo(): Thenable<void> | void;
@ -7818,7 +7818,7 @@ declare module 'vscode' {
}
/**
* Event triggered by extensions to signal to VS Code that the content of a {@link CustomDocument `CustomDocument`}
* Event triggered by extensions to signal to the editor that the content of a {@link CustomDocument `CustomDocument`}
* has changed.
*
* @see {@link CustomEditorProvider.onDidChangeCustomDocument `CustomEditorProvider.onDidChangeCustomDocument`}.
@ -7844,7 +7844,7 @@ declare module 'vscode' {
/**
* Delete the current backup.
*
* This is called by VS Code when it is clear the current backup is no longer needed, such as when a new backup
* This is called by the editor when it is clear the current backup is no longer needed, such as when a new backup
* is made or when the file is saved.
*/
delete(): void;
@ -7955,14 +7955,14 @@ declare module 'vscode' {
* anything from changing some text, to cropping an image, to reordering a list. Your extension is free to
* define what an edit is and what data is stored on each edit.
*
* Firing `onDidChange` causes VS Code to mark the editors as being dirty. This is cleared when the user either
* Firing `onDidChange` causes the editors to be marked as being dirty. This is cleared when the user either
* saves or reverts the file.
*
* Editors that support undo/redo must fire a `CustomDocumentEditEvent` whenever an edit happens. This allows
* users to undo and redo the edit using VS Code's standard VS Code keyboard shortcuts. VS Code will also mark
* users to undo and redo the edit using the editor's standard keyboard shortcuts. The editor will also mark
* the editor as no longer being dirty if the user undoes all edits to the last saved state.
*
* Editors that support editing but cannot use VS Code's standard undo/redo mechanism must fire a `CustomDocumentContentChangeEvent`.
* Editors that support editing but cannot use the editor's standard undo/redo mechanism must fire a `CustomDocumentContentChangeEvent`.
* The only way for a user to clear the dirty state of an editor that does not support undo/redo is to either
* `save` or `revert` the file.
*
@ -7973,7 +7973,7 @@ declare module 'vscode' {
/**
* Save a custom document.
*
* This method is invoked by VS Code when the user saves a custom editor. This can happen when the user
* This method is invoked by the editor when the user saves a custom editor. This can happen when the user
* triggers save while the custom editor is active, by commands such as `save all`, or by auto save if enabled.
*
* To implement `save`, the implementer must persist the custom editor. This usually means writing the
@ -7990,7 +7990,7 @@ declare module 'vscode' {
/**
* Save a custom document to a different location.
*
* This method is invoked by VS Code when the user triggers 'save as' on a custom editor. The implementer must
* This method is invoked by the editor when the user triggers 'save as' on a custom editor. The implementer must
* persist the custom editor to `destination`.
*
* When the user accepts save as, the current editor is be replaced by an non-dirty editor for the newly saved file.
@ -8006,8 +8006,8 @@ declare module 'vscode' {
/**
* Revert a custom document to its last saved state.
*
* This method is invoked by VS Code when the user triggers `File: Revert File` in a custom editor. (Note that
* this is only used using VS Code's `File: Revert File` command and not on a `git revert` of the file).
* This method is invoked by the editor when the user triggers `File: Revert File` in a custom editor. (Note that
* this is only used using the editor's `File: Revert File` command and not on a `git revert` of the file).
*
* To implement `revert`, the implementer must make sure all editor instances (webviews) for `document`
* are displaying the document in the same state is saved in. This usually means reloading the file from the
@ -8025,7 +8025,7 @@ declare module 'vscode' {
*
* Backups are used for hot exit and to prevent data loss. Your `backup` method should persist the resource in
* its current state, i.e. with the edits applied. Most commonly this means saving the resource to disk in
* the `ExtensionContext.storagePath`. When VS Code reloads and your custom editor is opened for a resource,
* the `ExtensionContext.storagePath`. When the editor reloads and your custom editor is opened for a resource,
* your extension should first check to see if any backups exist for the resource. If there is a backup, your
* extension should load the file contents from there instead of from the resource in the workspace.
*
@ -8039,7 +8039,7 @@ declare module 'vscode' {
* @param cancellation Token that signals the current backup since a new backup is coming in. It is up to your
* extension to decided how to respond to cancellation. If for example your extension is backing up a large file
* in an operation that takes time to complete, your extension may decide to finish the ongoing backup rather
* than cancelling it to ensure that VS Code has some valid backup.
* than cancelling it to ensure that the editor has some valid backup.
*/
backupCustomDocument(document: T, context: CustomDocumentBackupContext, cancellation: CancellationToken): Thenable<CustomDocumentBackup>;
}
@ -8193,7 +8193,7 @@ declare module 'vscode' {
*
* If the extension is running remotely, this function automatically establishes a port forwarding tunnel
* from the local machine to `target` on the remote and returns a local uri to the tunnel. The lifetime of
* the port forwarding tunnel is managed by VS Code and the tunnel can be closed by the user.
* the port forwarding tunnel is managed by the editor and the tunnel can be closed by the user.
*
* *Note* that uris passed through `openExternal` are automatically resolved and you should not call `asExternalUri` on them.
*
@ -9357,7 +9357,7 @@ declare module 'vscode' {
cwd?: string | Uri;
/**
* Object with environment variables that will be added to the VS Code process.
* Object with environment variables that will be added to the editor process.
*/
env?: { [key: string]: string | null | undefined };
@ -10306,15 +10306,15 @@ declare module 'vscode' {
/**
* Namespace for dealing with the current workspace. A workspace is the collection of one
* or more folders that are opened in a VS Code window (instance).
* or more folders that are opened in an editor window (instance).
*
* It is also possible to open VS Code without a workspace. For example, when you open a
* new VS Code window by selecting a file from your platform's File menu, you will not be
* inside a workspace. In this mode, some of VS Code's capabilities are reduced but you can
* It is also possible to open an editor without a workspace. For example, when you open a
* new editor window by selecting a file from your platform's File menu, you will not be
* inside a workspace. In this mode, some of the editor's capabilities are reduced but you can
* still open text files and edit them.
*
* Refer to https://code.visualstudio.com/docs/editor/workspaces for more information on
* the concept of workspaces in VS Code.
* the concept of workspaces.
*
* The workspace offers support for {@link workspace.createFileSystemWatcher listening} to fs
* events and for {@link workspace.findFiles finding} files. Both perform well and run _outside_
@ -10331,22 +10331,22 @@ declare module 'vscode' {
export const fs: FileSystem;
/**
* The workspace folder that is open in VS Code. `undefined` when no workspace
* The workspace folder that is open in the editor. `undefined` when no workspace
* has been opened.
*
* Refer to https://code.visualstudio.com/docs/editor/workspaces for more information
* on workspaces in VS Code.
* on workspaces.
*
* @deprecated Use {@link workspace.workspaceFolders `workspaceFolders`} instead.
*/
export const rootPath: string | undefined;
/**
* List of workspace folders that are open in VS Code. `undefined` when no workspace
* List of workspace folders that are open in the editor. `undefined` when no workspace
* has been opened.
*
* Refer to https://code.visualstudio.com/docs/editor/workspaces for more information
* on workspaces in VS Code.
* on workspaces.
*
* *Note* that the first entry corresponds to the value of `rootPath`.
*/
@ -10357,7 +10357,7 @@ declare module 'vscode' {
* has been opened.
*
* Refer to https://code.visualstudio.com/docs/editor/workspaces for more information on
* the concept of workspaces in VS Code.
* the concept of workspaces.
*/
export const name: string | undefined;
@ -10386,7 +10386,7 @@ declare module 'vscode' {
* ```
*
* Refer to https://code.visualstudio.com/docs/editor/workspaces for more information on
* the concept of workspaces in VS Code.
* the concept of workspaces.
*
* **Note:** it is not advised to use `workspace.workspaceFile` to write
* configuration data into the file. You can use `workspace.getConfiguration().update()`
@ -11079,7 +11079,7 @@ declare module 'vscode' {
/**
* Register a provider that locates evaluatable expressions in text documents.
* VS Code will evaluate the expression in the active debug session and will show the result in the debug hover.
* The editor will evaluate the expression in the active debug session and will show the result in the debug hover.
*
* If multiple providers are registered for a language an arbitrary provider will be used.
*
@ -11091,7 +11091,7 @@ declare module 'vscode' {
/**
* Register a provider that returns data for the debugger's 'inline value' feature.
* Whenever the generic VS Code debugger has stopped in a source file, providers registered for the language of the file
* Whenever the generic debugger has stopped in a source file, providers registered for the language of the file
* are called to return textual data that will be shown in the editor at the end of lines.
*
* Multiple providers can be registered for a language. In that case providers are asked in
@ -12074,7 +12074,7 @@ declare module 'vscode' {
* The command must be {@link commands.getCommands known}.
*
* Note that if this is a {@link Command `Command`} object, only the {@link Command.command `command`} and {@link Command.arguments `arguments`}
* are used by VS Code.
* are used by the editor.
*/
command?: string | Command;
@ -12496,10 +12496,10 @@ declare module 'vscode' {
customRequest(command: string, args?: any): Thenable<any>;
/**
* Maps a VS Code breakpoint to the corresponding Debug Adapter Protocol (DAP) breakpoint that is managed by the debug adapter of the debug session.
* If no DAP breakpoint exists (either because the VS Code breakpoint was not yet registered or because the debug adapter is not interested in the breakpoint), the value `undefined` is returned.
* Maps a breakpoint in the editor to the corresponding Debug Adapter Protocol (DAP) breakpoint that is managed by the debug adapter of the debug session.
* If no DAP breakpoint exists (either because the editor breakpoint was not yet registered or because the debug adapter is not interested in the breakpoint), the value `undefined` is returned.
*
* @param breakpoint A VS Code {@link Breakpoint}.
* @param breakpoint A {@link Breakpoint} in the editor.
* @return A promise that resolves to the Debug Adapter Protocol breakpoint or `undefined`.
*/
getDebugProtocolBreakpoint(breakpoint: Breakpoint): Thenable<DebugProtocolBreakpoint | undefined>;
@ -12588,7 +12588,7 @@ declare module 'vscode' {
/**
* The command or path of the debug adapter executable.
* A command must be either an absolute path of an executable or the name of an command to be looked up via the PATH environment variable.
* The special value 'node' will be mapped to VS Code's built-in Node.js runtime.
* The special value 'node' will be mapped to the editor's built-in Node.js runtime.
*/
readonly command: string;
@ -12659,12 +12659,12 @@ declare module 'vscode' {
}
/**
* A debug adapter that implements the Debug Adapter Protocol can be registered with VS Code if it implements the DebugAdapter interface.
* A debug adapter that implements the Debug Adapter Protocol can be registered with the editor if it implements the DebugAdapter interface.
*/
export interface DebugAdapter extends Disposable {
/**
* An event which fires after the debug adapter has sent a Debug Adapter Protocol message to VS Code.
* An event which fires after the debug adapter has sent a Debug Adapter Protocol message to the editor.
* Messages can be requests, responses, or events.
*/
readonly onDidSendMessage: Event<DebugProtocolMessage>;
@ -12713,7 +12713,7 @@ declare module 'vscode' {
}
/**
* A Debug Adapter Tracker is a means to track the communication between VS Code and a Debug Adapter.
* A Debug Adapter Tracker is a means to track the communication between the editor and a Debug Adapter.
*/
export interface DebugAdapterTracker {
/**
@ -12721,11 +12721,11 @@ declare module 'vscode' {
*/
onWillStartSession?(): void;
/**
* The debug adapter is about to receive a Debug Adapter Protocol message from VS Code.
* The debug adapter is about to receive a Debug Adapter Protocol message from the editor.
*/
onWillReceiveMessage?(message: any): void;
/**
* The debug adapter has sent a Debug Adapter Protocol message to VS Code.
* The debug adapter has sent a Debug Adapter Protocol message to the editor.
*/
onDidSendMessage?(message: any): void;
/**
@ -12745,7 +12745,7 @@ declare module 'vscode' {
export interface DebugAdapterTrackerFactory {
/**
* The method 'createDebugAdapterTracker' is called at the start of a debug session in order
* to return a "tracker" object that provides read-access to the communication between VS Code and a debug adapter.
* to return a "tracker" object that provides read-access to the communication between the editor and a debug adapter.
*
* @param session The {@link DebugSession debug session} for which the debug adapter tracker will be used.
* @return A {@link DebugAdapterTracker debug adapter tracker} or undefined.
@ -13036,7 +13036,7 @@ declare module 'vscode' {
/**
* Converts a "Source" descriptor object received via the Debug Adapter Protocol into a Uri that can be used to load its contents.
* If the source descriptor is based on a path, a file Uri is returned.
* If the source descriptor uses a reference number, a specific debug Uri (scheme 'debug') is constructed that requires a corresponding VS Code ContentProvider and a running debug session
* If the source descriptor uses a reference number, a specific debug Uri (scheme 'debug') is constructed that requires a corresponding ContentProvider and a running debug session
*
* If the "Source" descriptor has insufficient information for creating the Uri, an error is thrown.
*
@ -13585,7 +13585,7 @@ declare module 'vscode' {
* quickpick to select which account they would like to use.
*
* Currently, there are only two authentication providers that are contributed from built in extensions
* to VS Code that implement GitHub and Microsoft authentication: their providerId's are 'github' and 'microsoft'.
* to the editor that implement GitHub and Microsoft authentication: their providerId's are 'github' and 'microsoft'.
* @param providerId The id of the provider to use
* @param scopes A list of scopes representing the permissions requested. These are dependent on the authentication provider
* @param options The {@link GetSessionOptions} to use
@ -13600,7 +13600,7 @@ declare module 'vscode' {
* quickpick to select which account they would like to use.
*
* Currently, there are only two authentication providers that are contributed from built in extensions
* to VS Code that implement GitHub and Microsoft authentication: their providerId's are 'github' and 'microsoft'.
* to the editor that implement GitHub and Microsoft authentication: their providerId's are 'github' and 'microsoft'.
* @param providerId The id of the provider to use
* @param scopes A list of scopes representing the permissions requested. These are dependent on the authentication provider
* @param options The {@link GetSessionOptions} to use

View file

@ -9,7 +9,7 @@
* distribution and CANNOT be used in published extensions.
*
* To test these API in local environment:
* - Use Insiders release of VS Code.
* - Use Insiders release of 'VS Code'.
* - Add `"enableProposedApi": true` to your package.json.
* - Copy this file to your project.
*/
@ -147,8 +147,8 @@ declare module 'vscode' {
/**
* Resolve the authority part of the current opened `vscode-remote://` URI.
*
* This method will be invoked once during the startup of VS Code and again each time
* VS Code detects a disconnection.
* This method will be invoked once during the startup of the editor and again each time
* the editor detects a disconnection.
*
* @param authority The authority part of the current opened `vscode-remote://` URI.
* @param context A context indicating if this is the first call or a subsequent call.
@ -438,11 +438,11 @@ declare module 'vscode' {
/**
* Additional information regarding the state of the completed search.
*
* Messages with "Information" tyle support links in markdown syntax:
* Messages with "Information" style support links in markdown syntax:
* - Click to [run a command](command:workbench.action.OpenQuickPick)
* - Click to [open a website](https://aka.ms)
*
* Commands may optionally return { triggerSearch: true } to signal to VS Code that the original search should run be agian.
* Commands may optionally return { triggerSearch: true } to signal to the editor that the original search should run be again.
*/
message?: TextSearchCompleteMessage | TextSearchCompleteMessage[];
}
@ -553,7 +553,7 @@ declare module 'vscode' {
/**
* A FileSearchProvider provides search results for files in the given folder that match a query string. It can be invoked by quickopen or other extensions.
*
* A FileSearchProvider is the more powerful of two ways to implement file search in VS Code. Use a FileSearchProvider if you wish to search within a folder for
* A FileSearchProvider is the more powerful of two ways to implement file search in the editor. Use a FileSearchProvider if you wish to search within a folder for
* all files that match the user's query.
*
* The FileSearchProvider will be invoked on every keypress in quickopen. When `workspace.findFiles` is called, it will be invoked with an empty query string,
@ -987,7 +987,7 @@ declare module 'vscode' {
* Handle when the underlying resource for a custom editor is renamed.
*
* This allows the webview for the editor be preserved throughout the rename. If this method is not implemented,
* VS Code will destory the previous custom editor and create a replacement one.
* the editor will destroy the previous custom editor and create a replacement one.
*
* @param newDocument New text document to use for the custom editor.
* @param existingWebviewPanel Webview panel for the custom editor.
@ -1352,7 +1352,7 @@ declare module 'vscode' {
/**
* Delete the current backup.
*
* This is called by VS Code when it is clear the current backup is no longer needed, such as when a new backup
* This is called by the editor when it is clear the current backup is no longer needed, such as when a new backup
* is made or when the file is saved.
*/
delete(): void;
@ -1434,11 +1434,13 @@ declare module 'vscode' {
/**
* The parameters without the return type. Render after `name`.
*/
//todo@API rename to signature
parameters?: string;
/**
* The fully qualified name, like package name or file path. Rendered after `signature`.
*/
//todo@API find better name
qualifier?: string;
/**
@ -1882,7 +1884,7 @@ declare module 'vscode' {
* disambiguate multiple sets of results in a test run. It is useful if
* tests are run across multiple platforms, for example.
* @param persist Whether the results created by the run should be
* persisted in VS Code. This may be false if the results are coming from
* persisted in the editor. This may be false if the results are coming from
* a file already saved externally, such as a coverage information file.
*/
export function createTestRun<T>(request: TestRunRequest<T>, name?: string, persist?: boolean): TestRun<T>;
@ -1901,7 +1903,7 @@ declare module 'vscode' {
export function createTestItem<T = void, TChildren = any>(options: TestItemOptions): TestItem<T, TChildren>;
/**
* List of test results stored by VS Code, sorted in descnding
* List of test results stored by the editor, sorted in descending
* order by their `completedAt` time.
* @stability experimental
*/
@ -1941,7 +1943,7 @@ declare module 'vscode' {
readonly onDidDiscoverInitialTests: Event<void>;
/**
* Dispose of the observer, allowing VS Code to eventually tell test
* Dispose of the observer, allowing the editor to eventually tell test
* providers that they no longer need to update tests.
*/
dispose(): void;
@ -2028,7 +2030,7 @@ declare module 'vscode' {
tests: TestItem<T>[];
/**
* An array of tests the user has marked as excluded in VS Code. May be
* An array of tests the user has marked as excluded in the editor. May be
* omitted if no exclusions were requested. Test controllers should not run
* excluded tests or any children of excluded tests.
*/
@ -2332,7 +2334,7 @@ declare module 'vscode' {
}
/**
* TestResults can be provided to VS Code in {@link test.publishTestResult},
* TestResults can be provided to the editor in {@link test.publishTestResult},
* or read from it in {@link test.testResults}.
*
* The results contain a 'snapshot' of the tests at the point when the test
@ -2435,7 +2437,7 @@ declare module 'vscode' {
* if an opener should be selected automatically or if the user should be prompted to
* select an opener.
*
* VS Code will try to use the best available opener, as sorted by `ExternalUriOpenerPriority`.
* The editor will try to use the best available opener, as sorted by `ExternalUriOpenerPriority`.
* If there are multiple potential "best" openers for a URI, then the user will be prompted
* to select an opener.
*/
@ -2450,21 +2452,21 @@ declare module 'vscode' {
/**
* The opener can open the uri but will not cause a prompt on its own
* since VS Code always contributes a built-in `Default` opener.
* since the editor always contributes a built-in `Default` opener.
*/
Option = 1,
/**
* The opener can open the uri.
*
* VS Code's built-in opener has `Default` priority. This means that any additional `Default`
* The editor's built-in opener has `Default` priority. This means that any additional `Default`
* openers will cause the user to be prompted to select from a list of all potential openers.
*/
Default = 2,
/**
* The opener can open the uri and should be automatically selected over any
* default openers, include the built-in one from VS Code.
* default openers, include the built-in one from the editor.
*
* A preferred opener will be automatically selected if no other preferred openers
* are available. If multiple preferred openers are available, then the user
@ -2477,7 +2479,7 @@ declare module 'vscode' {
* Handles opening uris to external resources, such as http(s) links.
*
* Extensions can implement an `ExternalUriOpener` to open `http` links to a webserver
* inside of VS Code instead of having the link be opened by the web browser.
* inside of the editor instead of having the link be opened by the web browser.
*
* Currently openers may only be registered for `http` and `https` uris.
*/
@ -2568,11 +2570,11 @@ declare module 'vscode' {
* Allows using openers contributed by extensions through `registerExternalUriOpener`
* when opening the resource.
*
* If `true`, VS Code will check if any contributed openers can handle the
* If `true`, the editor will check if any contributed openers can handle the
* uri, and fallback to the default opener behavior.
*
* If it is string, this specifies the id of the `ExternalUriOpener`
* that should be used if it is available. Use `'default'` to force VS Code's
* that should be used if it is available. Use `'default'` to force the editor's
* standard external opener to be used.
*/
readonly allowContributedOpeners?: boolean | string;
@ -2601,7 +2603,7 @@ declare module 'vscode' {
*
* If the extension is running remotely, this function automatically establishes a port forwarding tunnel
* from the local machine to `target` on the remote and returns a local uri to the tunnel. The lifetime of
* the port forwarding tunnel is managed by VS Code and the tunnel can be closed by the user.
* the port forwarding tunnel is managed by the editor and the tunnel can be closed by the user.
*
* *Note* that uris passed through `openExternal` are automatically resolved and you should not call `asExternalUri` on them.
*
@ -2638,7 +2640,7 @@ declare module 'vscode' {
* #### Any other scheme
*
* Any other scheme will be handled as if the provided URI is a workspace URI. In that case, the method will return
* a URI which, when handled, will make VS Code open the workspace.
* a URI which, when handled, will make the editor open the workspace.
*
* @return A uri that can be used on the client machine.
*/
@ -2730,7 +2732,7 @@ declare module 'vscode' {
/**
* If your extension listens on ports, consider registering a PortAttributesProvider to provide information
* about the ports. For example, a debug extension may know about debug ports in it's debuggee. By providing
* this information with a PortAttributesProvider the extension can tell VS Code that these ports should be
* this information with a PortAttributesProvider the extension can tell the editor that these ports should be
* ignored, since they don't need to be user facing.
*
* @param portSelector If registerPortAttributesProvider is called after you start your process then you may already

View file

@ -22,6 +22,7 @@ import { ViewContainerLocation, IViewDescriptorService } from 'vs/workbench/comm
import { IPaneComposite } from 'vs/workbench/common/panecomposite';
import { IComposite } from 'vs/workbench/common/composite';
import { CompositeDragAndDropData, CompositeDragAndDropObserver, IDraggedCompositeData, ICompositeDragAndDrop, Before2D, toggleDropEffect } from 'vs/workbench/browser/dnd';
import { Gesture, EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch';
export interface ICompositeBarItem {
id: string;
@ -233,6 +234,8 @@ export class CompositeBar extends Widget implements ICompositeBar {
// Contextmenu for composites
this._register(addDisposableListener(parent, EventType.CONTEXT_MENU, e => this.showContextMenu(e)));
this._register(Gesture.addTarget(parent));
this._register(addDisposableListener(parent, TouchEventType.Contextmenu, e => this.showContextMenu(e)));
let insertDropBefore: Before2D | undefined = undefined;
// Register a drop target on the whole bar to prevent forbidden feedback
@ -619,7 +622,7 @@ export class CompositeBar extends Widget implements ICompositeBar {
return this.model.visibleItems.filter(c => overflowingIds.includes(c.id)).map(item => { return { id: item.id, name: this.getAction(item.id)?.label || item.name }; });
}
private showContextMenu(e: MouseEvent): void {
private showContextMenu(e: MouseEvent | GestureEvent): void {
EventHelper.stop(e, true);
const event = new StandardMouseEvent(e);
this.contextMenuService.showContextMenu({

View file

@ -35,6 +35,7 @@ import { CompositeDragAndDropObserver } from 'vs/workbench/browser/dnd';
import { IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { CATEGORIES } from 'vs/workbench/common/actions';
import { Gesture, EventType as GestureEventType } from 'vs/base/browser/touch';
export class SidebarPart extends CompositePart<Viewlet> implements IViewletService {
@ -171,6 +172,10 @@ export class SidebarPart extends CompositePart<Viewlet> implements IViewletServi
this._register(addDisposableListener(titleArea, EventType.CONTEXT_MENU, e => {
this.onTitleAreaContextMenu(new StandardMouseEvent(e));
}));
this._register(Gesture.addTarget(titleArea));
this._register(addDisposableListener(titleArea, GestureEventType.Contextmenu, e => {
this.onTitleAreaContextMenu(new StandardMouseEvent(e));
}));
this.titleLabelElement!.draggable = true;

View file

@ -10,6 +10,7 @@ import { dispose, IDisposable, Disposable, toDisposable, MutableDisposable } fro
import { SimpleIconLabel } from 'vs/base/browser/ui/iconLabel/simpleIconLabel';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { Part } from 'vs/workbench/browser/part';
import { EventType as TouchEventType, Gesture, GestureEvent } from 'vs/base/browser/touch';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { StatusbarAlignment, IStatusbarService, IStatusbarEntry, IStatusbarEntryAccessor } from 'vs/workbench/services/statusbar/common/statusbar';
@ -586,6 +587,8 @@ export class StatusbarPart extends Part implements IStatusbarService {
// Context menu support
this._register(addDisposableListener(parent, EventType.CONTEXT_MENU, e => this.showContextMenu(e)));
this._register(Gesture.addTarget(parent));
this._register(addDisposableListener(parent, TouchEventType.Contextmenu, e => this.showContextMenu(e)));
// Initial status bar entries
this.createInitialStatusbarEntries();
@ -660,7 +663,7 @@ export class StatusbarPart extends Part implements IStatusbarService {
}
}
private showContextMenu(e: MouseEvent): void {
private showContextMenu(e: MouseEvent | GestureEvent): void {
EventHelper.stop(e, true);
const event = new StandardMouseEvent(e);

View file

@ -38,6 +38,7 @@ import { ViewPane } from 'vs/workbench/browser/parts/views/viewPane';
import { CompositeMenuActions } from 'vs/workbench/browser/actions';
import { createActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { Gesture, EventType as TouchEventType } from 'vs/base/browser/touch';
export const ViewsSubMenu = new MenuId('Views');
MenuRegistry.appendMenuItem(MenuId.ViewContainerTitle, <ISubmenuItem>{
@ -409,6 +410,8 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
this._register(this.paneview.onDidDrop(({ from, to }) => this.movePane(from as ViewPane, to as ViewPane)));
this._register(this.paneview.onDidScroll(_ => this.onDidScrollPane()));
this._register(addDisposableListener(parent, EventType.CONTEXT_MENU, (e: MouseEvent) => this.showContextMenu(new StandardMouseEvent(e))));
this._register(Gesture.addTarget(parent));
this._register(addDisposableListener(parent, TouchEventType.Contextmenu, (e: MouseEvent) => this.showContextMenu(new StandardMouseEvent(e))));
this._menuActions = this._register(this.instantiationService.createInstance(ViewContainerMenuActions, this.paneview.element, this.viewContainer));
this._register(this._menuActions.onDidChange(() => this.updateTitleArea()));

View file

@ -233,9 +233,10 @@ class FormatOnSaveParticipant implements ITextFileSaveParticipant {
const nestedProgress = new Progress<{ displayName?: string, extensionId?: ExtensionIdentifier }>(provider => {
progress.report({
message: localize(
'formatting',
"Running '{0}' Formatter ([configure](command:workbench.action.openSettings?%5B%22editor.formatOnSave%22%5D)).",
provider.displayName || provider.extensionId && provider.extensionId.value || '???'
{ key: 'formatting2', comment: ['[configure]({1}) is a link. Only translate `configure`. Do not change brackets and parentheses or {1}'] },
"Running '{0}' Formatter ([configure]({1})).",
provider.displayName || provider.extensionId && provider.extensionId.value || '???',
'command:workbench.action.openSettings?%5B%22editor.formatOnSave%22%5D'
)
});
});
@ -336,9 +337,10 @@ class CodeActionOnSaveParticipant implements ITextFileSaveParticipant {
private _report(): void {
progress.report({
message: localize(
'codeaction.get',
"Getting code actions from '{0}' ([configure](command:workbench.action.openSettings?%5B%22editor.codeActionsOnSave%22%5D)).",
[...this._names].map(name => `'${name}'`).join(', ')
{ key: 'codeaction.get2', comment: ['[configure]({1}) is a link. Only translate `configure`. Do not change brackets and parentheses or {1}'] },
"Getting code actions from '{0}' ([configure]({1})).",
[...this._names].map(name => `'${name}'`).join(', '),
'command:workbench.action.openSettings?%5B%22editor.codeActionsOnSave%22%5D'
)
});
}

View file

@ -110,7 +110,7 @@ class UntitledTextEditorHintContentWidget implements IContentWidget {
this.domNode.style.width = 'max-content';
const language = $('a.language-mode');
language.style.cursor = 'pointer';
language.innerText = localize('selectAlanguage', "Select a language");
language.innerText = localize('selectAlanguage2', "Select a language");
this.domNode.appendChild(language);
const toGetStarted = $('span');
toGetStarted.innerText = localize('toGetStarted', " to get started. Start typing to dismiss, or ",);

View file

@ -41,7 +41,7 @@ export function filterExceptionsFromTelemetry<T extends { [key: string]: unknown
export function isSessionAttach(session: IDebugSession): boolean {
return session.configuration.request === 'attach' && !getExtensionHostDebugSession(session);
return session.configuration.request === 'attach' && !getExtensionHostDebugSession(session) && (!session.parentSession || isSessionAttach(session.parentSession));
}
/**

View file

@ -487,12 +487,13 @@ export class ExplorerView extends ViewPane {
}
private setContextKeys(stat: ExplorerItem | null | undefined): void {
const isSingleFolder = this.contextService.getWorkbenchState() === WorkbenchState.FOLDER;
const resource = stat ? stat.resource : isSingleFolder ? this.contextService.getWorkspace().folders[0].uri : null;
const folders = this.contextService.getWorkspace().folders;
const resource = stat ? stat.resource : folders[folders.length - 1].uri;
stat = stat || this.explorerService.findClosest(resource);
this.resourceContext.set(resource);
this.folderContext.set((isSingleFolder && !stat) || !!stat && stat.isDirectory);
this.folderContext.set(!!stat && stat.isDirectory);
this.readonlyContext.set(!!stat && stat.isReadonly);
this.rootContext.set(!stat || (stat && stat.isRoot));
this.rootContext.set(!!stat && stat.isRoot);
if (resource) {
const overrides = resource ? this.editorOverrideService.getEditorIds(resource) : [];

View file

@ -1874,14 +1874,18 @@ registerAction2(class NotebookConfigureLayoutAction extends Action2 {
group: 'notebookLayout',
when: ContextKeyExpr.and(
NOTEBOOK_IS_ACTIVE_EDITOR,
ContextKeyExpr.notEquals('config.notebook.globalToolbar', true)
ContextKeyExpr.notEquals('config.notebook.globalToolbar', true),
ContextKeyExpr.equals('config.notebook.experimental.openGettingStarted', true)
),
order: 0
},
{
id: MenuId.NotebookToolbar,
group: 'notebookLayout',
when: ContextKeyExpr.equals('config.notebook.globalToolbar', true),
when: ContextKeyExpr.and(
ContextKeyExpr.equals('config.notebook.globalToolbar', true),
ContextKeyExpr.equals('config.notebook.experimental.openGettingStarted', true)
),
order: 0
}
]

View file

@ -65,7 +65,7 @@ class NotebookViewportContribution extends Disposable implements INotebookEditor
if (pickedMimeTypeRenderer.rendererId === BUILTIN_RENDERER_ID) {
const renderer = this._notebookEditor.getOutputRenderer().getContribution(pickedMimeTypeRenderer.mimeType);
if (renderer?.getType() === RenderOutputType.Html) {
const renderResult = renderer.render(output, output.model.outputs.filter(op => op.mime === pickedMimeTypeRenderer.mimeType), DOM.$(''), this._notebookEditor.viewModel.uri) as IInsetRenderOutput;
const renderResult = renderer.render(output, output.model.outputs.filter(op => op.mime === pickedMimeTypeRenderer.mimeType)[0], DOM.$(''), this._notebookEditor.viewModel.uri) as IInsetRenderOutput;
this._notebookEditor.createOutput(viewCell, renderResult, 0);
}
return;

View file

@ -764,7 +764,7 @@ export interface IOutputTransformContribution {
* This call is allowed to have side effects, such as placing output
* directly into the container element.
*/
render(output: ICellOutputViewModel, items: IOutputItemDto[], container: HTMLElement, notebookUri: URI): IRenderOutput;
render(output: ICellOutputViewModel, item: IOutputItemDto, container: HTMLElement, notebookUri: URI): IRenderOutput;
}
export interface CellFindMatch {

View file

@ -1385,7 +1385,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
}
}
await this._webview!.initializeMarkdown(requests.map(request => ({
await this._webview!.initializeMarkup(requests.map(request => ({
mime: 'text/markdown',
cellId: request[0].id,
cellHandle: request[0].handle,
content: request[0].getText(),
@ -1393,8 +1394,10 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
visible: false,
})));
} else {
const initRequests = viewModel.viewCells.filter(cell => cell.cellKind === CellKind.Markup).slice(0, 5).map(cell => ({ cellId: cell.id, cellHandle: cell.handle, content: cell.getText(), offset: -10000, visible: false }));
await this._webview!.initializeMarkdown(initRequests);
const initRequests = viewModel.viewCells.filter(cell => cell.cellKind === CellKind.Markup).slice(0, 5).map(cell => ({
cellId: cell.id, cellHandle: cell.handle, content: cell.getText(), offset: -10000, visible: false, mime: 'text/markdown',
}));
await this._webview!.initializeMarkup(initRequests);
// no cached view state so we are rendering the first viewport
// after above async call, we already get init height for markdown cells, we can update their offset
@ -2261,6 +2264,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
const cellTop = this._list.getAbsoluteTopOfElement(cell);
await this._webview.showMarkdownPreview({
mime: 'text/markdown',
cellHandle: cell.handle,
cellId: cell.id,
content: cell.getText(),

View file

@ -68,6 +68,6 @@ export class OutputRenderer {
return this._renderMessage(container, localize('empty', "Cell has no output"));
}
return renderer.render(viewModel, [first], container, notebookUri);
return renderer.render(viewModel, first, container, notebookUri);
}
}

View file

@ -39,13 +39,11 @@ class JavaScriptRendererContrib extends Disposable implements IOutputRendererCon
super();
}
render(output: ICellOutputViewModel, items: IOutputItemDto[], container: HTMLElement, notebookUri: URI): IRenderOutput {
let scriptVal = '';
items.forEach(item => {
const str = getStringValue(item);
scriptVal += `<script type="application/javascript">${str}</script>`;
render(output: ICellOutputViewModel, item: IOutputItemDto, container: HTMLElement, notebookUri: URI): IRenderOutput {
const str = getStringValue(item);
const scriptVal = `<script type="application/javascript">${str}</script>`;
});
return {
type: RenderOutputType.Html,
source: output,
@ -72,8 +70,8 @@ class CodeRendererContrib extends Disposable implements IOutputRendererContribut
super();
}
render(output: ICellOutputViewModel, items: IOutputItemDto[], container: HTMLElement): IRenderOutput {
const value = items.map(getStringValue).join('');
render(output: ICellOutputViewModel, item: IOutputItemDto, container: HTMLElement): IRenderOutput {
const value = getStringValue(item);
return this._render(output, container, value, 'javascript');
}
@ -106,8 +104,8 @@ class JSONRendererContrib extends CodeRendererContrib {
return ['application/json'];
}
override render(output: ICellOutputViewModel, items: IOutputItemDto[], container: HTMLElement): IRenderOutput {
const str = items.map(getStringValue).join('');
override render(output: ICellOutputViewModel, item: IOutputItemDto, container: HTMLElement): IRenderOutput {
const str = getStringValue(item);
return this._render(output, container, str, 'jsonc');
}
}
@ -130,15 +128,13 @@ class StreamRendererContrib extends Disposable implements IOutputRendererContrib
super();
}
render(output: ICellOutputViewModel, items: IOutputItemDto[], container: HTMLElement, notebookUri: URI): IRenderOutput {
render(output: ICellOutputViewModel, item: IOutputItemDto, container: HTMLElement, notebookUri: URI): IRenderOutput {
const linkDetector = this.instantiationService.createInstance(LinkDetector);
items.forEach(item => {
const text = getStringValue(item);
const contentNode = DOM.$('span.output-stream');
truncatedArrayOfString(notebookUri!, output.cellViewModel, contentNode, [text], linkDetector, this.openerService, this.themeService);
container.appendChild(contentNode);
});
const text = getStringValue(item);
const contentNode = DOM.$('span.output-stream');
truncatedArrayOfString(notebookUri, output.cellViewModel, contentNode, [text], linkDetector, this.openerService, this.themeService);
container.appendChild(contentNode);
return { type: RenderOutputType.Mainframe };
}
@ -153,8 +149,8 @@ class StderrRendererContrib extends StreamRendererContrib {
return ['application/vnd.code.notebook.stderr', 'application/x.notebook.stderr'];
}
override render(output: ICellOutputViewModel, items: IOutputItemDto[], container: HTMLElement, notebookUri: URI): IRenderOutput {
const result = super.render(output, items, container, notebookUri);
override render(output: ICellOutputViewModel, item: IOutputItemDto, container: HTMLElement, notebookUri: URI): IRenderOutput {
const result = super.render(output, item, container, notebookUri);
container.classList.add('error');
return result;
}
@ -181,35 +177,34 @@ class JSErrorRendererContrib implements IOutputRendererContribution {
return ['application/vnd.code.notebook.error'];
}
render(_output: ICellOutputViewModel, items: IOutputItemDto[], container: HTMLElement, _notebookUri: URI): IRenderOutput {
render(_output: ICellOutputViewModel, item: IOutputItemDto, container: HTMLElement, _notebookUri: URI): IRenderOutput {
const linkDetector = this._instantiationService.createInstance(LinkDetector);
type ErrorLike = Partial<Error>;
for (let item of items) {
let err: ErrorLike;
try {
err = <ErrorLike>JSON.parse(getStringValue(item));
} catch (e) {
this._logService.warn('INVALID output item (failed to parse)', e);
continue;
}
const header = document.createElement('div');
const headerMessage = err.name && err.message ? `${err.name}: ${err.message}` : err.name || err.message;
if (headerMessage) {
header.innerText = headerMessage;
container.appendChild(header);
}
const stack = document.createElement('pre');
stack.classList.add('traceback');
if (err.stack) {
stack.appendChild(handleANSIOutput(err.stack, linkDetector, this._themeService, undefined));
}
container.appendChild(stack);
container.classList.add('error');
let err: ErrorLike;
try {
err = <ErrorLike>JSON.parse(getStringValue(item));
} catch (e) {
this._logService.warn('INVALID output item (failed to parse)', e);
return { type: RenderOutputType.Mainframe };
}
const header = document.createElement('div');
const headerMessage = err.name && err.message ? `${err.name}: ${err.message}` : err.name || err.message;
if (headerMessage) {
header.innerText = headerMessage;
container.appendChild(header);
}
const stack = document.createElement('pre');
stack.classList.add('traceback');
if (err.stack) {
stack.appendChild(handleANSIOutput(err.stack, linkDetector, this._themeService, undefined));
}
container.appendChild(stack);
container.classList.add('error');
return { type: RenderOutputType.Mainframe };
}
}
@ -232,12 +227,12 @@ class PlainTextRendererContrib extends Disposable implements IOutputRendererCont
super();
}
render(output: ICellOutputViewModel, items: IOutputItemDto[], container: HTMLElement, notebookUri: URI): IRenderOutput {
render(output: ICellOutputViewModel, item: IOutputItemDto, container: HTMLElement, notebookUri: URI): IRenderOutput {
const linkDetector = this.instantiationService.createInstance(LinkDetector);
const str = items.map(getStringValue);
const str = getStringValue(item);
const contentNode = DOM.$('.output-plaintext');
truncatedArrayOfString(notebookUri!, output.cellViewModel, contentNode, str, linkDetector, this.openerService, this.themeService);
truncatedArrayOfString(notebookUri, output.cellViewModel, contentNode, [str], linkDetector, this.openerService, this.themeService);
container.appendChild(contentNode);
return { type: RenderOutputType.Mainframe, supportAppend: true };
@ -259,8 +254,8 @@ class HTMLRendererContrib extends Disposable implements IOutputRendererContribut
super();
}
render(output: ICellOutputViewModel, items: IOutputItemDto[], container: HTMLElement, notebookUri: URI): IRenderOutput {
const str = items.map(getStringValue).join('');
render(output: ICellOutputViewModel, item: IOutputItemDto, container: HTMLElement, notebookUri: URI): IRenderOutput {
const str = getStringValue(item);
return {
type: RenderOutputType.Html,
source: output,
@ -285,16 +280,14 @@ class MdRendererContrib extends Disposable implements IOutputRendererContributio
super();
}
render(output: ICellOutputViewModel, items: IOutputItemDto[], container: HTMLElement, notebookUri: URI): IRenderOutput {
render(output: ICellOutputViewModel, item: IOutputItemDto, container: HTMLElement, notebookUri: URI): IRenderOutput {
const disposable = new DisposableStore();
for (let item of items) {
const str = getStringValue(item);
const mdOutput = document.createElement('div');
const mdRenderer = this.instantiationService.createInstance(MarkdownRenderer, { baseUrl: dirname(notebookUri) });
mdOutput.appendChild(mdRenderer.render({ value: str, isTrusted: true, supportThemeIcons: true }, undefined, { gfm: true }).element);
container.appendChild(mdOutput);
disposable.add(mdRenderer);
}
const str = getStringValue(item);
const mdOutput = document.createElement('div');
const mdRenderer = this.instantiationService.createInstance(MarkdownRenderer, { baseUrl: dirname(notebookUri) });
mdOutput.appendChild(mdRenderer.render({ value: str, isTrusted: true, supportThemeIcons: true }, undefined, { gfm: true }).element);
container.appendChild(mdOutput);
disposable.add(mdRenderer);
return { type: RenderOutputType.Mainframe, disposable };
}
}
@ -314,23 +307,21 @@ class ImgRendererContrib extends Disposable implements IOutputRendererContributi
super();
}
render(output: ICellOutputViewModel, items: IOutputItemDto[], container: HTMLElement, notebookUri: URI): IRenderOutput {
render(output: ICellOutputViewModel, item: IOutputItemDto, container: HTMLElement, notebookUri: URI): IRenderOutput {
const disposable = new DisposableStore();
for (let item of items) {
const bytes = new Uint8Array(item.valueBytes);
const blob = new Blob([bytes], { type: item.mime });
const src = URL.createObjectURL(blob);
disposable.add(toDisposable(() => URL.revokeObjectURL(src)));
const bytes = new Uint8Array(item.valueBytes);
const blob = new Blob([bytes], { type: item.mime });
const src = URL.createObjectURL(blob);
disposable.add(toDisposable(() => URL.revokeObjectURL(src)));
const image = document.createElement('img');
image.src = src;
const display = document.createElement('div');
display.classList.add('display');
display.appendChild(image);
container.appendChild(display);
const image = document.createElement('img');
image.src = src;
const display = document.createElement('div');
display.classList.add('display');
display.appendChild(image);
container.appendChild(display);
}
return { type: RenderOutputType.Mainframe, disposable };
}
}

View file

@ -26,7 +26,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { asWebviewUri } from 'vs/workbench/api/common/shared/webview';
import { CellEditState, ICellOutputViewModel, ICommonCellInfo, ICommonNotebookEditor, IDisplayOutputLayoutUpdateRequest, IDisplayOutputViewModel, IGenericCellViewModel, IInsetRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { PreloadOptions, preloadsScriptStr, RendererMetadata } from 'vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads';
import { preloadsScriptStr, RendererMetadata } from 'vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads';
import { transformWebviewThemeVars } from 'vs/workbench/contrib/notebook/browser/view/renderers/webviewThemeMapping';
import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel';
import { INotebookKernel, INotebookRendererInfo, RendererMessagingSpec } from 'vs/workbench/contrib/notebook/common/notebookCommon';
@ -34,368 +34,7 @@ import { IScopedRendererMessaging } from 'vs/workbench/contrib/notebook/common/n
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
import { IWebviewService, WebviewContentPurpose, WebviewElement } from 'vs/workbench/contrib/webview/browser/webview';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
interface BaseToWebviewMessage {
readonly __vscode_notebook_message: true;
}
export interface WebviewIntialized extends BaseToWebviewMessage {
type: 'initialized';
}
export interface DimensionUpdate {
id: string;
init?: boolean;
height: number;
isOutput?: boolean;
}
export interface IDimensionMessage extends BaseToWebviewMessage {
type: 'dimension';
updates: readonly DimensionUpdate[];
}
export interface IMouseEnterMessage extends BaseToWebviewMessage {
type: 'mouseenter';
id: string;
}
export interface IMouseLeaveMessage extends BaseToWebviewMessage {
type: 'mouseleave';
id: string;
}
export interface IOutputFocusMessage extends BaseToWebviewMessage {
type: 'outputFocus';
id: string;
}
export interface IOutputBlurMessage extends BaseToWebviewMessage {
type: 'outputBlur';
id: string;
}
export interface IWheelMessage extends BaseToWebviewMessage {
type: 'did-scroll-wheel';
payload: any;
}
export interface IScrollAckMessage extends BaseToWebviewMessage {
type: 'scroll-ack';
data: { top: number };
version: number;
}
export interface IBlurOutputMessage extends BaseToWebviewMessage {
type: 'focus-editor';
id: string;
focusNext?: boolean;
}
export interface IClickedDataUrlMessage extends BaseToWebviewMessage {
type: 'clicked-data-url';
data: string | ArrayBuffer | null;
downloadName?: string;
}
export interface IClickMarkdownPreviewMessage extends BaseToWebviewMessage {
readonly type: 'clickMarkdownPreview';
readonly cellId: string;
readonly ctrlKey: boolean
readonly altKey: boolean;
readonly metaKey: boolean;
readonly shiftKey: boolean;
}
export interface IContextMenuMarkdownPreviewMessage extends BaseToWebviewMessage {
readonly type: 'contextMenuMarkdownPreview';
readonly cellId: string;
readonly clientX: number;
readonly clientY: number;
}
export interface IMouseEnterMarkdownPreviewMessage extends BaseToWebviewMessage {
type: 'mouseEnterMarkdownPreview';
cellId: string;
}
export interface IMouseLeaveMarkdownPreviewMessage extends BaseToWebviewMessage {
type: 'mouseLeaveMarkdownPreview';
cellId: string;
}
export interface IToggleMarkdownPreviewMessage extends BaseToWebviewMessage {
type: 'toggleMarkdownPreview';
cellId: string;
}
export interface ICellDragStartMessage extends BaseToWebviewMessage {
type: 'cell-drag-start';
readonly cellId: string;
readonly dragOffsetY: number;
}
export interface ICellDragMessage extends BaseToWebviewMessage {
type: 'cell-drag';
readonly cellId: string;
readonly dragOffsetY: number;
}
export interface ICellDropMessage extends BaseToWebviewMessage {
readonly type: 'cell-drop';
readonly cellId: string;
readonly ctrlKey: boolean
readonly altKey: boolean;
readonly dragOffsetY: number;
}
export interface ICellDragEndMessage extends BaseToWebviewMessage {
readonly type: 'cell-drag-end';
readonly cellId: string;
}
export interface IInitializedMarkdownPreviewMessage extends BaseToWebviewMessage {
readonly type: 'initializedMarkdownPreview';
}
export interface ITelemetryFoundRenderedMarkdownMath extends BaseToWebviewMessage {
readonly type: 'telemetryFoundRenderedMarkdownMath';
}
export interface ITelemetryFoundUnrenderedMarkdownMath extends BaseToWebviewMessage {
readonly type: 'telemetryFoundUnrenderedMarkdownMath';
readonly latexDirective: string;
}
export interface IClearMessage {
type: 'clear';
}
export interface IOutputRequestMetadata {
/**
* Additional attributes of a cell metadata.
*/
custom?: { [key: string]: unknown };
}
export interface IOutputRequestDto {
/**
* { mime_type: value }
*/
data: { [key: string]: unknown; }
metadata?: IOutputRequestMetadata;
outputId: string;
}
export interface ICreationRequestMessage {
type: 'html';
content:
| { type: RenderOutputType.Html; htmlContent: string }
| { type: RenderOutputType.Extension; outputId: string; valueBytes: Uint8Array, metadata: unknown; metadata2: unknown, mimeType: string };
cellId: string;
outputId: string;
cellTop: number;
outputOffset: number;
left: number;
requiredPreloads: ReadonlyArray<IControllerPreload>;
readonly initiallyHidden?: boolean;
rendererId?: string | undefined;
}
export interface IContentWidgetTopRequest {
outputId: string;
cellTop: number;
outputOffset: number;
forceDisplay: boolean;
}
export interface IViewScrollTopRequestMessage {
type: 'view-scroll';
widgets: IContentWidgetTopRequest[];
markdownPreviews: { id: string; top: number }[];
}
export interface IScrollRequestMessage {
type: 'scroll';
id: string;
top: number;
widgetTop?: number;
version: number;
}
export interface IClearOutputRequestMessage {
type: 'clearOutput';
cellId: string;
outputId: string;
cellUri: string;
rendererId: string | undefined;
}
export interface IHideOutputMessage {
type: 'hideOutput';
outputId: string;
cellId: string;
}
export interface IShowOutputMessage {
type: 'showOutput';
cellId: string;
outputId: string;
cellTop: number;
outputOffset: number;
}
export interface IFocusOutputMessage {
type: 'focus-output';
cellId: string;
}
export interface IAckOutputHeightMessage {
type: 'ack-dimension',
cellId: string;
outputId: string;
height: number;
}
export interface IControllerPreload {
originalUri: string;
uri: string;
}
export interface IUpdateControllerPreloadsMessage {
type: 'preload';
resources: IControllerPreload[];
}
export interface IUpdateDecorationsMessage {
type: 'decorations';
cellId: string;
addedClassNames: string[];
removedClassNames: string[];
}
export interface ICustomKernelMessage extends BaseToWebviewMessage {
type: 'customKernelMessage';
message: unknown;
}
export interface ICustomRendererMessage extends BaseToWebviewMessage {
type: 'customRendererMessage';
rendererId: string;
message: unknown;
}
export interface ICreateMarkdownMessage {
type: 'createMarkdownPreview',
cell: IMarkdownCellInitialization;
}
export interface IDeleteMarkdownMessage {
type: 'deleteMarkdownPreview',
ids: readonly string[];
}
export interface IHideMarkdownMessage {
type: 'hideMarkdownPreviews';
ids: readonly string[];
}
export interface IUnhideMarkdownMessage {
type: 'unhideMarkdownPreviews';
ids: readonly string[];
}
export interface IShowMarkdownMessage {
type: 'showMarkdownPreview',
id: string;
handle: number;
content: string | undefined;
top: number;
}
export interface IUpdateSelectedMarkdownPreviews {
readonly type: 'updateSelectedMarkdownPreviews',
readonly selectedCellIds: readonly string[]
}
export interface IMarkdownCellInitialization {
cellId: string;
cellHandle: number;
content: string;
offset: number;
visible: boolean;
}
export interface IInitializeMarkdownMessage {
type: 'initializeMarkdownPreview';
cells: ReadonlyArray<IMarkdownCellInitialization>;
}
export interface INotebookStylesMessage {
type: 'notebookStyles';
styles: {
[key: string]: string;
};
}
export interface INotebookOptionsMessage {
type: 'notebookOptions';
options: PreloadOptions;
}
export type FromWebviewMessage =
| WebviewIntialized
| IDimensionMessage
| IMouseEnterMessage
| IMouseLeaveMessage
| IOutputFocusMessage
| IOutputBlurMessage
| IWheelMessage
| IScrollAckMessage
| IBlurOutputMessage
| ICustomKernelMessage
| ICustomRendererMessage
| IClickedDataUrlMessage
| IClickMarkdownPreviewMessage
| IContextMenuMarkdownPreviewMessage
| IMouseEnterMarkdownPreviewMessage
| IMouseLeaveMarkdownPreviewMessage
| IToggleMarkdownPreviewMessage
| ICellDragStartMessage
| ICellDragMessage
| ICellDropMessage
| ICellDragEndMessage
| IInitializedMarkdownPreviewMessage
| ITelemetryFoundRenderedMarkdownMath
| ITelemetryFoundUnrenderedMarkdownMath
;
export type ToWebviewMessage =
| IClearMessage
| IFocusOutputMessage
| IAckOutputHeightMessage
| ICreationRequestMessage
| IViewScrollTopRequestMessage
| IScrollRequestMessage
| IClearOutputRequestMessage
| IHideOutputMessage
| IShowOutputMessage
| IUpdateControllerPreloadsMessage
| IUpdateDecorationsMessage
| ICustomKernelMessage
| ICustomRendererMessage
| ICreateMarkdownMessage
| IDeleteMarkdownMessage
| IShowMarkdownMessage
| IHideMarkdownMessage
| IUnhideMarkdownMessage
| IUpdateSelectedMarkdownPreviews
| IInitializeMarkdownMessage
| INotebookStylesMessage
| INotebookOptionsMessage;
export type AnyMessage = FromWebviewMessage | ToWebviewMessage;
import { ICreationRequestMessage, IMarkupCellInitialization, FromWebviewMessage, IClickedDataUrlMessage, IContentWidgetTopRequest, IControllerPreload, ToWebviewMessage } from './webviewMessages';
export interface ICachedInset<K extends ICommonCellInfo> {
outputId: string;
@ -424,7 +63,7 @@ export class BackLayerWebView<T extends ICommonCellInfo> extends Disposable {
element: HTMLElement;
webview: WebviewElement | undefined = undefined;
insetMapping: Map<IDisplayOutputViewModel, ICachedInset<T>> = new Map();
readonly markdownPreviewMapping = new Map<string, IMarkdownCellInitialization>();
readonly markdownPreviewMapping = new Map<string, IMarkupCellInitialization>();
hiddenInsetMapping: Set<IDisplayOutputViewModel> = new Set();
reversedInsetMapping: Map<string, IDisplayOutputViewModel> = new Map();
localResourceRootsCache: URI[] | undefined = undefined;
@ -525,7 +164,8 @@ export class BackLayerWebView<T extends ICommonCellInfo> extends Disposable {
'notebook-markdown-left-margin': `${this.options.markdownLeftMargin}px`,
'notebook-output-node-left-padding': `${this.options.outputNodeLeftPadding}px`,
'notebook-markdown-min-height': `${this.options.previewNodePadding * 2}px`,
'notebook-cell-output-font-size': `${this.options.fontSize}px`
'notebook-cell-output-font-size': `${this.options.fontSize}px`,
'notebook-cell-markup-empty-content': nls.localize('notebook.emptyMarkdownPlaceholder', "Empty markdown cell, double click or press enter to edit."),
};
}
@ -536,134 +176,6 @@ export class BackLayerWebView<T extends ICommonCellInfo> extends Disposable {
<head>
<meta charset="UTF-8">
<base href="${baseUrl}/"/>
<!--
Markdown previews are rendered using a shadow dom and are not effected by normal css.
Insert this style node into all preview shadow doms for styling.
-->
<template id="preview-styles">
<style>
img {
max-width: 100%;
max-height: 100%;
}
a {
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
a:focus,
input:focus,
select:focus,
textarea:focus {
outline: 1px solid -webkit-focus-ring-color;
outline-offset: -1px;
}
hr {
border: 0;
height: 2px;
border-bottom: 2px solid;
}
h1 {
font-size: 26px;
line-height: 31px;
margin: 0;
margin-bottom: 13px;
}
h2 {
font-size: 19px;
margin: 0;
margin-bottom: 10px;
}
h1,
h2,
h3 {
font-weight: normal;
}
div {
width: 100%;
}
/* Adjust margin of first item in markdown cell */
*:first-child {
margin-top: 0px;
}
/* h1 tags don't need top margin */
h1:first-child {
margin-top: 0;
}
/* Removes bottom margin when only one item exists in markdown cell */
*:only-child,
*:last-child {
margin-bottom: 0;
padding-bottom: 0;
}
/* makes all markdown cells consistent */
div {
min-height: var(--notebook-markdown-min-height);
}
table {
border-collapse: collapse;
border-spacing: 0;
}
table th,
table td {
border: 1px solid;
}
table > thead > tr > th {
text-align: left;
border-bottom: 1px solid;
}
table > thead > tr > th,
table > thead > tr > td,
table > tbody > tr > th,
table > tbody > tr > td {
padding: 5px 10px;
}
table > tbody > tr + tr > td {
border-top: 1px solid;
}
blockquote {
margin: 0 7px 0 5px;
padding: 0 16px 0 10px;
border-left-width: 5px;
border-left-style: solid;
}
code,
.code {
font-family: var(--monaco-monospace-font);
font-size: 1em;
line-height: 1.357em;
}
.code {
white-space: pre-wrap;
}
dragging {
background-color: var(--theme-background);
}
</style>
</template>
<style>
#container .cell_container {
width: 100%;
@ -707,12 +219,6 @@ export class BackLayerWebView<T extends ICommonCellInfo> extends Disposable {
cursor: grab;
}
#container > div.preview.emptyMarkdownCell::before {
content: "${nls.localize('notebook.emptyMarkdownPlaceholder', "Empty markdown cell, double click or press enter to edit.")}";
font-style: italic;
opacity: 0.6;
}
#container > div.preview.selected {
background: var(--theme-notebook-cell-selected-background);
}
@ -1049,7 +555,7 @@ var requirejs = (function() {
this.rendererMessaging?.postMessage(data.rendererId, data.message);
break;
}
case 'clickMarkdownPreview':
case 'clickMarkupCell':
{
const cell = this.notebookEditor.getCellById(data.cellId);
if (cell) {
@ -1063,7 +569,7 @@ var requirejs = (function() {
}
break;
}
case 'contextMenuMarkdownPreview':
case 'contextMenuMarkupCell':
{
const cell = this.notebookEditor.getCellById(data.cellId);
if (cell) {
@ -1088,7 +594,7 @@ var requirejs = (function() {
}
break;
}
case 'toggleMarkdownPreview':
case 'toggleMarkupPreview':
{
const cell = this.notebookEditor.getCellById(data.cellId);
if (cell) {
@ -1097,7 +603,7 @@ var requirejs = (function() {
}
break;
}
case 'mouseEnterMarkdownPreview':
case 'mouseEnterMarkupCell':
{
const cell = this.notebookEditor.getCellById(data.cellId);
if (cell instanceof MarkdownCellViewModel) {
@ -1105,7 +611,7 @@ var requirejs = (function() {
}
break;
}
case 'mouseLeaveMarkdownPreview':
case 'mouseLeaveMarkupCell':
{
const cell = this.notebookEditor.getCellById(data.cellId);
if (cell instanceof MarkdownCellViewModel) {
@ -1246,7 +752,7 @@ var requirejs = (function() {
const mdCells = [...this.markdownPreviewMapping.values()];
this.markdownPreviewMapping.clear();
this.initializeMarkdown(mdCells);
this.initializeMarkup(mdCells);
this._updateStyles();
this._updateOptions();
}
@ -1324,7 +830,7 @@ var requirejs = (function() {
});
}
private async createMarkdownPreview(initialization: IMarkdownCellInitialization) {
private async createMarkdownPreview(initialization: IMarkupCellInitialization) {
if (this._disposed) {
return;
}
@ -1336,12 +842,12 @@ var requirejs = (function() {
this.markdownPreviewMapping.set(initialization.cellId, initialization);
this._sendMessageToWebview({
type: 'createMarkdownPreview',
type: 'createMarkupCell',
cell: initialization
});
}
async showMarkdownPreview(initialization: IMarkdownCellInitialization) {
async showMarkdownPreview(initialization: IMarkupCellInitialization) {
if (this._disposed) {
return;
}
@ -1354,7 +860,7 @@ var requirejs = (function() {
const sameContent = initialization.content === entry.content;
if (!sameContent || !entry.visible) {
this._sendMessageToWebview({
type: 'showMarkdownPreview',
type: 'showMarkupCell',
id: initialization.cellId,
handle: initialization.cellHandle,
// If the content has not changed, we still want to make sure the
@ -1387,7 +893,7 @@ var requirejs = (function() {
if (cellsToHide.length) {
this._sendMessageToWebview({
type: 'hideMarkdownPreviews',
type: 'hideMarkupCells',
ids: cellsToHide
});
}
@ -1412,7 +918,7 @@ var requirejs = (function() {
}
this._sendMessageToWebview({
type: 'unhideMarkdownPreviews',
type: 'unhideMarkupCells',
ids: toUnhide,
});
}
@ -1431,7 +937,7 @@ var requirejs = (function() {
if (cellIds.length) {
this._sendMessageToWebview({
type: 'deleteMarkdownPreview',
type: 'deleteMarkupCell',
ids: cellIds
});
}
@ -1443,12 +949,12 @@ var requirejs = (function() {
}
this._sendMessageToWebview({
type: 'updateSelectedMarkdownPreviews',
type: 'updateSelectedMarkupCells',
selectedCellIds: selectedCellsIds.filter(id => this.markdownPreviewMapping.has(id)),
});
}
async initializeMarkdown(cells: ReadonlyArray<IMarkdownCellInitialization>) {
async initializeMarkup(cells: readonly IMarkupCellInitialization[]) {
if (this._disposed) {
return;
}
@ -1456,7 +962,7 @@ var requirejs = (function() {
// TODO: use proper handler
const p = new Promise<void>(resolve => {
this.webview?.onMessage(e => {
if (e.message.type === 'initializedMarkdownPreview') {
if (e.message.type === 'initializedMarkup') {
resolve();
}
});
@ -1467,7 +973,7 @@ var requirejs = (function() {
}
this._sendMessageToWebview({
type: 'initializeMarkdownPreview',
type: 'initializeMarkup',
cells,
});

View file

@ -0,0 +1,366 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import type { RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import type { PreloadOptions } from 'vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads';
interface BaseToWebviewMessage {
readonly __vscode_notebook_message: true;
}
export interface WebviewIntialized extends BaseToWebviewMessage {
readonly type: 'initialized';
}
export interface DimensionUpdate {
readonly id: string;
readonly init?: boolean;
readonly height: number;
readonly isOutput?: boolean;
}
export interface IDimensionMessage extends BaseToWebviewMessage {
readonly type: 'dimension';
readonly updates: readonly DimensionUpdate[];
}
export interface IMouseEnterMessage extends BaseToWebviewMessage {
readonly type: 'mouseenter';
readonly id: string;
}
export interface IMouseLeaveMessage extends BaseToWebviewMessage {
readonly type: 'mouseleave';
readonly id: string;
}
export interface IOutputFocusMessage extends BaseToWebviewMessage {
readonly type: 'outputFocus';
readonly id: string;
}
export interface IOutputBlurMessage extends BaseToWebviewMessage {
readonly type: 'outputBlur';
readonly id: string;
}
export interface IWheelMessage extends BaseToWebviewMessage {
readonly type: 'did-scroll-wheel';
readonly payload: any;
}
export interface IScrollAckMessage extends BaseToWebviewMessage {
readonly type: 'scroll-ack';
readonly data: { top: number; };
readonly version: number;
}
export interface IBlurOutputMessage extends BaseToWebviewMessage {
readonly type: 'focus-editor';
readonly id: string;
readonly focusNext?: boolean;
}
export interface IClickedDataUrlMessage extends BaseToWebviewMessage {
readonly type: 'clicked-data-url';
readonly data: string | ArrayBuffer | null;
readonly downloadName?: string;
}
export interface IClickMarkupCellMessage extends BaseToWebviewMessage {
readonly type: 'clickMarkupCell';
readonly cellId: string;
readonly ctrlKey: boolean;
readonly altKey: boolean;
readonly metaKey: boolean;
readonly shiftKey: boolean;
}
export interface IContextMenuMarkupCellMessage extends BaseToWebviewMessage {
readonly type: 'contextMenuMarkupCell';
readonly cellId: string;
readonly clientX: number;
readonly clientY: number;
}
export interface IMouseEnterMarkupCellMessage extends BaseToWebviewMessage {
readonly type: 'mouseEnterMarkupCell';
readonly cellId: string;
}
export interface IMouseLeaveMarkupCellMessage extends BaseToWebviewMessage {
readonly type: 'mouseLeaveMarkupCell';
readonly cellId: string;
}
export interface IToggleMarkupPreviewMessage extends BaseToWebviewMessage {
readonly type: 'toggleMarkupPreview';
readonly cellId: string;
}
export interface ICellDragStartMessage extends BaseToWebviewMessage {
readonly type: 'cell-drag-start';
readonly cellId: string;
readonly dragOffsetY: number;
}
export interface ICellDragMessage extends BaseToWebviewMessage {
readonly type: 'cell-drag';
readonly cellId: string;
readonly dragOffsetY: number;
}
export interface ICellDropMessage extends BaseToWebviewMessage {
readonly type: 'cell-drop';
readonly cellId: string;
readonly ctrlKey: boolean;
readonly altKey: boolean;
readonly dragOffsetY: number;
}
export interface ICellDragEndMessage extends BaseToWebviewMessage {
readonly type: 'cell-drag-end';
readonly cellId: string;
}
export interface IInitializedMarkupMessage extends BaseToWebviewMessage {
readonly type: 'initializedMarkup';
}
export interface ITelemetryFoundRenderedMarkdownMath extends BaseToWebviewMessage {
readonly type: 'telemetryFoundRenderedMarkdownMath';
}
export interface ITelemetryFoundUnrenderedMarkdownMath extends BaseToWebviewMessage {
readonly type: 'telemetryFoundUnrenderedMarkdownMath';
readonly latexDirective: string;
}
export interface IClearMessage {
readonly type: 'clear';
}
export interface IOutputRequestMetadata {
/**
* Additional attributes of a cell metadata.
*/
readonly custom?: { [key: string]: unknown; };
}
export interface IOutputRequestDto {
/**
* { mime_type: value }
*/
readonly data: { [key: string]: unknown; };
readonly metadata?: IOutputRequestMetadata;
readonly outputId: string;
}
export interface ICreationRequestMessage {
readonly type: 'html';
readonly content: { type: RenderOutputType.Html; htmlContent: string; } |
{ type: RenderOutputType.Extension; outputId: string; valueBytes: Uint8Array; metadata: unknown; metadata2: unknown; mimeType: string; };
readonly cellId: string;
readonly outputId: string;
cellTop: number;
outputOffset: number;
readonly left: number;
readonly requiredPreloads: ReadonlyArray<IControllerPreload>;
readonly initiallyHidden?: boolean;
readonly rendererId?: string | undefined;
}
export interface IContentWidgetTopRequest {
readonly outputId: string;
readonly cellTop: number;
readonly outputOffset: number;
readonly forceDisplay: boolean;
}
export interface IViewScrollTopRequestMessage {
readonly type: 'view-scroll';
readonly widgets: IContentWidgetTopRequest[];
readonly markdownPreviews: { id: string; top: number; }[];
}
export interface IScrollRequestMessage {
readonly type: 'scroll';
readonly id: string;
readonly top: number;
readonly widgetTop?: number;
readonly version: number;
}
export interface IClearOutputRequestMessage {
readonly type: 'clearOutput';
readonly cellId: string;
readonly outputId: string;
readonly cellUri: string;
readonly rendererId: string | undefined;
}
export interface IHideOutputMessage {
readonly type: 'hideOutput';
readonly outputId: string;
readonly cellId: string;
}
export interface IShowOutputMessage {
readonly type: 'showOutput';
readonly cellId: string;
readonly outputId: string;
readonly cellTop: number;
readonly outputOffset: number;
}
export interface IFocusOutputMessage {
readonly type: 'focus-output';
readonly cellId: string;
}
export interface IAckOutputHeightMessage {
readonly type: 'ack-dimension';
readonly cellId: string;
readonly outputId: string;
readonly height: number;
}
export interface IControllerPreload {
readonly originalUri: string;
readonly uri: string;
}
export interface IUpdateControllerPreloadsMessage {
readonly type: 'preload';
readonly resources: IControllerPreload[];
}
export interface IUpdateDecorationsMessage {
readonly type: 'decorations';
readonly cellId: string;
readonly addedClassNames: string[];
readonly removedClassNames: string[];
}
export interface ICustomKernelMessage extends BaseToWebviewMessage {
readonly type: 'customKernelMessage';
readonly message: unknown;
}
export interface ICustomRendererMessage extends BaseToWebviewMessage {
readonly type: 'customRendererMessage';
readonly rendererId: string;
readonly message: unknown;
}
export interface ICreateMarkupCellMessage {
readonly type: 'createMarkupCell';
readonly cell: IMarkupCellInitialization;
}
export interface IDeleteMarkupCellMessage {
readonly type: 'deleteMarkupCell';
readonly ids: readonly string[];
}
export interface IHideMarkupCellMessage {
readonly type: 'hideMarkupCells';
readonly ids: readonly string[];
}
export interface IUnhideMarkupCellMessage {
readonly type: 'unhideMarkupCells';
readonly ids: readonly string[];
}
export interface IShowMarkupCellMessage {
readonly type: 'showMarkupCell';
readonly id: string;
readonly handle: number;
readonly content: string | undefined;
readonly top: number;
}
export interface IUpdateSelectedMarkupCellsMessage {
readonly type: 'updateSelectedMarkupCells';
readonly selectedCellIds: readonly string[];
}
export interface IMarkupCellInitialization {
mime: string;
cellId: string;
cellHandle: number;
content: string;
offset: number;
visible: boolean;
}
export interface IInitializeMarkupCells {
readonly type: 'initializeMarkup';
readonly cells: ReadonlyArray<IMarkupCellInitialization>;
}
export interface INotebookStylesMessage {
readonly type: 'notebookStyles';
readonly styles: {
[key: string]: string;
};
}
export interface INotebookOptionsMessage {
readonly type: 'notebookOptions';
readonly options: PreloadOptions;
}
export type FromWebviewMessage = WebviewIntialized |
IDimensionMessage |
IMouseEnterMessage |
IMouseLeaveMessage |
IOutputFocusMessage |
IOutputBlurMessage |
IWheelMessage |
IScrollAckMessage |
IBlurOutputMessage |
ICustomKernelMessage |
ICustomRendererMessage |
IClickedDataUrlMessage |
IClickMarkupCellMessage |
IContextMenuMarkupCellMessage |
IMouseEnterMarkupCellMessage |
IMouseLeaveMarkupCellMessage |
IToggleMarkupPreviewMessage |
ICellDragStartMessage |
ICellDragMessage |
ICellDropMessage |
ICellDragEndMessage |
IInitializedMarkupMessage |
ITelemetryFoundRenderedMarkdownMath |
ITelemetryFoundUnrenderedMarkdownMath;
export type ToWebviewMessage = IClearMessage |
IFocusOutputMessage |
IAckOutputHeightMessage |
ICreationRequestMessage |
IViewScrollTopRequestMessage |
IScrollRequestMessage |
IClearOutputRequestMessage |
IHideOutputMessage |
IShowOutputMessage |
IUpdateControllerPreloadsMessage |
IUpdateDecorationsMessage |
ICustomKernelMessage |
ICustomRendererMessage |
ICreateMarkupCellMessage |
IDeleteMarkupCellMessage |
IShowMarkupCellMessage |
IHideMarkupCellMessage |
IUnhideMarkupCellMessage |
IUpdateSelectedMarkupCellsMessage |
IInitializeMarkupCells |
INotebookStylesMessage |
INotebookOptionsMessage;
export type AnyMessage = FromWebviewMessage | ToWebviewMessage;

View file

@ -6,7 +6,7 @@
import type { Event } from 'vs/base/common/event';
import type { IDisposable } from 'vs/base/common/lifecycle';
import { RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import type { FromWebviewMessage, IBlurOutputMessage, ICellDropMessage, ICellDragMessage, ICellDragStartMessage, IClickedDataUrlMessage, IDimensionMessage, IClickMarkdownPreviewMessage, IMouseEnterMarkdownPreviewMessage, IMouseEnterMessage, IMouseLeaveMarkdownPreviewMessage, IMouseLeaveMessage, IToggleMarkdownPreviewMessage, IWheelMessage, ToWebviewMessage, ICellDragEndMessage, IOutputFocusMessage, IOutputBlurMessage, DimensionUpdate, IContextMenuMarkdownPreviewMessage, ITelemetryFoundRenderedMarkdownMath, ITelemetryFoundUnrenderedMarkdownMath, IMarkdownCellInitialization } from 'vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView';
import type * as webviewMessages from 'vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages';
// !! IMPORTANT !! everything must be in-line within the webviewPreloads
// function. Imports are not allowed. This is stringified and injected into
@ -71,7 +71,7 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re
};
const handleDataUrl = async (data: string | ArrayBuffer | null, downloadName: string) => {
postNotebookMessage<IClickedDataUrlMessage>('clicked-data-url', {
postNotebookMessage<webviewMessages.IClickedDataUrlMessage>('clicked-data-url', {
data,
downloadName
});
@ -201,7 +201,7 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re
});
const dimensionUpdater = new class {
private readonly pending = new Map<string, DimensionUpdate>();
private readonly pending = new Map<string, webviewMessages.DimensionUpdate>();
update(id: string, height: number, options: { init?: boolean; isOutput?: boolean }) {
if (!this.pending.size) {
@ -221,7 +221,7 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re
return;
}
postNotebookMessage<IDimensionMessage>('dimension', {
postNotebookMessage<webviewMessages.IDimensionMessage>('dimension', {
updates: Array.from(this.pending.values())
});
this.pending.clear();
@ -300,7 +300,7 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re
if (event.defaultPrevented || scrollWillGoToParent(event)) {
return;
}
postNotebookMessage<IWheelMessage>('did-scroll-wheel', {
postNotebookMessage<webviewMessages.IWheelMessage>('did-scroll-wheel', {
payload: {
deltaMode: event.deltaMode,
deltaX: event.deltaX,
@ -324,7 +324,7 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re
const element = document.createElement('div');
element.tabIndex = 0;
element.addEventListener('focus', () => {
postNotebookMessage<IBlurOutputMessage>('focus-editor', {
postNotebookMessage<webviewMessages.IBlurOutputMessage>('focus-editor', {
id: outputId,
focusNext
});
@ -335,12 +335,12 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re
function addMouseoverListeners(element: HTMLElement, outputId: string): void {
element.addEventListener('mouseenter', () => {
postNotebookMessage<IMouseEnterMessage>('mouseenter', {
postNotebookMessage<webviewMessages.IMouseEnterMessage>('mouseenter', {
id: outputId,
});
});
element.addEventListener('mouseleave', () => {
postNotebookMessage<IMouseLeaveMessage>('mouseleave', {
postNotebookMessage<webviewMessages.IMouseLeaveMessage>('mouseleave', {
id: outputId,
});
});
@ -376,7 +376,7 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re
this._loosingFocus = false;
if (!this._hasFocus) {
this._hasFocus = true;
postNotebookMessage<IOutputFocusMessage>('outputFocus', {
postNotebookMessage<webviewMessages.IOutputFocusMessage>('outputFocus', {
id: this._outputId,
});
}
@ -389,7 +389,7 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re
if (this._loosingFocus) {
this._loosingFocus = false;
this._hasFocus = false;
postNotebookMessage<IOutputBlurMessage>('outputBlur', {
postNotebookMessage<webviewMessages.IOutputBlurMessage>('outputBlur', {
id: this._outputId,
});
}
@ -507,79 +507,45 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re
window.addEventListener('wheel', handleWheel);
window.addEventListener('message', async rawEvent => {
const event = rawEvent as ({ data: ToWebviewMessage; });
const event = rawEvent as ({ data: webviewMessages.ToWebviewMessage; });
switch (event.data.type) {
case 'initializeMarkdownPreview':
{
await ensureMarkdownPreviewCells(event.data.cells);
dimensionUpdater.updateImmediately();
postNotebookMessage('initializedMarkdownPreview', {});
}
case 'initializeMarkup':
await notebookDocument.ensureMarkupCells(event.data.cells);
dimensionUpdater.updateImmediately();
postNotebookMessage('initializedMarkup', {});
break;
case 'createMarkdownPreview':
ensureMarkdownPreviewCells([event.data.cell]);
break;
case 'showMarkdownPreview':
{
const data = event.data;
const cellContainer = document.getElementById(data.id);
if (cellContainer) {
cellContainer.style.visibility = 'visible';
cellContainer.style.top = `${data.top}px`;
updateMarkdownPreview(cellContainer, data.id, data.content);
}
}
case 'createMarkupCell':
notebookDocument.ensureMarkupCells([event.data.cell]);
break;
case 'hideMarkdownPreviews':
{
for (const id of event.data.ids) {
const cellContainer = document.getElementById(id);
if (cellContainer) {
cellContainer.style.visibility = 'hidden';
}
}
}
break;
case 'unhideMarkdownPreviews':
{
for (const id of event.data.ids) {
const cellContainer = document.getElementById(id);
if (cellContainer) {
cellContainer.style.visibility = 'visible';
updateMarkdownPreview(cellContainer, id, undefined);
}
}
}
break;
case 'deleteMarkdownPreview':
{
for (const id of event.data.ids) {
const cellContainer = document.getElementById(id);
cellContainer?.remove();
}
}
break;
case 'updateSelectedMarkdownPreviews':
{
const selectedCellIds = new Set<string>(event.data.selectedCellIds);
for (const oldSelected of document.querySelectorAll('.preview.selected')) {
const id = oldSelected.id;
if (!selectedCellIds.has(id)) {
oldSelected.classList.remove('selected');
}
}
case 'showMarkupCell':
notebookDocument.showMarkupCell(event.data.id, event.data.top, event.data.content);
break;
for (const newSelected of selectedCellIds) {
const previewContainer = document.getElementById(newSelected);
if (previewContainer) {
previewContainer.classList.add('selected');
}
}
case 'hideMarkupCells':
for (const id of event.data.ids) {
notebookDocument.hideMarkupCell(id);
}
break;
case 'unhideMarkupCells':
for (const id of event.data.ids) {
notebookDocument.unhideMarkupCell(id);
}
break;
case 'deleteMarkupCell':
for (const id of event.data.ids) {
notebookDocument.deleteMarkupCell(id);
}
break;
case 'updateSelectedMarkupCells':
notebookDocument.updateSelectedCells(event.data.selectedCellIds);
break;
case 'html': {
const data = event.data;
outputs.enqueue(event.data.outputId, async (state) => {
@ -829,10 +795,9 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re
// Update markdown previews
for (const markdownContainer of document.querySelectorAll('.preview')) {
setMarkdownContainerDraggable(markdownContainer, currentOptions.dragAndDropEnabled);
setMarkupContainerDraggable(markdownContainer, currentOptions.dragAndDropEnabled);
}
break;
}
});
@ -1021,47 +986,254 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re
this._renderers.get(rendererId)?.api?.disposeOutputItem?.(outputId);
}
public async renderCustom(rendererId: string, info: IOutputItem, element: HTMLElement) {
const api = await this.load(rendererId);
if (!api) {
throw new Error(`renderer ${rendererId} did not return an API`);
}
public async render(info: IOutputItem, element: HTMLElement) {
const renderers = Array.from(this._renderers.values())
.filter(renderer => renderer.data.mimeTypes.includes(info.mime) && !renderer.data.extends);
api.renderOutputItem(info, element);
}
public async renderMarkdown(id: string, element: HTMLElement, content: string): Promise<void> {
const markdownRenderers = Array.from(this._renderers.values())
.filter(renderer => renderer.data.mimeTypes.includes('text/markdown') && !renderer.data.extends);
if (!markdownRenderers.length) {
if (!renderers.length) {
throw new Error('Could not find renderer');
}
await Promise.all(markdownRenderers.map(x => x.load()));
await Promise.all(renderers.map(x => x.load()));
markdownRenderers[0].api?.renderOutputItem({
id,
element,
mime: 'text/markdown',
metadata: undefined,
metadata2: undefined,
outputId: undefined,
text() { return content; },
json() { return undefined; },
bytes() { return this.data(); },
data() { return new TextEncoder().encode(content); },
blob() { return new Blob([this.data()], { type: this.mime }); },
}, element);
renderers[0].api?.renderOutputItem(info, element);
}
}();
let hasPostedRenderedMathTelemetry = false;
const unsupportedKatexTermsRegex = /(\\(?:abovewithdelims|array|Arrowvert|arrowvert|atopwithdelims|bbox|bracevert|buildrel|cancelto|cases|class|cssId|ddddot|dddot|DeclareMathOperator|definecolor|displaylines|enclose|eqalign|eqalignno|eqref|hfil|hfill|idotsint|iiiint|label|leftarrowtail|leftroot|leqalignno|lower|mathtip|matrix|mbox|mit|mmlToken|moveleft|moveright|mspace|newenvironment|Newextarrow|notag|oldstyle|overparen|overwithdelims|pmatrix|raise|ref|renewenvironment|require|root|Rule|scr|shoveleft|shoveright|sideset|skew|Space|strut|style|texttip|Tiny|toggle|underparen|unicode|uproot)\b)/gi;
const notebookDocument = new class {
private readonly _markupCells = new Map<string, MarkupCell>();
private async createMarkupCell(init: webviewMessages.IMarkupCellInitialization, top: number): Promise<MarkupCell> {
const existing = this._markupCells.get(init.cellId);
if (existing) {
console.error(`Trying to create markup that already exists: ${init.cellId}`);
return existing;
}
const markdownCell = new MarkupCell(init.cellId, init.mime, init.content, top);
this._markupCells.set(init.cellId, markdownCell);
await markdownCell.ready;
return markdownCell;
}
public async ensureMarkupCells(update: readonly webviewMessages.IMarkupCellInitialization[]): Promise<void> {
await Promise.all(update.map(async info => {
let cell = this._markupCells.get(info.cellId);
if (cell) {
await cell.updateContentAndRender(info.content);
} else {
cell = await this.createMarkupCell(info, info.offset);
}
cell.element.style.visibility = info.visible ? 'visible' : 'hidden';
}));
}
public deleteMarkupCell(id: string) {
const cell = this.getExpectedMarkupCell(id);
if (cell) {
cell.element.remove();
this._markupCells.delete(id);
}
}
public async updateMarkupContent(id: string, newContent: string): Promise<void> {
const cell = this.getExpectedMarkupCell(id);
await cell?.updateContentAndRender(newContent);
}
public showMarkupCell(id: string, top: number, newContent: string | undefined): void {
const cell = this.getExpectedMarkupCell(id);
cell?.show(id, top, newContent);
}
public hideMarkupCell(id: string): void {
const cell = this.getExpectedMarkupCell(id);
cell?.hide();
}
public unhideMarkupCell(id: string): void {
const cell = this.getExpectedMarkupCell(id);
cell?.unhide();
}
private getExpectedMarkupCell(id: string): MarkupCell | undefined {
const cell = this._markupCells.get(id);
if (!cell) {
console.log(`Could not find markup cell '${id}'`);
return undefined;
}
return cell;
}
public updateSelectedCells(selectedCellIds: readonly string[]) {
const selectedCellSet = new Set<string>(selectedCellIds);
for (const cell of this._markupCells.values()) {
cell.setSelected(selectedCellSet.has(cell.id));
}
}
}();
class MarkupCell implements IOutputItem {
public readonly ready: Promise<void>;
/// Internal field that holds markdown text
private _content: string;
constructor(id: string, mime: string, content: string, top: number) {
this.id = id;
this.mime = mime;
this._content = content;
let resolveReady: () => void;
this.ready = new Promise<void>(r => resolveReady = r);
const root = document.getElementById('container')!;
this.element = document.createElement('div');
this.element.id = this.id;
this.element.classList.add('preview');
this.element.style.position = 'absolute';
this.element.style.top = top + 'px';
root.appendChild(this.element);
this.addEventListeners();
this.updateContentAndRender(this._content).then(() => {
resizeObserver.observe(this.element, this.id, false);
resolveReady();
});
}
//#region IOutputItem
public readonly id: string;
public readonly mime;
public readonly element: HTMLElement;
// deprecated fields
public readonly metadata = undefined;
public readonly metadata2 = undefined;
public readonly outputId?: string | undefined;
text() { return this._content; }
json() { return undefined; }
bytes() { return this.data(); }
data() { return new TextEncoder().encode(this._content); }
blob() { return new Blob([this.data()], { type: this.mime }); }
//#endregion
private addEventListeners() {
this.element.addEventListener('dblclick', () => {
postNotebookMessage<webviewMessages.IToggleMarkupPreviewMessage>('toggleMarkupPreview', { cellId: this.id });
});
this.element.addEventListener('click', e => {
postNotebookMessage<webviewMessages.IClickMarkupCellMessage>('clickMarkupCell', {
cellId: this.id,
altKey: e.altKey,
ctrlKey: e.ctrlKey,
metaKey: e.metaKey,
shiftKey: e.shiftKey,
});
});
this.element.addEventListener('contextmenu', e => {
postNotebookMessage<webviewMessages.IContextMenuMarkupCellMessage>('contextMenuMarkupCell', {
cellId: this.id,
clientX: e.clientX,
clientY: e.clientY,
});
});
this.element.addEventListener('mouseenter', () => {
postNotebookMessage<webviewMessages.IMouseEnterMarkupCellMessage>('mouseEnterMarkupCell', { cellId: this.id });
});
this.element.addEventListener('mouseleave', () => {
postNotebookMessage<webviewMessages.IMouseLeaveMarkupCellMessage>('mouseLeaveMarkupCell', { cellId: this.id });
});
setMarkupContainerDraggable(this.element, currentOptions.dragAndDropEnabled);
this.element.addEventListener('dragstart', e => {
markdownPreviewDragManager.startDrag(e, this.id);
});
this.element.addEventListener('drag', e => {
markdownPreviewDragManager.updateDrag(e, this.id);
});
this.element.addEventListener('dragend', e => {
markdownPreviewDragManager.endDrag(e, this.id);
});
}
public async updateContentAndRender(newContent: string): Promise<void> {
this._content = newContent;
await renderers.render(this, this.element);
if (!hasPostedRenderedMathTelemetry) {
const hasRenderedMath = this.element.querySelector('.katex');
if (hasRenderedMath) {
hasPostedRenderedMathTelemetry = true;
postNotebookMessage<webviewMessages.ITelemetryFoundRenderedMarkdownMath>('telemetryFoundRenderedMarkdownMath', {});
}
}
const matches = this.element.innerText.match(unsupportedKatexTermsRegex);
if (matches) {
postNotebookMessage<webviewMessages.ITelemetryFoundUnrenderedMarkdownMath>('telemetryFoundUnrenderedMarkdownMath', {
latexDirective: matches[0],
});
}
dimensionUpdater.update(this.id, this.element.clientHeight, {
isOutput: false
});
}
public show(id: string, top: number, newContent: string | undefined): void {
this.element.style.visibility = 'visible';
this.element.style.top = `${top}px`;
if (typeof newContent === 'string') {
this.updateContentAndRender(newContent);
} else {
this.updateMarkupDimensions();
}
}
public hide() {
this.element.style.visibility = 'hidden';
}
public unhide() {
this.element.style.visibility = 'visible';
this.updateMarkupDimensions();
}
private async updateMarkupDimensions() {
dimensionUpdater.update(this.id, this.element.clientHeight, {
isOutput: false
});
}
public setSelected(selected: boolean) {
this.element.classList.toggle('selected', selected);
}
}
vscode.postMessage({
__vscode_notebook_message: true,
type: 'initialized'
});
function setMarkdownContainerDraggable(element: Element, isDraggable: boolean) {
function setMarkupContainerDraggable(element: Element, isDraggable: boolean) {
if (isDraggable) {
element.classList.add('draggable');
element.setAttribute('draggable', 'true');
@ -1071,102 +1243,7 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re
}
}
async function createMarkdownPreview(cellId: string, content: string, top: number): Promise<HTMLElement> {
const container = document.getElementById('container')!;
const cellContainer = document.createElement('div');
const existing = document.getElementById(cellId);
if (existing) {
console.error(`Trying to create markdown preview that already exists: ${cellId}`);
return existing;
}
cellContainer.id = cellId;
cellContainer.classList.add('preview');
cellContainer.style.position = 'absolute';
cellContainer.style.top = top + 'px';
container.appendChild(cellContainer);
cellContainer.addEventListener('dblclick', () => {
postNotebookMessage<IToggleMarkdownPreviewMessage>('toggleMarkdownPreview', { cellId });
});
cellContainer.addEventListener('click', e => {
postNotebookMessage<IClickMarkdownPreviewMessage>('clickMarkdownPreview', {
cellId,
altKey: e.altKey,
ctrlKey: e.ctrlKey,
metaKey: e.metaKey,
shiftKey: e.shiftKey,
});
});
cellContainer.addEventListener('contextmenu', e => {
postNotebookMessage<IContextMenuMarkdownPreviewMessage>('contextMenuMarkdownPreview', {
cellId,
clientX: e.clientX,
clientY: e.clientY,
});
});
cellContainer.addEventListener('mouseenter', () => {
postNotebookMessage<IMouseEnterMarkdownPreviewMessage>('mouseEnterMarkdownPreview', { cellId });
});
cellContainer.addEventListener('mouseleave', () => {
postNotebookMessage<IMouseLeaveMarkdownPreviewMessage>('mouseLeaveMarkdownPreview', { cellId });
});
setMarkdownContainerDraggable(cellContainer, currentOptions.dragAndDropEnabled);
cellContainer.addEventListener('dragstart', e => {
markdownPreviewDragManager.startDrag(e, cellId);
});
cellContainer.addEventListener('drag', e => {
markdownPreviewDragManager.updateDrag(e, cellId);
});
cellContainer.addEventListener('dragend', e => {
markdownPreviewDragManager.endDrag(e, cellId);
});
const previewRoot = cellContainer.attachShadow({ mode: 'open' });
// Add default webview style
const defaultStyles = document.getElementById('_defaultStyles') as HTMLStyleElement;
previewRoot.appendChild(defaultStyles.cloneNode(true));
// Add default preview style
const previewStyles = document.getElementById('preview-styles') as HTMLTemplateElement;
previewRoot.appendChild(previewStyles.content.cloneNode(true));
const previewNode = document.createElement('div');
previewNode.id = 'preview';
previewRoot.appendChild(previewNode);
await updateMarkdownPreview(cellContainer, cellId, content);
resizeObserver.observe(cellContainer, cellId, false);
return cellContainer;
}
async function ensureMarkdownPreviewCells(update: readonly IMarkdownCellInitialization[]): Promise<void> {
await Promise.all(update.map(async cell => {
let container = document.getElementById(cell.cellId);
if (container) {
await updateMarkdownPreview(container, cell.cellId, cell.content);
} else {
container = await createMarkdownPreview(cell.cellId, cell.content, cell.offset);
}
container.style.visibility = cell.visible ? 'visible' : 'hidden';
}));
}
function postNotebookMessage<T extends FromWebviewMessage>(
function postNotebookMessage<T extends webviewMessages.FromWebviewMessage>(
type: T['type'],
properties: Omit<T, '__vscode_notebook_message' | 'type'>
) {
@ -1177,46 +1254,6 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re
});
}
let hasPostedRenderedMathTelemetry = false;
const unsupportedKatexTermsRegex = /(\\(?:abovewithdelims|array|Arrowvert|arrowvert|atopwithdelims|bbox|bracevert|buildrel|cancelto|cases|class|cssId|ddddot|dddot|DeclareMathOperator|definecolor|displaylines|enclose|eqalign|eqalignno|eqref|hfil|hfill|idotsint|iiiint|label|leftarrowtail|leftroot|leqalignno|lower|mathtip|matrix|mbox|mit|mmlToken|moveleft|moveright|mspace|newenvironment|Newextarrow|notag|oldstyle|overparen|overwithdelims|pmatrix|raise|ref|renewenvironment|require|root|Rule|scr|shoveleft|shoveright|sideset|skew|Space|strut|style|texttip|Tiny|toggle|underparen|unicode|uproot)\b)/gi;
async function updateMarkdownPreview(previewContainerNode: HTMLElement, cellId: string, content: string | undefined) {
const previewRoot = previewContainerNode.shadowRoot;
const previewNode = previewRoot?.getElementById('preview');
if (!previewNode) {
return;
}
if (typeof content === 'string') {
if (content.trim().length === 0) {
previewContainerNode.classList.add('emptyMarkdownCell');
previewNode.innerText = '';
} else {
previewContainerNode.classList.remove('emptyMarkdownCell');
await renderers.renderMarkdown(cellId, previewNode, content);
if (!hasPostedRenderedMathTelemetry) {
const hasRenderedMath = previewNode.querySelector('.katex');
if (hasRenderedMath) {
hasPostedRenderedMathTelemetry = true;
postNotebookMessage<ITelemetryFoundRenderedMarkdownMath>('telemetryFoundRenderedMarkdownMath', {});
}
}
const matches = previewNode.innerText.match(unsupportedKatexTermsRegex);
if (matches) {
postNotebookMessage<ITelemetryFoundUnrenderedMarkdownMath>('telemetryFoundUnrenderedMarkdownMath', {
latexDirective: matches[0],
});
}
}
}
dimensionUpdater.update(cellId, previewContainerNode.clientHeight, {
isOutput: false
});
}
const markdownPreviewDragManager = new class MarkdownPreviewDragManager {
private currentDrag: { cellId: string, clientY: number } | undefined;
@ -1236,7 +1273,7 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re
}
this.currentDrag = undefined;
postNotebookMessage<ICellDropMessage>('cell-drop', {
postNotebookMessage<webviewMessages.ICellDropMessage>('cell-drop', {
cellId: drag.cellId,
ctrlKey: e.ctrlKey,
altKey: e.altKey,
@ -1258,7 +1295,7 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re
(e.target as HTMLElement).classList.add('dragging');
postNotebookMessage<ICellDragStartMessage>('cell-drag-start', {
postNotebookMessage<webviewMessages.ICellDragStartMessage>('cell-drag-start', {
cellId: cellId,
dragOffsetY: e.clientY,
});
@ -1270,7 +1307,7 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re
return;
}
postNotebookMessage<ICellDragMessage>('cell-drag', {
postNotebookMessage<webviewMessages.ICellDragMessage>('cell-drag', {
cellId: cellId,
dragOffsetY: this.currentDrag.clientY,
});
@ -1289,7 +1326,7 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re
endDrag(e: DragEvent, cellId: string) {
this.currentDrag = undefined;
(e.target as HTMLElement).classList.remove('dragging');
postNotebookMessage<ICellDragEndMessage>('cell-drag-end', {
postNotebookMessage<webviewMessages.ICellDragEndMessage>('cell-drag-end', {
cellId: cellId
});
}

View file

@ -19,19 +19,24 @@ import { BaseCellViewModel } from './baseCellViewModel';
export class CodeCellViewModel extends BaseCellViewModel implements ICellViewModel {
readonly cellKind = CellKind.Code;
protected readonly _onDidChangeOutputs = new Emitter<NotebookCellOutputsSplice[]>();
protected readonly _onDidChangeOutputs = this._register(new Emitter<NotebookCellOutputsSplice[]>());
readonly onDidChangeOutputs = this._onDidChangeOutputs.event;
private readonly _onDidRemoveOutputs = new Emitter<readonly ICellOutputViewModel[]>();
private readonly _onDidRemoveOutputs = this._register(new Emitter<readonly ICellOutputViewModel[]>());
readonly onDidRemoveOutputs = this._onDidRemoveOutputs.event;
private readonly _onDidHideInput = new Emitter<void>();
private readonly _onDidHideInput = this._register(new Emitter<void>());
readonly onDidHideInput = this._onDidHideInput.event;
private readonly _onDidHideOutputs = new Emitter<readonly ICellOutputViewModel[]>();
private readonly _onDidHideOutputs = this._register(new Emitter<readonly ICellOutputViewModel[]>());
readonly onDidHideOutputs = this._onDidHideOutputs.event;
private _outputCollection: number[] = [];
private _outputsTop: PrefixSumComputer | null = null;
protected readonly _onDidChangeLayout = new Emitter<CodeCellLayoutChangeEvent>();
protected readonly _onDidChangeLayout = this._register(new Emitter<CodeCellLayoutChangeEvent>());
readonly onDidChangeLayout = this._onDidChangeLayout.event;
private _editorHeight = 0;

View file

@ -17,6 +17,8 @@ import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/mode
import { CellKind, INotebookSearchOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { dirname } from 'vs/base/common/resources';
export class MarkdownCellViewModel extends BaseCellViewModel implements ICellViewModel {
readonly cellKind = CellKind.Markup;
@ -97,17 +99,22 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie
private readonly _onDidHideInput = new Emitter<void>();
readonly onDidHideInput = this._onDidHideInput.event;
private readonly _mdRenderer: MarkdownRenderer;
constructor(
viewType: string,
model: NotebookCellTextModel,
initialNotebookLayoutInfo: NotebookLayoutInfo | null,
readonly foldingDelegate: EditorFoldingStateDelegate,
readonly viewContext: ViewContext,
private readonly _mdRenderer: MarkdownRenderer,
@IConfigurationService configurationService: IConfigurationService,
@ITextModelService textModelService: ITextModelService,
@IInstantiationService instantiationService: IInstantiationService,
) {
super(viewType, model, UUID.generateUuid(), viewContext, configurationService, textModelService);
this._mdRenderer = this._register(instantiationService.createInstance(MarkdownRenderer, { baseUrl: dirname(model.uri) }));
const { bottomToolbarGap } = this.viewContext.notebookOptions.computeBottomToolbarDimensions(this.viewType);
this._layoutInfo = {

View file

@ -8,10 +8,8 @@ import { onUnexpectedError } from 'vs/base/common/errors';
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
import { clamp } from 'vs/base/common/numbers';
import { dirname } from 'vs/base/common/resources';
import * as strings from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
import { MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer';
import { IBulkEditService, ResourceEdit, ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService';
import { IPosition, Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
@ -1188,7 +1186,6 @@ export function createCellViewModel(instantiationService: IInstantiationService,
if (cell.cellKind === CellKind.Code) {
return instantiationService.createInstance(CodeCellViewModel, notebookViewModel.viewType, cell, notebookViewModel.layoutInfo, notebookViewModel.viewContext);
} else {
const mdRenderer = instantiationService.createInstance(MarkdownRenderer, { baseUrl: dirname(notebookViewModel.uri) });
return instantiationService.createInstance(MarkdownCellViewModel, notebookViewModel.viewType, cell, notebookViewModel.layoutInfo, notebookViewModel, notebookViewModel.viewContext, mdRenderer);
return instantiationService.createInstance(MarkdownCellViewModel, notebookViewModel.viewType, cell, notebookViewModel.layoutInfo, notebookViewModel, notebookViewModel.viewContext);
}
}

View file

@ -8,7 +8,7 @@ import { Emitter, Event } from 'vs/base/common/event';
import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { INotebookTextModel, NotebookCellOutputsSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, CellEditType, CellUri, notebookDocumentMetadataDefaults, diff, NotebookCellsChangeType, ICellDto2, TransientOptions, NotebookTextModelChangedEvent, NotebookRawContentEvent, IOutputDto, ICellOutput, IOutputItemDto, ISelectionState, NullablePartialNotebookCellMetadata, NotebookCellInternalMetadata, NullablePartialNotebookCellInternalMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { INotebookTextModel, NotebookCellOutputsSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, CellEditType, CellUri, diff, NotebookCellsChangeType, ICellDto2, TransientOptions, NotebookTextModelChangedEvent, NotebookRawContentEvent, IOutputDto, ICellOutput, IOutputItemDto, ISelectionState, NullablePartialNotebookCellMetadata, NotebookCellInternalMetadata, NullablePartialNotebookCellInternalMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { IUndoRedoService, UndoRedoElementType, IUndoRedoElement, IResourceUndoRedoElement, UndoRedoGroup, IWorkspaceUndoRedoElement } from 'vs/platform/undoRedo/common/undoRedo';
import { MoveCellEdit, SpliceCellsEdit, CellMetadataEdit } from 'vs/workbench/contrib/notebook/common/model/cellEdit';
import { ISequence, LcsDiff } from 'vs/base/common/diff/diff';
@ -197,7 +197,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
private _cellListeners: Map<number, IDisposable> = new Map();
private _cells: NotebookCellTextModel[] = [];
metadata: NotebookDocumentMetadata = notebookDocumentMetadataDefaults;
metadata: NotebookDocumentMetadata = {};
transientOptions: TransientOptions = { transientCellMetadata: {}, transientDocumentMetadata: {}, transientOutputs: false };
private _versionId = 0;

View file

@ -60,14 +60,7 @@ export enum NotebookRunState {
Idle = 2
}
export const notebookDocumentMetadataDefaults: Required<NotebookDocumentMetadata> = {
custom: {},
};
export interface NotebookDocumentMetadata {
custom?: { [key: string]: unknown; };
[key: string]: unknown;
}
export type NotebookDocumentMetadata = Record<string, unknown>;
// Aligns with the vscode.d.ts version
export enum NotebookCellExecutionState {

View file

@ -20,7 +20,7 @@ import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/v
import { NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
import { CellKind, diff, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CellKind, diff } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { NotebookOptions } from 'vs/workbench/contrib/notebook/common/notebookOptions';
import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange';
import { NotebookEditorTestModel, setupInstantiationService, withTestNotebook } from 'vs/workbench/contrib/notebook/test/testNotebookEditor';
@ -37,7 +37,7 @@ suite('NotebookViewModel', () => {
instantiationService.stub(IThemeService, new TestThemeService());
test('ctor', function () {
const notebook = new NotebookTextModel('notebook', URI.parse('test'), [], notebookDocumentMetadataDefaults, { transientCellMetadata: {}, transientDocumentMetadata: {}, transientOutputs: false }, undoRedoService, modelService, modeService);
const notebook = new NotebookTextModel('notebook', URI.parse('test'), [], {}, { transientCellMetadata: {}, transientDocumentMetadata: {}, transientOutputs: false }, undoRedoService, modelService, modeService);
const model = new NotebookEditorTestModel(notebook);
const viewContext = new ViewContext(new NotebookOptions(instantiationService.get(IConfigurationService)), new NotebookEventDispatcher());
const viewModel = new NotebookViewModel('notebook', model.notebook, viewContext, null, instantiationService, bulkEditService, undoRedoService, textModelService);

View file

@ -21,7 +21,7 @@ import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/v
import { CellViewModel, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
import { CellKind, CellUri, INotebookDiffEditorModel, INotebookEditorModel, IOutputDto, IResolvedNotebookEditorModel, NotebookCellMetadata, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CellKind, CellUri, INotebookDiffEditorModel, INotebookEditorModel, IOutputDto, IResolvedNotebookEditorModel, NotebookCellMetadata, } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange';
import { TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService';
import { IModelService } from 'vs/editor/common/services/modelService';
@ -176,7 +176,7 @@ function _createTestNotebookEditor(instantiationService: TestInstantiationServic
outputs: cell[3] ?? [],
metadata: cell[4]
};
}), notebookDocumentMetadataDefaults, { transientCellMetadata: {}, transientDocumentMetadata: {}, transientOutputs: false });
}), {}, { transientCellMetadata: {}, transientDocumentMetadata: {}, transientOutputs: false });
const model = new NotebookEditorTestModel(notebook);
const viewContext = new ViewContext(new NotebookOptions(instantiationService.get(IConfigurationService)), new NotebookEventDispatcher());

View file

@ -314,7 +314,9 @@ class OnAutoForwardedAction extends Disposable {
}
private linkMessage() {
return nls.localize('remote.tunnelsView.notificationLink', "[See all forwarded ports](command:{0}.focus)", TunnelPanel.ID);
return nls.localize(
{ key: 'remote.tunnelsView.notificationLink2', comment: ['[See all forwarded ports]({0}) is a link. Only translate `See all forwarded ports`. Do not change brackets and parentheses or {0}'] },
"[See all forwarded ports]({0})", `command:${TunnelPanel.ID}.focus`);
}
private async showNotification(tunnel: RemoteTunnel) {

View file

@ -301,7 +301,11 @@ export class RemoteStatusIndicator extends Disposable implements IWorkbenchContr
const workspaceLabel = this.getWorkspaceLabel();
if (workspaceLabel) {
const toolTip: IMarkdownString = {
value: nls.localize('workspace.tooltip', "Virtual workspace on {0}\n\n[Some features](command:{1}) are not available for resources located on a virtual file system.", workspaceLabel, LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID),
value: nls.localize(
{ key: 'workspace.tooltip2', comment: ['{0} is a remote location name, e.g. GitHub', '[Some features]({1}) is a link. Only translate `Some features`. Do not change brackets and parentheses or {1}'] },
"Virtual workspace on {0}\n\n[Some features]({1}) are not available for resources located on a virtual file system.",
workspaceLabel, `command:${LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID}`
),
isTrusted: true
};
this.renderRemoteStatusIndicator(`$(remote) ${truncate(workspaceLabel, RemoteStatusIndicator.REMOTE_STATUS_LABEL_MAX_LENGTH)}`, toolTip);

View file

@ -327,7 +327,7 @@ suite('SearchModel', () => {
}
function stub(arg1: any, arg2: any, arg3: any): sinon.SinonStub {
const stub = sinon.stub(arg1, arg2, arg3);
const stub = sinon.stub(arg1, arg2).callsFake(arg3);
restoreStubs.push(stub);
return stub;
}

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { renderStringAsPlaintext } from 'vs/base/browser/markdownRenderer';
import { Action, IAction, Separator } from 'vs/base/common/actions';
import { Action, IAction, Separator, SubmenuAction } from 'vs/base/common/actions';
import { Event } from 'vs/base/common/event';
import { MarkdownString } from 'vs/base/common/htmlContent';
import { Disposable, dispose, IDisposable, IReference, MutableDisposable } from 'vs/base/common/lifecycle';
@ -19,6 +19,7 @@ import { IModelDeltaDecoration, OverviewRulerLane, TrackedRangeStickiness } from
import { overviewRulerError, overviewRulerInfo, overviewRulerWarning } from 'vs/editor/common/view/editorColorRegistry';
import { localize } from 'vs/nls';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IThemeService, themeColorFromId, ThemeIcon } from 'vs/platform/theme/common/themeService';
@ -28,7 +29,10 @@ import { BREAKPOINT_EDITOR_CONTRIBUTION_ID, IBreakpointEditorContribution } from
import { testingRunAllIcon, testingRunIcon, testingStatesToIcons } from 'vs/workbench/contrib/testing/browser/icons';
import { TestingOutputPeekController } from 'vs/workbench/contrib/testing/browser/testingOutputPeek';
import { testMessageSeverityColors } from 'vs/workbench/contrib/testing/browser/theme';
import { IncrementalTestCollectionItem, IRichLocation, ITestMessage, TestDiffOpType, TestResultItem } from 'vs/workbench/contrib/testing/common/testCollection';
import { DefaultGutterClickAction, getTestingConfiguration, TestingConfigKeys } from 'vs/workbench/contrib/testing/common/configuration';
import { labelForTestInState } from 'vs/workbench/contrib/testing/common/constants';
import { IncrementalTestCollectionItem, InternalTestItem, IRichLocation, ITestMessage, TestDiffOpType, TestResultItem } from 'vs/workbench/contrib/testing/common/testCollection';
import { maxPriority } from 'vs/workbench/contrib/testing/common/testingStates';
import { buildTestUri, TestUriType } from 'vs/workbench/contrib/testing/common/testingUri';
import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService';
import { IMainThreadTestCollection, ITestService } from 'vs/workbench/contrib/testing/common/testService';
@ -173,8 +177,15 @@ export class TestingDecorations extends Disposable implements IEditorContributio
for (const test of ref.object.all) {
const stateLookup = this.results.getStateById(test.item.extId);
if (test.item.range) {
newDecorations.push(this.instantiationService.createInstance(
RunTestDecoration, test, ref.object, test.item.range, this.editor, stateLookup?.[1]));
const line = test.item.range.startLineNumber;
const resultItem = stateLookup?.[1];
const existing = newDecorations.findIndex(d => d instanceof RunTestDecoration && d.line === line);
if (existing !== -1) {
newDecorations[existing] = (newDecorations[existing] as RunTestDecoration).merge(test, ref.object, resultItem);
} else {
newDecorations.push(this.instantiationService.createInstance(
RunSingleTestDecoration, test, ref.object, this.editor, stateLookup?.[1]));
}
}
if (!stateLookup) {
@ -249,116 +260,179 @@ const firstLineRange = (originalRange: IRange) => ({
endColumn: 1,
});
class RunTestDecoration extends Disposable implements ITestDecoration {
/**
* @inheritdoc
*/
id = '';
/**
* @inheritdoc
*/
public readonly editorDecoration: IModelDeltaDecoration;
private line: number;
constructor(
private readonly test: IncrementalTestCollectionItem,
private readonly collection: IMainThreadTestCollection,
range: IRange,
private readonly editor: ICodeEditor,
stateItem: TestResultItem | undefined,
@ITestService private readonly testService: ITestService,
@IContextMenuService private readonly contextMenuService: IContextMenuService,
@ICommandService private readonly commandService: ICommandService,
) {
super();
this.line = range.startLineNumber;
const icon = stateItem?.computedState !== undefined && stateItem.computedState !== TestResultState.Unset
? testingStatesToIcons.get(stateItem.computedState)!
: test.children.size > 0 ? testingRunAllIcon : testingRunIcon;
const hoverMessage = new MarkdownString('', true).appendText(localize('failedHoverMessage', '{0} has failed. ', test.item.label));
if (stateItem?.tasks.some(s => s.messages.length > 0)) {
const args = encodeURIComponent(JSON.stringify([test.item.extId]));
hoverMessage.appendMarkdown(`[${localize('failedPeekAction', 'Peek Error')}](command:vscode.peekTestError?${args})`);
}
let glyphMarginClassName = ThemeIcon.asClassName(icon) + ' testing-run-glyph';
if (stateItem?.retired) {
glyphMarginClassName += ' retired';
}
this.editorDecoration = {
range: firstLineRange(range),
options: {
description: 'run-test-decoration',
isWholeLine: true,
hoverMessage,
glyphMarginClassName,
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
glyphMarginHoverMessage: new MarkdownString().appendText(localize('testing.clickToRun', 'Click to run tests, right click for more options')),
}
};
const createRunTestDecoration = (tests: readonly IncrementalTestCollectionItem[], states: readonly (TestResultItem | undefined)[]): IModelDeltaDecoration => {
const range = tests[0]?.item.range;
if (!range) {
throw new Error('Test decorations can only be created for tests with a range');
}
/**
* @inheritdoc
*/
let computedState = TestResultState.Unset;
let hoverMessageParts: string[] = [];
let testIdWithMessages: string | undefined;
let retired = false;
for (let i = 0; i < tests.length; i++) {
const test = tests[i];
const resultItem = states[i];
const state = resultItem?.computedState ?? TestResultState.Unset;
hoverMessageParts.push(labelForTestInState(test.item.label, state));
computedState = maxPriority(computedState, state);
retired = retired || !!resultItem?.retired;
if (!testIdWithMessages && resultItem?.tasks.some(t => t.messages.length)) {
testIdWithMessages = test.item.extId;
}
}
const hasMultipleTests = tests.length > 1 || tests[0].children.size > 0;
const icon = computedState === TestResultState.Unset
? (hasMultipleTests ? testingRunAllIcon : testingRunIcon)
: testingStatesToIcons.get(computedState)!;
const hoverMessage = new MarkdownString('', true).appendText(hoverMessageParts.join(', ') + '.');
if (testIdWithMessages) {
const args = encodeURIComponent(JSON.stringify([testIdWithMessages]));
hoverMessage.appendMarkdown(`[${localize('peekTestOutout', 'Peek Test Output')}](command:vscode.peekTestError?${args})`);
}
let glyphMarginClassName = ThemeIcon.asClassName(icon) + ' testing-run-glyph';
if (retired) {
glyphMarginClassName += ' retired';
}
return {
range: firstLineRange(range),
options: {
description: 'run-test-decoration',
isWholeLine: true,
hoverMessage,
glyphMarginClassName,
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
}
};
};
abstract class RunTestDecoration extends Disposable {
/** @inheritdoc */
public id = '';
public get line() {
return this.editorDecoration.range.startLineNumber;
}
constructor(
public editorDecoration: IModelDeltaDecoration,
protected readonly editor: ICodeEditor,
@ITestService protected readonly testService: ITestService,
@IContextMenuService protected readonly contextMenuService: IContextMenuService,
@ICommandService protected readonly commandService: ICommandService,
@IConfigurationService protected readonly configurationService: IConfigurationService,
) {
super();
editorDecoration.options.glyphMarginHoverMessage = new MarkdownString().appendText(this.getGutterLabel());
}
/** @inheritdoc */
public click(e: IEditorMouseEvent): boolean {
if (e.target.position?.lineNumber !== this.line || e.target.type !== MouseTargetType.GUTTER_GLYPH_MARGIN) {
return false;
}
if (e.event.rightButton) {
const actions = this.getContextMenu();
this.contextMenuService.showContextMenu({
getAnchor: () => ({ x: e.event.posx, y: e.event.posy }),
getActions: () => actions,
onHide: () => dispose(actions),
});
} else {
// todo: customize click behavior
this.testService.runTests({
tests: [{ testId: this.test.item.extId, src: this.test.src }],
debug: false,
});
this.showContextMenu(e);
return true;
}
switch (getTestingConfiguration(this.configurationService, TestingConfigKeys.DefaultGutterClickAction)) {
case DefaultGutterClickAction.ContextMenu:
this.showContextMenu(e);
break;
case DefaultGutterClickAction.Debug:
this.defaultDebug();
break;
case DefaultGutterClickAction.Run:
default:
this.defaultRun();
break;
}
return true;
}
public override dispose() {
// no-op
/**
* Adds the test to this decoration.
*/
public abstract merge(other: IncrementalTestCollectionItem, collection: IMainThreadTestCollection, resultItem: TestResultItem | undefined): RunTestDecoration;
/**
* Called when the decoration is clicked on.
*/
protected abstract getContextMenuActions(e: IEditorMouseEvent): IAction[];
/**
* Default run action.
*/
protected abstract defaultRun(): void;
/**
* Default debug action.
*/
protected abstract defaultDebug(): void;
private showContextMenu(e: IEditorMouseEvent) {
let actions = this.getContextMenuActions(e);
const model = this.editor.getModel();
if (model) {
actions = Separator.join(
actions,
this.editor
.getContribution<IBreakpointEditorContribution>(BREAKPOINT_EDITOR_CONTRIBUTION_ID)
.getContextMenuActionsAtPosition(this.line, model)
);
}
this.contextMenuService.showContextMenu({
getAnchor: () => ({ x: e.event.posx, y: e.event.posy }),
getActions: () => actions,
onHide: () => dispose(actions),
});
}
private getContextMenu() {
const model = this.editor.getModel();
if (!model) {
return [];
private getGutterLabel() {
switch (getTestingConfiguration(this.configurationService, TestingConfigKeys.DefaultGutterClickAction)) {
case DefaultGutterClickAction.ContextMenu:
return localize('testing.gutterMsg.contextMenu', 'Click for test options');
case DefaultGutterClickAction.Debug:
return localize('testing.gutterMsg.debug', 'Click to debug tests, right click for more options');
case DefaultGutterClickAction.Run:
default:
return localize('testing.gutterMsg.run', 'Click to run tests, right click for more options');
}
}
/**
* Gets context menu actions relevant for a singel test.
*/
protected getTestContextMenuActions(collection: IMainThreadTestCollection, test: InternalTestItem) {
const testActions: IAction[] = [];
if (this.test.item.runnable) {
testActions.push(new Action('testing.run', localize('run test', 'Run Test'), undefined, undefined, () => this.testService.runTests({
if (test.item.runnable) {
testActions.push(new Action('testing.gutter.run', localize('run test', 'Run Test'), undefined, undefined, () => this.testService.runTests({
debug: false,
tests: [{ src: this.test.src, testId: this.test.item.extId }],
tests: [{ src: test.src, testId: test.item.extId }],
})));
}
if (this.test.item.debuggable) {
testActions.push(new Action('testing.debug', localize('debug test', 'Debug Test'), undefined, undefined, () => this.testService.runTests({
if (test.item.debuggable) {
testActions.push(new Action('testing.gutter.debug', localize('debug test', 'Debug Test'), undefined, undefined, () => this.testService.runTests({
debug: true,
tests: [{ src: this.test.src, testId: this.test.item.extId }],
tests: [{ src: test.src, testId: test.item.extId }],
})));
}
testActions.push(new Action('testing.reveal', localize('reveal test', 'Reveal in Test Explorer'), undefined, undefined, async () => {
const path = [this.test];
testActions.push(new Action('testing.gutter.reveal', localize('reveal test', 'Reveal in Test Explorer'), undefined, undefined, async () => {
const path = [test];
while (true) {
const parentId = path[0].parent;
const parent = parentId && this.collection.getNodeById(parentId);
const parent = parentId && collection.getNodeById(parentId);
if (!parent) {
break;
}
@ -369,11 +443,112 @@ class RunTestDecoration extends Disposable implements ITestDecoration {
await this.commandService.executeCommand('vscode.revealTestInExplorer', path.map(t => t.item.extId));
}));
const breakpointActions = this.editor
.getContribution<IBreakpointEditorContribution>(BREAKPOINT_EDITOR_CONTRIBUTION_ID)
.getContextMenuActionsAtPosition(this.line, model);
return testActions;
}
}
return breakpointActions.length ? [...testActions, new Separator(), ...breakpointActions] : testActions;
class MultiRunTestDecoration extends RunTestDecoration implements ITestDecoration {
constructor(
private readonly tests: {
test: IncrementalTestCollectionItem,
collection: IMainThreadTestCollection,
resultItem: TestResultItem | undefined,
}[],
editor: ICodeEditor,
@ITestService testService: ITestService,
@ICommandService commandService: ICommandService,
@IContextMenuService contextMenuService: IContextMenuService,
@IConfigurationService configurationService: IConfigurationService,
) {
super(createRunTestDecoration(tests.map(t => t.test), tests.map(t => t.resultItem)), editor, testService, contextMenuService, commandService, configurationService);
}
public override merge(test: IncrementalTestCollectionItem, collection: IMainThreadTestCollection, resultItem: TestResultItem | undefined): RunTestDecoration {
this.tests.push({ collection, test, resultItem });
this.editorDecoration = createRunTestDecoration(this.tests.map(t => t.test), this.tests.map(t => t.resultItem));
return this;
}
protected override getContextMenuActions() {
const allActions: IAction[] = [];
if (this.tests.some(({ test }) => test.item.runnable)) {
allActions.push(new Action('testing.gutter.runAll', localize('run all test', 'Run All Tests'), undefined, undefined, () => this.defaultRun()));
}
if (this.tests.some(({ test }) => test.item.debuggable)) {
allActions.push(new Action('testing.gutter.debugAll', localize('debug all test', 'Debug All Tests'), undefined, undefined, () => this.defaultDebug()));
}
const testSubmenus = this.tests.map(({ collection, test }) =>
new SubmenuAction(test.item.extId, test.item.label, this.getTestContextMenuActions(collection, test)));
return Separator.join(allActions, testSubmenus);
}
protected override defaultRun() {
return this.testService.runTests({
tests: this.tests
.filter(({ test }) => test.item.runnable)
.map(({ test }) => ({ testId: test.item.extId, src: test.src })),
debug: false,
});
}
protected override defaultDebug() {
return this.testService.runTests({
tests: this.tests
.filter(({ test }) => test.item.debuggable)
.map(({ test }) => ({ testId: test.item.extId, src: test.src })),
debug: true,
});
}
}
class RunSingleTestDecoration extends RunTestDecoration implements ITestDecoration {
constructor(
private readonly test: IncrementalTestCollectionItem,
private readonly collection: IMainThreadTestCollection,
editor: ICodeEditor,
private readonly resultItem: TestResultItem | undefined,
@ITestService testService: ITestService,
@ICommandService commandService: ICommandService,
@IContextMenuService contextMenuService: IContextMenuService,
@IConfigurationService configurationService: IConfigurationService,
) {
super(createRunTestDecoration([test], [resultItem]), editor, testService, contextMenuService, commandService, configurationService);
}
public override merge(test: IncrementalTestCollectionItem, collection: IMainThreadTestCollection, resultItem: TestResultItem | undefined): RunTestDecoration {
return new MultiRunTestDecoration([
{ collection: this.collection, test: this.test, resultItem: this.resultItem },
{ collection, test, resultItem },
], this.editor, this.testService, this.commandService, this.contextMenuService, this.configurationService);
}
protected override getContextMenuActions(e: IEditorMouseEvent) {
return this.getTestContextMenuActions(this.collection, this.test);
}
protected override defaultRun() {
if (!this.test.item.runnable) {
return;
}
return this.testService.runTests({
tests: [{ testId: this.test.item.extId, src: this.test.src }],
debug: false,
});
}
protected override defaultDebug() {
if (!this.test.item.debuggable) {
return;
}
return this.testService.runTests({
tests: [{ testId: this.test.item.extId, src: this.test.src }],
debug: true,
});
}
}

View file

@ -51,12 +51,12 @@ import { HierarchicalByNameProjection } from 'vs/workbench/contrib/testing/brows
import { IActionableTestTreeElement, isActionableTestTreeElement, ITestTreeProjection, TestExplorerTreeElement, TestItemTreeElement, TestTreeErrorMessage, TestTreeWorkspaceFolder } from 'vs/workbench/contrib/testing/browser/explorerProjections/index';
import { testingHiddenIcon, testingStatesToIcons } from 'vs/workbench/contrib/testing/browser/icons';
import { ITestExplorerFilterState, TestExplorerFilterState, TestingExplorerFilter } from 'vs/workbench/contrib/testing/browser/testingExplorerFilter';
import { ITestingPeekOpener } from 'vs/workbench/contrib/testing/common/testingPeekOpener';
import { ITestingProgressUiService } from 'vs/workbench/contrib/testing/browser/testingProgressUiService';
import { getTestingConfiguration, TestingConfigKeys } from 'vs/workbench/contrib/testing/common/configuration';
import { TestExplorerStateFilter, TestExplorerViewMode, TestExplorerViewSorting, Testing, testStateNames } from 'vs/workbench/contrib/testing/common/constants';
import { labelForTestInState, TestExplorerStateFilter, TestExplorerViewMode, TestExplorerViewSorting, Testing, testStateNames } from 'vs/workbench/contrib/testing/common/constants';
import { TestIdPath, TestItemExpandState } from 'vs/workbench/contrib/testing/common/testCollection';
import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys';
import { ITestingPeekOpener } from 'vs/workbench/contrib/testing/common/testingPeekOpener';
import { cmpPriority, isFailedState, isStateWithResult } from 'vs/workbench/contrib/testing/common/testingStates';
import { getPathForTestInResult, TestResultItemChangeReason } from 'vs/workbench/contrib/testing/common/testResult';
import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService';
@ -830,10 +830,7 @@ class TestExplorerActionRunner extends ActionRunner {
}
const getLabelForTestTreeElement = (element: IActionableTestTreeElement) => {
let label = localize({
key: 'testing.treeElementLabel',
comment: ['label then the unit tests state, for example "Addition Tests (Running)"'],
}, '{0} ({1})', element.label, testStateNames[element.state]);
let label = labelForTestInState(element.label, element.state);
if (element instanceof TestItemTreeElement) {
if (element.duration !== undefined) {

View file

@ -64,7 +64,7 @@ import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingC
import { ITestingPeekOpener } from 'vs/workbench/contrib/testing/common/testingPeekOpener';
import { isFailedState } from 'vs/workbench/contrib/testing/common/testingStates';
import { buildTestUri, ParsedTestUri, parseTestUri, TestUriType } from 'vs/workbench/contrib/testing/common/testingUri';
import { getPathForTestInResult, ITestResult, maxCountPriority, TestResultItemChange, TestResultItemChangeReason } from 'vs/workbench/contrib/testing/common/testResult';
import { getPathForTestInResult, ITestResult, maxCountPriority, resultItemParents, TestResultItemChange, TestResultItemChangeReason } from 'vs/workbench/contrib/testing/common/testResult';
import { ITestResultService, ResultChangeEvent } from 'vs/workbench/contrib/testing/common/testResultService';
import { getAllTestsInHierarchy, ITestService } from 'vs/workbench/contrib/testing/common/testService';
import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
@ -196,9 +196,11 @@ export class TestingPeekOpener extends Disposable implements ITestingPeekOpener
// don't show the peek if the user asked to only auto-open peeks for visible tests,
// and this test is not in any of the editors' models.
const testUri = evt.item.item.uri?.toString();
if (cfg === AutoOpenPeekViewWhen.FailureVisible && (!testUri || !editors.some(e => e.getModel()?.uri.toString() === testUri))) {
return;
if (cfg === AutoOpenPeekViewWhen.FailureVisible) {
const editorUris = new Set(editors.map(e => e.getModel()?.uri.toString()));
if (!Iterable.some(resultItemParents(evt.result, evt.item), i => i.item.uri && editorUris.has(i.item.uri.toString()))) {
return;
}
}
const controllers = editors.map(TestingOutputPeekController.get);
@ -912,20 +914,12 @@ export class TestCaseElement implements ITreeElement {
private readonly results: ITestResult,
public readonly test: TestResultItem,
) {
for (const parent of this.parents()) {
this.description = this.description
? parent.item.label + flatTestItemDelimiter + this.description
: parent.item.label;
}
}
private *parents() {
for (
let parent = this.test.parent && this.results.getStateById(this.test.parent);
parent;
parent = parent.parent && this.results.getStateById(parent.parent)
) {
yield parent;
for (const parent of resultItemParents(results, test)) {
if (parent !== test) {
this.description = this.description
? parent.item.label + flatTestItemDelimiter + this.description
: parent.item.label;
}
}
}
}

View file

@ -13,6 +13,7 @@ export const enum TestingConfigKeys {
AutoOpenPeekView = 'testing.automaticallyOpenPeekView',
AutoOpenPeekViewDuringAutoRun = 'testing.automaticallyOpenPeekViewDuringAutoRun',
FollowRunningTest = 'testing.followRunningTest',
DefaultGutterClickAction = 'testing.defaultGutterClickAction',
}
export const enum AutoOpenPeekViewWhen {
@ -25,6 +26,12 @@ export const enum AutoRunMode {
OnlyPreviouslyRun = 'rerun',
}
export const enum DefaultGutterClickAction {
Run = 'run',
Debug = 'debug',
ContextMenu = 'contextMenu',
}
export const testingConfiguation: IConfigurationNode = {
id: 'testing',
order: 21,
@ -71,6 +78,20 @@ export const testingConfiguation: IConfigurationNode = {
type: 'boolean',
default: true,
},
[TestingConfigKeys.DefaultGutterClickAction]: {
description: localize('testing.defaultGutterClickAction', 'Controls the action to take when left-clicking on a test decoration in the gutter.'),
enum: [
DefaultGutterClickAction.Run,
DefaultGutterClickAction.Debug,
DefaultGutterClickAction.ContextMenu,
],
enumDescriptions: [
localize('testing.defaultGutterClickAction.run', 'Run the test.'),
localize('testing.defaultGutterClickAction.debug', 'Debug the test.'),
localize('testing.defaultGutterClickAction.contextMenu', 'Open the context menu for more options.'),
],
default: DefaultGutterClickAction.Run,
},
}
};
@ -80,6 +101,7 @@ export interface ITestingConfiguration {
[TestingConfigKeys.AutoOpenPeekView]: AutoOpenPeekViewWhen;
[TestingConfigKeys.AutoOpenPeekViewDuringAutoRun]: boolean;
[TestingConfigKeys.FollowRunningTest]: boolean;
[TestingConfigKeys.DefaultGutterClickAction]: DefaultGutterClickAction;
}
export const getTestingConfiguration = <K extends TestingConfigKeys>(config: IConfigurationService, key: K) => config.getValue<ITestingConfiguration[K]>(key);

View file

@ -38,5 +38,10 @@ export const testStateNames: { [K in TestResultState]: string } = {
[TestResultState.Queued]: localize('testState.queued', 'Queued'),
[TestResultState.Running]: localize('testState.running', 'Running'),
[TestResultState.Skipped]: localize('testState.skipped', 'Skipped'),
[TestResultState.Unset]: localize('testState.unset', 'Unset'),
[TestResultState.Unset]: localize('testState.unset', 'Not yet run'),
};
export const labelForTestInState = (label: string, state: TestResultState) => localize({
key: 'testing.treeElementLabel',
comment: ['label then the unit tests state, for example "Addition Tests (Running)"'],
}, '{0} ({1})', label, testStateNames[state]);

View file

@ -69,19 +69,21 @@ export interface ITestResult {
toJSON(): ISerializedTestResults | undefined;
}
export const getPathForTestInResult = (test: TestResultItem, results: ITestResult): TestIdPath => {
const path = [test];
while (true) {
const parentId = path[0].parent;
const parent = parentId && results.getStateById(parentId);
if (!parent) {
break;
}
export const resultItemParents = function* (results: ITestResult, item: TestResultItem) {
let i: TestResultItem | undefined = item;
while (i) {
yield i;
i = i.parent ? results.getStateById(i.parent) : undefined;
}
};
path.unshift(parent);
export const getPathForTestInResult = (test: TestResultItem, results: ITestResult): TestIdPath => {
const path: TestIdPath = [];
for (const node of resultItemParents(results, test)) {
path.unshift(node.item.extId);
}
return path.map(t => t.item.extId);
return path;
};
/**

View file

@ -69,10 +69,10 @@ export interface IMainThreadTestCollection extends AbstractIncrementalTestCollec
* Iterates through the item and its parents to the root.
*/
export const getCollectionItemParents = function* (collection: IMainThreadTestCollection, item: InternalTestItem) {
let p: InternalTestItem | undefined = item;
while (p) {
yield p;
p = p.parent ? collection.getNodeById(p.parent) : undefined;
let i: InternalTestItem | undefined = item;
while (i) {
yield i;
i = i.parent ? collection.getNodeById(i.parent) : undefined;
}
};

View file

@ -10,7 +10,7 @@ import { Lazy } from 'vs/base/common/lazy';
import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
import { NullLogService } from 'vs/platform/log/common/log';
import { ITestTaskState, TestResultItem } from 'vs/workbench/contrib/testing/common/testCollection';
import { HydratedTestResult, LiveOutputController, LiveTestResult, makeEmptyCounts, TestResultItemChange, TestResultItemChangeReason } from 'vs/workbench/contrib/testing/common/testResult';
import { getPathForTestInResult, HydratedTestResult, LiveOutputController, LiveTestResult, makeEmptyCounts, resultItemParents, TestResultItemChange, TestResultItemChangeReason } from 'vs/workbench/contrib/testing/common/testResult';
import { TestResultService } from 'vs/workbench/contrib/testing/common/testResultService';
import { InMemoryResultStorage, ITestResultStorage } from 'vs/workbench/contrib/testing/common/testResultStorage';
import { Convert, ReExportedTestRunState as TestRunState, TestItemImpl, TestResultState, testStubs, testStubsChain } from 'vs/workbench/contrib/testing/common/testStubs';
@ -291,4 +291,24 @@ suite('Workbench - Test Results Service', () => {
assert.deepStrictEqual(results.results, [r, hydrated1, hydrated2]);
});
});
test('resultItemParents', () => {
assert.deepStrictEqual([...resultItemParents(r, r.getStateById('id-aa')!)], [
r.getStateById('id-aa'),
r.getStateById('id-a'),
r.getStateById('id-root'),
]);
assert.deepStrictEqual([...resultItemParents(r, r.getStateById('id-root')!)], [
r.getStateById('id-root'),
]);
});
test('getPathForTestInResult', () => {
assert.deepStrictEqual([...getPathForTestInResult(r.getStateById('id-aa')!, r)], [
'id-root',
'id-a',
'id-aa',
]);
});
});

View file

@ -5,7 +5,7 @@
import 'vs/css!./gettingStarted';
import { localize } from 'vs/nls';
import { IInstantiationService, optional } from 'vs/platform/instantiation/common/instantiation';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IEditorInputSerializer, IEditorOpenContext } from 'vs/workbench/common/editor';
import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { assertIsDefined } from 'vs/base/common/types';
@ -17,7 +17,7 @@ import { IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platfor
import { welcomePageBackground, welcomePageProgressBackground, welcomePageProgressForeground, welcomePageTileBackground, welcomePageTileHoverBackground, welcomePageTileShadow } from 'vs/workbench/contrib/welcome/page/browser/welcomePageColors';
import { activeContrastBorder, buttonBackground, buttonForeground, buttonHoverBackground, contrastBorder, descriptionForeground, focusBorder, foreground, textLinkActiveForeground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { ITelemetryService, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry';
import { firstSessionDateStorageKey, ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { gettingStartedCheckedCodicon, gettingStartedUncheckedCodicon } from 'vs/workbench/contrib/welcome/gettingStarted/browser/gettingStartedIcons';
import { IOpenerService, matchesScheme } from 'vs/platform/opener/common/opener';
@ -27,7 +27,6 @@ import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storag
import { CancellationToken } from 'vs/base/common/cancellation';
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ContextKeyExpr, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { ITASExperimentService } from 'vs/workbench/services/experiment/common/experimentService';
import { IRecentFolder, IRecentlyOpened, IRecentWorkspace, isRecentFolder, isRecentWorkspace, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { onUnexpectedError } from 'vs/base/common/errors';
@ -103,7 +102,6 @@ export class GettingStartedPage extends EditorPane {
private container: HTMLElement;
private contextService: IContextKeyService;
private tasExperimentService?: ITASExperimentService;
private previousSelection?: string;
private recentlyOpened: Promise<IRecentlyOpened>;
private selectedStepElement?: HTMLDivElement;
@ -144,7 +142,6 @@ export class GettingStartedPage extends EditorPane {
@IHostService private readonly hostService: IHostService,
@IWebviewService private readonly webviewService: IWebviewService,
@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
@optional(ITASExperimentService) tasExperimentService: ITASExperimentService,
) {
super(GettingStartedPage.ID, telemetryService, themeService, storageService);
@ -158,9 +155,6 @@ export class GettingStartedPage extends EditorPane {
this.stepMediaComponent = $('.getting-started-media');
this.stepMediaComponent.id = generateUuid();
this.tasExperimentService = tasExperimentService;
this.contextService = this._register(contextService.createScoped(this.container));
inGettingStartedContext.bindTo(this.contextService).set(true);
@ -797,28 +791,19 @@ export class GettingStartedPage extends EditorPane {
const someStepsComplete = this.gettingStartedCategories.some(categry => categry.content.type === 'steps' && categry.content.stepsComplete);
if (!someStepsComplete && !this.hasScrolledToFirstCategory) {
const fistContentBehaviour =
!this.storageService.get(lastSessionDateStorageKey, StorageScope.GLOBAL) // isNewUser ?
? 'openToFirstCategory'
: await Promise.race([
this.tasExperimentService?.getTreatment<'index' | 'openToFirstCategory'>('GettingStartedFirstContent'),
new Promise<'index'>(resolve => setTimeout(() => resolve('index'), 1000)),
]);
const firstSessionDateString = this.storageService.get(firstSessionDateStorageKey, StorageScope.GLOBAL) || new Date().toUTCString();
const daysSinceFirstSession = ((+new Date()) - (+new Date(firstSessionDateString))) / 1000 / 60 / 60 / 24;
const fistContentBehaviour = daysSinceFirstSession < 1 ? 'openToFirstCategory' : 'index';
if (this.gettingStartedCategories.some(category => category.content.type === 'steps' && category.content.stepsComplete)) {
this.setSlide('categories');
return;
} else {
if (fistContentBehaviour === 'openToFirstCategory') {
const first = this.gettingStartedCategories.find(category => category.content.type === 'steps');
this.hasScrolledToFirstCategory = true;
if (first) {
this.currentCategory = first;
this.editorInput.selectedCategory = this.currentCategory?.id;
this.buildCategorySlide(this.editorInput.selectedCategory);
this.setSlide('details');
return;
}
if (fistContentBehaviour === 'openToFirstCategory') {
const first = this.gettingStartedCategories.find(category => category.content.type === 'steps');
this.hasScrolledToFirstCategory = true;
if (first) {
this.currentCategory = first;
this.editorInput.selectedCategory = this.currentCategory?.id;
this.buildCategorySlide(this.editorInput.selectedCategory);
this.setSlide('details');
return;
}
}
}

View file

@ -7,6 +7,8 @@ import { localize } from 'vs/nls';
import { IStartEntry, IWalkthrough } from 'vs/platform/extensions/common/extensions';
import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry';
const titleTranslated = localize('title', "Title");
export const walkthroughsExtensionPoint = ExtensionsRegistry.registerExtensionPoint<IWalkthrough[]>({
extensionPoint: 'walkthroughs',
jsonSchema: {
@ -64,10 +66,10 @@ export const walkthroughsExtensionPoint = ExtensionsRegistry.registerExtensionPo
},
description: {
type: 'string',
description: localize('walkthroughs.steps.description', "Description of step. Supports ``preformatted``, __italic__, and **bold** text. Use markdown-style links for commands or external links: [Title](command:myext.command), [Title](command:toSide:myext.command), or [Title](https://aka.ms). Links on their own line will be rendered as buttons.")
description: localize('walkthroughs.steps.description.interpolated', "Description of step. Supports ``preformatted``, __italic__, and **bold** text. Use markdown-style links for commands or external links: {0}, {1}, or {2}. Links on their own line will be rendered as buttons.", `[${titleTranslated}](command:myext.command)`, `[${titleTranslated}](command:toSide:myext.command)`, `[${titleTranslated}](https://aka.ms)`)
},
button: {
deprecationMessage: localize('walkthroughs.steps.button.deprecated', "Deprecated. Use markdown links in the description instead, i.e. [Title](command:myext.command), [Title](command:toSide:myext.command), or [Title](https://aka.ms), "),
deprecationMessage: localize('walkthroughs.steps.button.deprecated.interpolated', "Deprecated. Use markdown links in the description instead, i.e. {0}, {1}, or {2}", `[${titleTranslated}](command:myext.command)`, `[${titleTranslated}](command:toSide:myext.command)`, `[${titleTranslated}](https://aka.ms)`),
},
media: {
type: 'object',

View file

@ -118,6 +118,8 @@ export const startEntries: GettingStartedStartEntryContent = [
},
];
const Button = (title: string, href: string) => `[${title}](${href})`;
export const walkthroughs: GettingStartedWalkthroughContent = [
{
id: 'Setup',
@ -131,14 +133,17 @@ export const walkthroughs: GettingStartedWalkthroughContent = [
{
id: 'pickColorTheme',
title: localize('gettingStarted.pickColor.title', "Choose the look you want"),
description: localize('gettingStarted.pickColor.description', "The right color palette helps you focus on your code, is easy on your eyes, and is simply more fun to use.\n[Browse Color Themes](command:workbench.action.selectTheme)"),
completionEvents: ['onSettingChanged:workbench.colorTheme'],
description: localize('gettingStarted.pickColor.description.interpolated', "The right color palette helps you focus on your code, is easy on your eyes, and is simply more fun to use.\n{0}", Button(localize('titleID', "Browse Color Themes"), 'command:workbench.action.selectTheme')),
completionEvents: [
'onSettingChanged:workbench.colorTheme',
'onCommand:workbench.action.selectTheme'
],
media: { type: 'markdown', path: 'example_markdown_media', }
},
{
id: 'findLanguageExtensions',
title: localize('gettingStarted.findLanguageExts.title', "Rich support for all your languages"),
description: localize('gettingStarted.findLanguageExts.description', "Code smarter with syntax highlighting, code completion, linting and debugging. While many languages are built-in, many more can be added as extensions.\n[Browse Language Extensions](command:workbench.extensions.action.showLanguageExtensions)"),
description: localize('gettingStarted.findLanguageExts.description.interpolated', "Code smarter with syntax highlighting, code completion, linting and debugging. While many languages are built-in, many more can be added as extensions.\n{0}", Button(localize('browseLangExts', "Browse Language Extensions"), 'command:workbench.extensions.action.showLanguageExtensions')),
media: {
type: 'image', altText: 'Language extensions', path: {
dark: 'dark/languageExtensions.png',
@ -150,7 +155,7 @@ export const walkthroughs: GettingStartedWalkthroughContent = [
{
id: 'commandPaletteTask',
title: localize('gettingStarted.commandPalette.title', "One shortcut to access everything"),
description: localize('gettingStarted.commandPalette.description', "Commands Palette is the keyboard way to accomplish any task in VS Code. **Practice** by looking up your frequently used commands to save time and keep in the flow.\n[Open Command Palette](command:workbench.action.showCommands)\n__Try searching for 'view toggle'.__"),
description: localize('gettingStarted.commandPalette.description.interpolated', "Commands Palette is the keyboard way to accomplish any task in VS Code. **Practice** by looking up your frequently used commands to save time and keep in the flow.\n{0}\n__Try searching for 'view toggle'.__", Button(localize('commandPalette', "Open Command Palette"), 'command:workbench.action.showCommands')),
media: {
type: 'image', altText: 'Command Palette overlay for searching and executing commands.', path: {
dark: 'dark/commandPalette.png',
@ -162,7 +167,7 @@ export const walkthroughs: GettingStartedWalkthroughContent = [
{
id: 'workspaceTrust',
title: localize('gettingStarted.workspaceTrust.title', "Safely browse and edit code"),
description: localize('gettingStarted.workspaceTrust.description', "[Workspace Trust](https://github.com/microsoft/vscode-docs/blob/workspaceTrust/docs/editor/workspace-trust.md) lets you decide whether your project folders should **allow or restrict** automatic code execution __(required for extensions, debugging, etc)__.\nOpening a file/folder will prompt to grant trust. You can always [enable trust](command:toSide:workbench.action.manageTrustedDomain) later."),
description: localize('gettingStarted.workspaceTrust.description.interpolated', "{0} lets you decide whether your project folders should **allow or restrict** automatic code execution __(required for extensions, debugging, etc)__.\nOpening a file/folder will prompt to grant trust. You can always {1} later.", Button(localize('workspaceTrust', "Workspace Trust"), 'https://github.com/microsoft/vscode-docs/blob/workspaceTrust/docs/editor/workspace-trust.md'), Button(localize('enableTrust', "enable trust"), 'command:toSide:workbench.action.manageTrustedDomain')),
when: '!isWorkspaceTrusted && workspaceFolderCount == 0',
media: {
type: 'image', altText: 'Workspace Trust editor in Restricted mode and a primary button for switching to Trusted mode.', path: {
@ -175,7 +180,7 @@ export const walkthroughs: GettingStartedWalkthroughContent = [
{
id: 'pickAFolderTask-Mac',
title: localize('gettingStarted.setup.OpenFolder.title', "Open up your code"),
description: localize('gettingStarted.setup.OpenFolder.description', "You're all set to start coding. Open a project folder to get your files into VS Code.\n[Pick a Folder](command:workbench.action.files.openFileFolder)"),
description: localize('gettingStarted.setup.OpenFolder.description.interpolated', "You're all set to start coding. Open a project folder to get your files into VS Code.\n{0}", Button(localize('pickFolder', "Pick a Folder"), 'command:workbench.action.files.openFileFolder')),
when: 'isMac && workspaceFolderCount == 0',
media: {
type: 'image', altText: 'Explorer view showing buttons for opening folder and cloning repository.', path: {
@ -188,7 +193,7 @@ export const walkthroughs: GettingStartedWalkthroughContent = [
{
id: 'pickAFolderTask-Other',
title: localize('gettingStarted.setup.OpenFolder.title', "Open up your code"),
description: localize('gettingStarted.setup.OpenFolder.description2', "You're all set to start coding. Open a project folder to get your files into VS Code.\n[Pick a Folder](command:workbench.action.files.openFolder)"),
description: localize('gettingStarted.setup.OpenFolder.description.interpolated', "You're all set to start coding. Open a project folder to get your files into VS Code.\n{0}", Button(localize('pickFolder', "Pick a Folder"), 'command:workbench.action.files.openFolder')),
when: '!isMac && workspaceFolderCount == 0',
media: {
type: 'image', altText: 'Explorer view showing buttons for opening folder and cloning repository.', path: {
@ -201,7 +206,7 @@ export const walkthroughs: GettingStartedWalkthroughContent = [
{
id: 'quickOpen',
title: localize('gettingStarted.quickOpen.title', "Quickly navigate between your files"),
description: localize('gettingStarted.quickOpen.description', "Navigate between files in an instant with one keystroke. Tip: Open multiple files by pressing the right arrow key.\n[Quick Open a File](command:toSide:workbench.action.quickOpen)"),
description: localize('gettingStarted.quickOpen.description.interpolated', "Navigate between files in an instant with one keystroke. Tip: Open multiple files by pressing the right arrow key.\n{0}", Button(localize('quickOpen', "Quick Open a File"), 'command:toSide:workbench.action.quickOpen')),
when: 'workspaceFolderCount != 0',
media: {
type: 'image', altText: 'Go to file in quick search.', path: {
@ -227,7 +232,7 @@ export const walkthroughs: GettingStartedWalkthroughContent = [
{
id: 'playground',
title: localize('gettingStarted.playground.title', "Redefine your editing skills"),
description: localize('gettingStarted.playground.description', "Want to code faster and smarter? Practice powerful code editing features in the interactive playground.\n[Open Interactive Playground](command:toSide:workbench.action.showInteractivePlayground)"),
description: localize('gettingStarted.playground.description.interpolated', "Want to code faster and smarter? Practice powerful code editing features in the interactive playground.\n{0}", Button(localize('openInteractivePlayground', "Open Interactive Playground"), 'command:toSide:workbench.action.showInteractivePlayground')),
media: {
type: 'image', altText: 'Interactive Playground.', path: {
dark: 'dark/playground.png',
@ -239,7 +244,7 @@ export const walkthroughs: GettingStartedWalkthroughContent = [
{
id: 'terminal',
title: localize('gettingStarted.terminal.title', "Convenient built-in terminal"),
description: localize('gettingStarted.terminal.description', "Quickly run shell commands and monitor build output, right next to your code.\n[Show Terminal Panel](command:workbench.action.terminal.toggleTerminal)"),
description: localize('gettingStarted.terminal.description.interpolated', "Quickly run shell commands and monitor build output, right next to your code.\n{0}", Button(localize('showTerminal', "Show Terminal Panel"), 'command:workbench.action.terminal.toggleTerminal')),
when: 'remoteName != codespaces && !terminalIsOpen',
media: {
type: 'image', altText: 'Integrated terminal running a few npm commands', path: {
@ -252,7 +257,7 @@ export const walkthroughs: GettingStartedWalkthroughContent = [
{
id: 'extensions',
title: localize('gettingStarted.extensions.title', "Limitless extensibility"),
description: localize('gettingStarted.extensions.description', "Extensions are VS Code's power-ups. They range from handy productivity hacks, expanding out-of-the-box features, to adding completely new capabilities.\n[Browse Recommended Extensions](command:workbench.extensions.action.showRecommendedExtensions)"),
description: localize('gettingStarted.extensions.description.interpolated', "Extensions are VS Code's power-ups. They range from handy productivity hacks, expanding out-of-the-box features, to adding completely new capabilities.\n{0}", Button(localize('browseRecommended', "Browse Recommended Extensions"), 'command:workbench.extensions.action.showRecommendedExtensions')),
media: {
type: 'image', altText: 'VS Code extension marketplace with featured language extensions', path: {
dark: 'dark/extensions.png',
@ -264,7 +269,7 @@ export const walkthroughs: GettingStartedWalkthroughContent = [
{
id: 'settings',
title: localize('gettingStarted.settings.title', "Tune your settings"),
description: localize('gettingStarted.settings.description', "Tweak every aspect of VS Code and your extensions to your liking. Commonly used settings are listed first to get you started.\n[Tweak my Settings](command:toSide:workbench.action.openSettings)"),
description: localize('gettingStarted.settings.description.interpolated', "Tweak every aspect of VS Code and your extensions to your liking. Commonly used settings are listed first to get you started.\n{0}", Button(localize('tweakSettings', "Tweak my Settings"), 'command:toSide:workbench.action.openSettings')),
media: {
type: 'image', altText: 'VS Code Settings', path: {
dark: 'dark/settings.png',
@ -276,7 +281,7 @@ export const walkthroughs: GettingStartedWalkthroughContent = [
{
id: 'settingsSync',
title: localize('gettingStarted.settingsSync.title', "Sync your stuff across devices"),
description: localize('gettingStarted.settingsSync.description', "Never lose the perfect VS Code setup! Settings Sync will back up and share settings, keybindings & extensions across several installations.\n[Enable Settings Sync](command:workbench.userDataSync.actions.turnOn)"),
description: localize('gettingStarted.settingsSync.description.interpolated', "Never lose the perfect VS Code setup! Settings Sync will back up and share settings, keybindings & extensions across several installations.\n{0}", Button(localize('enableSync', "Enable Settings Sync"), 'command:workbench.userDataSync.actions.turnOn')),
when: 'syncStatus != uninitialized',
completionEvents: ['onEvent:sync-enabled'],
media: {
@ -290,7 +295,7 @@ export const walkthroughs: GettingStartedWalkthroughContent = [
{
id: 'videoTutorial',
title: localize('gettingStarted.videoTutorial.title', "Lean back and learn"),
description: localize('gettingStarted.videoTutorial.description', "Watch the first in a series of short & practical video tutorials for VS Code's key features.\n[Watch Tutorial](https://aka.ms/vscode-getting-started-video)"),
description: localize('gettingStarted.videoTutorial.description.interpolated', "Watch the first in a series of short & practical video tutorials for VS Code's key features.\n{0}", Button(localize('watch', "Watch Tutorial"), 'https://aka.ms/vscode-getting-started-video')),
media: { type: 'image', altText: 'VS Code Settings', path: 'tutorialVideo.png' },
}
]
@ -308,7 +313,7 @@ export const walkthroughs: GettingStartedWalkthroughContent = [
{
id: 'splitview',
title: localize('gettingStarted.splitview.title', "Side by side editing"),
description: localize('gettingStarted.splitview.description', "Make the most of your screen estate by opening files side by side, vertically and horizontally.\n[Split Editor](command:workbench.action.splitEditor)"),
description: localize('gettingStarted.splitview.description.interpolated', "Make the most of your screen estate by opening files side by side, vertically and horizontally.\n{0}", Button(localize('splitEditor', "Split Editor"), 'command:workbench.action.splitEditor')),
media: {
type: 'image', altText: 'Multiple editors in split view.', path: {
dark: 'dark/splitview.png',
@ -320,7 +325,7 @@ export const walkthroughs: GettingStartedWalkthroughContent = [
{
id: 'debugging',
title: localize('gettingStarted.debug.title', "Watch your code in action"),
description: localize('gettingStarted.debug.description', "Accelerate your edit, build, test, and debug loop by setting up a launch configuration.\n[Run your Project](command:workbench.action.debug.selectandstart)"),
description: localize('gettingStarted.debug.description.interpolated', "Accelerate your edit, build, test, and debug loop by setting up a launch configuration.\n{0}", Button(localize('runProject', "Run your Project"), 'command:workbench.action.debug.selectandstart')),
when: 'workspaceFolderCount != 0',
media: {
type: 'image', altText: 'Run and debug view.', path: {
@ -333,7 +338,7 @@ export const walkthroughs: GettingStartedWalkthroughContent = [
{
id: 'scmClone',
title: localize('gettingStarted.scm.title', "Track your code with Git"),
description: localize('gettingStarted.scmClone.description', "Set up the built-in version control for your project to track your changes and collaborate with others.\n[Clone Repository](command:git.clone)"),
description: localize('gettingStarted.scmClone.description.interpolated', "Set up the built-in version control for your project to track your changes and collaborate with others.\n{0}", Button(localize('cloneRepo', "Clone Repository"), 'command:git.clone')),
when: 'config.git.enabled && !git.missing && workspaceFolderCount == 0',
media: {
type: 'image', altText: 'Source Control view.', path: {
@ -346,7 +351,7 @@ export const walkthroughs: GettingStartedWalkthroughContent = [
{
id: 'scmSetup',
title: localize('gettingStarted.scm.title', "Track your code with Git"),
description: localize('gettingStarted.scmSetup.description', "Set up the built-in version control for your project to track your changes and collaborate with others.\n[Initialize Git Repository](command:git.init)"),
description: localize('gettingStarted.scmSetup.description.interpolated', "Set up the built-in version control for your project to track your changes and collaborate with others.\n{0}", Button(localize('initRepo', "Initialize Git Repository"), 'command:git.init')),
when: 'config.git.enabled && !git.missing && workspaceFolderCount != 0 && gitOpenRepositoryCount == 0',
media: {
type: 'image', altText: 'Source Control view.', path: {
@ -359,7 +364,7 @@ export const walkthroughs: GettingStartedWalkthroughContent = [
{
id: 'scm',
title: localize('gettingStarted.scm.title', "Track your code with Git"),
description: localize('gettingStarted.scm.description', "No more looking up Git commands! Git and GitHub workflows are seamlessly integrated.\n[Open Source Control](command:workbench.view.scm)"),
description: localize('gettingStarted.scm.description.interpolated', "No more looking up Git commands! Git and GitHub workflows are seamlessly integrated.\n{0}", Button(localize('openSCM', "Open Source Control"), 'command:workbench.view.scm')),
when: 'config.git.enabled && !git.missing && workspaceFolderCount != 0 && gitOpenRepositoryCount != 0 && activeViewlet != \'workbench.view.scm\'',
media: {
type: 'image', altText: 'Source Control view.', path: {
@ -373,7 +378,7 @@ export const walkthroughs: GettingStartedWalkthroughContent = [
id: 'tasks',
title: localize('gettingStarted.tasks.title', "Automate your project tasks"),
when: 'workspaceFolderCount != 0',
description: localize('gettingStarted.tasks.description', "Create tasks for your common workflows and enjoy the integrated experience of running scripts and automatically checking results.\n[Run Auto-detected Tasks](command:workbench.action.tasks.runTask)"),
description: localize('gettingStarted.tasks.description.interpolated', "Create tasks for your common workflows and enjoy the integrated experience of running scripts and automatically checking results.\n{0}", Button(localize('runTasks', "Run Auto-detected Tasks"), 'command:workbench.action.tasks.runTask')),
media: {
type: 'image', altText: 'Task runner.', path: {
dark: 'dark/tasks.png',
@ -385,7 +390,7 @@ export const walkthroughs: GettingStartedWalkthroughContent = [
{
id: 'shortcuts',
title: localize('gettingStarted.shortcuts.title', "Customize your shortcuts"),
description: localize('gettingStarted.shortcuts.description', "Once you have discovered your favorite commands, create custom keyboard shortcuts for instant access.\n[Keyboard Shortcuts](command:toSide:workbench.action.openGlobalKeybindings)"),
description: localize('gettingStarted.shortcuts.description.interpolated', "Once you have discovered your favorite commands, create custom keyboard shortcuts for instant access.\n{0}", Button(localize('keyboardShortcuts', "Keyboard Shortcuts"), 'command:toSide:workbench.action.openGlobalKeybindings')),
media: {
type: 'image', altText: 'Interactive shortcuts.', path: {
dark: 'dark/shortcuts.png',

View file

@ -25,7 +25,7 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
'enumDescriptions': [
localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.none' }, "Start without an editor."),
localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.welcomePage' }, "Open the legacy Welcome page."),
localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.readme' }, "Open the README when opening a folder that contains one, fallback to 'welcomePage' otherwise."),
localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.readme' }, "Open the README when opening a folder that contains one, fallback to 'welcomePage' otherwise. Note: This is only observed as a global ccnfiguration, it will be ignored if set in a workspace or folder configuration."),
localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.newUntitledFile' }, "Open a new untitled file (only applies when opening an empty window)."),
localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.welcomePageInEmptyWorkbench' }, "Open the legacy Welcome page when opening an empty workbench."),
localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.gettingStarted' }, "Open the new Welcome Page with content to aid in getting started with VS Code and extensions."),

View file

@ -374,7 +374,12 @@ export class WorkspaceTrustUXHandler extends Disposable implements IWorkbenchCon
ariaLabel = trusted ? localize('status.ariaTrustedWindow', "This window is trusted.") :
localize('status.ariaUntrustedWindow', "Restricted Mode: Some features are disabled because this window is not trusted.");
toolTip = trusted ? ariaLabel : {
value: localize('status.tooltipUntrustedWindow', "Running in Restricted Mode\n\n\Some [features are disabled](command:{0}) because this [window is not trusted](command:{1}).", LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID, MANAGE_TRUST_COMMAND_ID),
value: localize(
{ key: 'status.tooltipUntrustedWindow2', comment: ['[abc]({n}) are links. Only translate `features are disabled` and `window is not trusted`. Do not change brackets and parentheses or {n}'] },
"Running in Restricted Mode\n\nSome [features are disabled]({0}) because this [window is not trusted]({1}).",
`command:${LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID}`,
`command:${MANAGE_TRUST_COMMAND_ID}`
),
isTrusted: true,
supportThemeIcons: true
};
@ -384,7 +389,12 @@ export class WorkspaceTrustUXHandler extends Disposable implements IWorkbenchCon
ariaLabel = trusted ? localize('status.ariaTrustedFolder', "This folder is trusted.") :
localize('status.ariaUntrustedFolder', "Restricted Mode: Some features are disabled because this folder is not trusted.");
toolTip = trusted ? ariaLabel : {
value: localize('status.tooltipUntrustedFolder', "Running in Restricted Mode\n\n\Some [features are disabled](command:{0}) because this [folder is not trusted](command:{1}).", LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID, MANAGE_TRUST_COMMAND_ID),
value: localize(
{ key: 'status.tooltipUntrustedFolder2', comment: ['[abc]({n}) are links. Only translate `features are disabled` and `folder is not trusted`. Do not change brackets and parentheses or {n}'] },
"Running in Restricted Mode\n\nSome [features are disabled]({0}) because this [folder is not trusted]({1}).",
`command:${LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID}`,
`command:${MANAGE_TRUST_COMMAND_ID}`
),
isTrusted: true,
supportThemeIcons: true
};
@ -394,7 +404,12 @@ export class WorkspaceTrustUXHandler extends Disposable implements IWorkbenchCon
ariaLabel = trusted ? localize('status.ariaTrustedWorkspace', "This workspace is trusted.") :
localize('status.ariaUntrustedWorkspace', "Restricted Mode: Some features are disabled because this workspace is not trusted.");
toolTip = trusted ? ariaLabel : {
value: localize('status.tooltipUntrustedWorkspace', "Running in Restricted Mode\n\n\Some [features are disabled](command:{0}) because this [workspace is not trusted](command:{1}).", LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID, MANAGE_TRUST_COMMAND_ID),
value: localize(
{ key: 'status.tooltipUntrustedWorkspace2', comment: ['[abc]({n}) are links. Only translate `features are disabled` and `workspace is not trusted`. Do not change brackets and parentheses or {n}'] },
"Running in Restricted Mode\n\nSome [features are disabled]({0}) because this [workspace is not trusted]({1}).",
`command:${LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID}`,
`command:${MANAGE_TRUST_COMMAND_ID}`
),
isTrusted: true,
supportThemeIcons: true
};
@ -637,7 +652,7 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
},
[WORKSPACE_TRUST_EMPTY_WINDOW]: {
type: 'boolean',
default: false,
default: true,
included: !isWeb,
markdownDescription: localize('workspace.trust.emptyWindow.description', "Controls whether or not the empty window is trusted by default within VS Code. When used with `#{0}#`, you can enable the full functionality of VS Code without prompting in an empty window.", WORKSPACE_TRUST_UNTRUSTED_FILES),
scope: ConfigurationScope.APPLICATION

View file

@ -823,13 +823,13 @@ export class WorkspaceTrustEditor extends EditorPane {
[
localize('untrustedTasks', "Tasks are disabled"),
localize('untrustedDebugging', "Debugging is disabled"),
localize('untrustedExtensions', "[{0} extensions](command:{1}) are disabled or have limited functionality", numExtensions, LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID)
localize('untrustedExtensions', "[{0} extensions]({1}) are disabled or have limited functionality", numExtensions, `command:${LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID}`)
] :
[
localize('untrustedTasks', "Tasks are disabled"),
localize('untrustedDebugging', "Debugging is disabled"),
numSettings ? localize('untrustedSettings', "[{0} workspace settings](command:{1}) are not applied", numSettings, 'settings.filterUntrusted') : localize('no untrustedSettings', "Workspace settings requiring trust are not applied"),
localize('untrustedExtensions', "[{0} extensions](command:{1}) are disabled or have limited functionality", numExtensions, LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID)
numSettings ? localize('untrustedSettings', "[{0} workspace settings]({1}) are not applied", numSettings, 'command:settings.filterUntrusted') : localize('no untrustedSettings', "Workspace settings requiring trust are not applied"),
localize('untrustedExtensions', "[{0} extensions]({1}) are disabled or have limited functionality", numExtensions, `command:${LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID}`)
];
this.renderLimitationsListElement(untrustedContainer, untrustedContainerItems, xListIcon.classNamesArray);

View file

@ -235,8 +235,8 @@ export class WorkspaceTrustManagementService extends Disposable implements IWork
return true;
}
if (!this._initialized) {
return false;
if (this.environmentService.extensionTestsLocationURI) {
return true; // trust running tests with vscode-test
}
// Remote - remote authority explicitly sets workspace trust
@ -244,16 +244,16 @@ export class WorkspaceTrustManagementService extends Disposable implements IWork
return this._remoteAuthority.options.isTrusted;
}
if (this.environmentService.extensionTestsLocationURI) {
return true; // trust running tests with vscode-test
}
if (this.workspaceService.getWorkbenchState() === WorkbenchState.EMPTY) {
// Use memento if present, otherwise default to restricted mode
// Workspace may transition to trusted based on the opened editors
return this._trustState.isTrusted ?? false;
}
if (!this._initialized) {
return false;
}
return this.getUrisTrust(this.getWorkspaceUris());
}

View file

@ -15,7 +15,7 @@ import { TestInstantiationService } from 'vs/platform/instantiation/test/common/
import { MainThreadCommands } from 'vs/workbench/api/browser/mainThreadCommands';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { mock } from 'vs/base/test/common/mock';
import { TreeItemCollapsibleState, ITreeItem } from 'vs/workbench/common/views';
import { TreeItemCollapsibleState, ITreeItem, IRevealOptions } from 'vs/workbench/common/views';
import { NullLogService } from 'vs/platform/log/common/log';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import type { IDisposable } from 'vs/base/common/lifecycle';
@ -35,7 +35,7 @@ suite('ExtHostTreeView', function () {
});
}
override $reveal(): Promise<void> {
override $reveal(treeViewId: string, itemInfo: { item: ITreeItem, parentChain: ITreeItem[] } | undefined, options: IRevealOptions): Promise<void> {
return Promise.resolve();
}
@ -515,8 +515,8 @@ suite('ExtHostTreeView', function () {
.then(() => {
assert.ok(revealTarget.calledOnce);
assert.deepStrictEqual('treeDataProvider', revealTarget.args[0][0]);
assert.deepStrictEqual(expected.item, removeUnsetKeys(revealTarget.args[0][1].item));
assert.deepStrictEqual(expected.parentChain, (<Array<any>>(revealTarget.args[0][1].parentChain)).map(arg => removeUnsetKeys(arg)));
assert.deepStrictEqual(expected.item, removeUnsetKeys(revealTarget.args[0][1]!.item));
assert.deepStrictEqual(expected.parentChain, (<Array<any>>(revealTarget.args[0][1]!.parentChain)).map(arg => removeUnsetKeys(arg)));
assert.deepStrictEqual({ select: true, focus: false, expand: false }, revealTarget.args[0][2]);
});
});
@ -534,8 +534,8 @@ suite('ExtHostTreeView', function () {
.then(() => {
assert.ok(revealTarget.calledOnce);
assert.deepStrictEqual('treeDataProvider', revealTarget.args[0][0]);
assert.deepStrictEqual(expected.item, removeUnsetKeys(revealTarget.args[0][1].item));
assert.deepStrictEqual(expected.parentChain, (<Array<any>>(revealTarget.args[0][1].parentChain)).map(arg => removeUnsetKeys(arg)));
assert.deepStrictEqual(expected.item, removeUnsetKeys(revealTarget.args[0][1]!.item));
assert.deepStrictEqual(expected.parentChain, (<Array<any>>(revealTarget.args[0][1]!.parentChain)).map(arg => removeUnsetKeys(arg)));
assert.deepStrictEqual({ select: true, focus: false, expand: false }, revealTarget.args[0][2]);
}));
});
@ -561,8 +561,8 @@ suite('ExtHostTreeView', function () {
.then(() => {
assert.ok(revealTarget.calledOnce);
assert.deepStrictEqual('treeDataProvider', revealTarget.args[0][0]);
assert.deepStrictEqual(expected.item, removeUnsetKeys(revealTarget.args[0][1].item));
assert.deepStrictEqual(expected.parentChain, (<Array<any>>(revealTarget.args[0][1].parentChain)).map(arg => removeUnsetKeys(arg)));
assert.deepStrictEqual(expected.item, removeUnsetKeys(revealTarget.args[0][1]!.item));
assert.deepStrictEqual(expected.parentChain, (<Array<any>>(revealTarget.args[0][1]!.parentChain)).map(arg => removeUnsetKeys(arg)));
assert.deepStrictEqual({ select: false, focus: false, expand: false }, revealTarget.args[0][2]);
});
});
@ -592,8 +592,8 @@ suite('ExtHostTreeView', function () {
.then(() => {
assert.ok(revealTarget.calledOnce);
assert.deepStrictEqual('treeDataProvider', revealTarget.args[0][0]);
assert.deepStrictEqual(expected.item, removeUnsetKeys(revealTarget.args[0][1].item));
assert.deepStrictEqual(expected.parentChain, (<Array<any>>(revealTarget.args[0][1].parentChain)).map(arg => removeUnsetKeys(arg)));
assert.deepStrictEqual(expected.item, removeUnsetKeys(revealTarget.args[0][1]!.item));
assert.deepStrictEqual(expected.parentChain, (<Array<any>>(revealTarget.args[0][1]!.parentChain)).map(arg => removeUnsetKeys(arg)));
assert.deepStrictEqual({ select: true, focus: false, expand: false }, revealTarget.args[0][2]);
});
});
@ -633,8 +633,8 @@ suite('ExtHostTreeView', function () {
.then(() => {
assert.ok(revealTarget.calledOnce);
assert.deepStrictEqual('treeDataProvider', revealTarget.args[0][0]);
assert.deepStrictEqual({ handle: '0/0:b/0:bc', label: { label: 'bc' }, collapsibleState: TreeItemCollapsibleState.None, parentHandle: '0/0:b' }, removeUnsetKeys(revealTarget.args[0][1].item));
assert.deepStrictEqual([{ handle: '0/0:b', label: { label: 'b' }, collapsibleState: TreeItemCollapsibleState.Collapsed }], (<Array<any>>revealTarget.args[0][1].parentChain).map(arg => removeUnsetKeys(arg)));
assert.deepStrictEqual({ handle: '0/0:b/0:bc', label: { label: 'bc' }, collapsibleState: TreeItemCollapsibleState.None, parentHandle: '0/0:b' }, removeUnsetKeys(revealTarget.args[0][1]!.item));
assert.deepStrictEqual([{ handle: '0/0:b', label: { label: 'b' }, collapsibleState: TreeItemCollapsibleState.Collapsed }], (<Array<any>>revealTarget.args[0][1]!.parentChain).map(arg => removeUnsetKeys(arg)));
assert.deepStrictEqual({ select: true, focus: false, expand: false }, revealTarget.args[0][2]);
});
});

View file

@ -53,7 +53,8 @@
paths: {
vs: new URL(`../../../${!!isBuild ? 'out-build' : 'out'}/vs`, baseUrl).href,
assert: new URL('../assert.js', baseUrl).href,
sinon: new URL('../../../node_modules/sinon/pkg/sinon-1.17.7.js', baseUrl).href,
sinon: new URL('../../../node_modules/sinon/pkg/sinon.js', baseUrl).href,
'sinon-test': new URL('../../../node_modules/sinon-test/dist/sinon-test.js', baseUrl).href,
xterm: new URL('../../../node_modules/xterm/lib/xterm.js', baseUrl).href,
'iconv-lite-umd': new URL('../../../node_modules/iconv-lite-umd/lib/iconv-lite-umd.js', baseUrl).href,
jschardet: new URL('../../../node_modules/jschardet/dist/jschardet.min.js', baseUrl).href

View file

@ -17,7 +17,8 @@
baseUrl: '/out',
paths: {
assert: '/test/unit/assert.js',
sinon: '/node_modules/sinon/pkg/sinon-1.17.7.js'
sinon: '/node_modules/sinon/pkg/sinon.js',
'sinon-test': '/node_modules/sinon-test/dist/sinon-test.js'
}
});

176
yarn.lock
View file

@ -338,6 +338,34 @@
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea"
integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==
"@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.8.3":
version "1.8.3"
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d"
integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==
dependencies:
type-detect "4.0.8"
"@sinonjs/fake-timers@^7.0.4", "@sinonjs/fake-timers@^7.1.0":
version "7.1.2"
resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz#2524eae70c4910edccf99b2f4e6efc5894aff7b5"
integrity sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg==
dependencies:
"@sinonjs/commons" "^1.7.0"
"@sinonjs/samsam@^6.0.2":
version "6.0.2"
resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-6.0.2.tgz#a0117d823260f282c04bff5f8704bdc2ac6910bb"
integrity sha512-jxPRPp9n93ci7b8hMfJOFDPRLFYadN6FSpeROFTR4UNF4i5b+EK6m4QXPO46BDhFgRy1JuS87zAnFOzCUwMJcQ==
dependencies:
"@sinonjs/commons" "^1.6.0"
lodash.get "^4.4.2"
type-detect "^4.0.8"
"@sinonjs/text-encoding@^0.7.1":
version "0.7.1"
resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5"
integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==
"@szmarczak/http-timer@^1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421"
@ -350,14 +378,15 @@
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==
"@ts-morph/common@~0.9.0":
version "0.9.0"
resolved "https://registry.yarnpkg.com/@ts-morph/common/-/common-0.9.0.tgz#a306355bad82cff22a1881f7f2f2c710bbb4d69d"
integrity sha512-yPcW6koNVK1hVKUu+KhPzhfgMb0uwzr2FewF+q8kxLerl0b+YZwmjvFMU2qbIawytIHT2VBI4bi+C09EFPB4aw==
"@ts-morph/common@~0.10.0":
version "0.10.0"
resolved "https://registry.yarnpkg.com/@ts-morph/common/-/common-0.10.0.tgz#d032ea6f6d4115b72fa50ad56009baebcc1e71b8"
integrity sha512-6wC+CovwzxLP+bQZcqHJEbZ7ViaIfsid8VzsVjJRkdfCQ8C8K5mm1+9/wkgmn814BPATtgSgFuDmVJnIb8/leg==
dependencies:
fast-glob "^3.2.5"
minimatch "^3.0.4"
mkdirp "^1.0.4"
path-browserify "^1.0.1"
"@types/anymatch@*":
version "1.3.1"
@ -519,10 +548,19 @@
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-5.5.0.tgz#146c2a29ee7d3bae4bf2fcb274636e264c813c45"
integrity sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ==
"@types/sinon@^1.16.36":
version "1.16.36"
resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-1.16.36.tgz#74bb6ed7928597c1b3fb1b009005e94dc6eae357"
integrity sha1-dLtu15KFl8Gz+xsAkAXpTcbq41c=
"@types/sinon-test@^2.4.2":
version "2.4.2"
resolved "https://registry.yarnpkg.com/@types/sinon-test/-/sinon-test-2.4.2.tgz#f55bdf5486e7b7a4dd7257789fcc2b7b125c4164"
integrity sha512-3BX9mk5+o//Xzs5N4bFYxPT+QlPLrqbyNfDWkIGtk9pVIp2Nl8ctsIGXsY3F01DsCd1Zlin3FqAk6V5XqkCyJA==
dependencies:
"@types/sinon" "*"
"@types/sinon@*", "@types/sinon@^10.0.2":
version "10.0.2"
resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-10.0.2.tgz#f360d2f189c0fd433d14aeb97b9d705d7e4cc0e4"
integrity sha512-BHn8Bpkapj8Wdfxvh2jWIUoaYB/9/XhsL0oOvBfRagJtKlSl9NWPcFOz2lRukI9szwGxFtYZCTejJSqsGDbdmw==
dependencies:
"@sinonjs/fake-timers" "^7.1.0"
"@types/source-list-map@*":
version "0.1.2"
@ -2868,6 +2906,11 @@ diff@4.0.2:
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
diff@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b"
integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==
diffie-hellman@^5.0.0:
version "5.0.3"
resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875"
@ -3939,13 +3982,6 @@ form-data@~2.3.2:
combined-stream "^1.0.6"
mime-types "^2.1.12"
formatio@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/formatio/-/formatio-1.1.1.tgz#5ed3ccd636551097383465d996199100e86161e9"
integrity sha1-XtPM1jZVEJc4NGXZlhmRAOhhYek=
dependencies:
samsam "~1.1"
fragment-cache@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19"
@ -5670,6 +5706,11 @@ just-debounce@^1.0.0:
resolved "https://registry.yarnpkg.com/just-debounce/-/just-debounce-1.0.0.tgz#87fccfaeffc0b68cd19d55f6722943f929ea35ea"
integrity sha1-h/zPrv/AtozRnVX2cilD+SnqNeo=
just-extend@^4.0.2:
version "4.2.1"
resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.2.1.tgz#ef5e589afb61e5d66b24eca749409a8939a8c744"
integrity sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==
keytar@7.2.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/keytar/-/keytar-7.2.0.tgz#4db2bec4f9700743ffd9eda22eebb658965c8440"
@ -5867,6 +5908,11 @@ lodash.clone@^4.3.2:
resolved "https://registry.yarnpkg.com/lodash.clone/-/lodash.clone-4.5.0.tgz#195870450f5a13192478df4bc3d23d2dea1907b6"
integrity sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=
lodash.get@^4.4.2:
version "4.4.2"
resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=
lodash.isequal@^4.0.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
@ -5914,11 +5960,6 @@ log-symbols@4.0.0:
dependencies:
chalk "^4.0.0"
lolex@1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/lolex/-/lolex-1.3.2.tgz#7c3da62ffcb30f0f5a80a2566ca24e45d8a01f31"
integrity sha1-fD2mL/yzDw9agKJWbKJORdigHzE=
lowercase-keys@^1.0.0, lowercase-keys@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
@ -6519,6 +6560,17 @@ nice-try@^1.0.4:
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
nise@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/nise/-/nise-5.1.0.tgz#713ef3ed138252daef20ec035ab62b7a28be645c"
integrity sha512-W5WlHu+wvo3PaKLsJJkgPup2LrsXCcm7AWwyNZkUnn5rwPkuPBi3Iwk5SQtN0mv+K65k7nKKjwNQ30wg3wLAQQ==
dependencies:
"@sinonjs/commons" "^1.7.0"
"@sinonjs/fake-timers" "^7.0.4"
"@sinonjs/text-encoding" "^0.7.1"
just-extend "^4.0.2"
path-to-regexp "^1.7.0"
node-abi@^2.21.0:
version "2.21.0"
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.21.0.tgz#c2dc9ebad6f4f53d6ea9b531e7b8faad81041d48"
@ -7093,6 +7145,11 @@ path-browserify@0.0.1:
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a"
integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==
path-browserify@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd"
integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==
path-dirname@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0"
@ -7147,6 +7204,13 @@ path-root@^0.1.1:
dependencies:
path-root-regex "^0.1.0"
path-to-regexp@^1.7.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a"
integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==
dependencies:
isarray "0.0.1"
path-type@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441"
@ -8366,16 +8430,6 @@ safe-regex@^1.1.0:
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
samsam@1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.1.2.tgz#bec11fdc83a9fda063401210e40176c3024d1567"
integrity sha1-vsEf3IOp/aBjQBIQ5AF2wwJNFWc=
samsam@~1.1:
version "1.1.3"
resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.1.3.tgz#9f5087419b4d091f232571e7fa52e90b0f552621"
integrity sha1-n1CHQZtNCR8jJXHn+lLpCw9VJiE=
sax@0.5.x:
version "0.5.8"
resolved "https://registry.yarnpkg.com/sax/-/sax-0.5.8.tgz#d472db228eb331c2506b0e8c15524adb939d12c1"
@ -8551,15 +8605,22 @@ simple-swizzle@^0.2.2:
dependencies:
is-arrayish "^0.3.1"
sinon@^1.17.2:
version "1.17.7"
resolved "https://registry.yarnpkg.com/sinon/-/sinon-1.17.7.tgz#4542a4f49ba0c45c05eb2e9dd9d203e2b8efe0bf"
integrity sha1-RUKk9JugxFwF6y6d2dID4rjv4L8=
sinon-test@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/sinon-test/-/sinon-test-3.1.0.tgz#25a3f4d9a9deb172252407041d577d67b73fefd5"
integrity sha512-aGQwq6Xl9eJg/8Ugv4Ko4LQWUqjwRYNI8UtxnKa9hmcMEz3HBTR3nnzYrbW4isuRLsJWFuJTUcPGuz7f4XvODg==
sinon@^11.1.1:
version "11.1.1"
resolved "https://registry.yarnpkg.com/sinon/-/sinon-11.1.1.tgz#99a295a8b6f0fadbbb7e004076f3ae54fc6eab91"
integrity sha512-ZSSmlkSyhUWbkF01Z9tEbxZLF/5tRC9eojCdFh33gtQaP7ITQVaMWQHGuFM7Cuf/KEfihuh1tTl3/ABju3AQMg==
dependencies:
formatio "1.1.1"
lolex "1.3.2"
samsam "1.1.2"
util ">=0.10.3 <1"
"@sinonjs/commons" "^1.8.3"
"@sinonjs/fake-timers" "^7.1.0"
"@sinonjs/samsam" "^6.0.2"
diff "^5.0.0"
nise "^5.1.0"
supports-color "^7.2.0"
slash@^3.0.0:
version "3.0.0"
@ -9077,7 +9138,7 @@ sumchecker@^3.0.1:
dependencies:
debug "^4.1.0"
supports-color@7.2.0:
supports-color@7.2.0, supports-color@^7.2.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
@ -9449,12 +9510,12 @@ ts-loader@^6.2.1:
micromatch "^4.0.0"
semver "^6.0.0"
ts-morph@^10.0.2:
version "10.0.2"
resolved "https://registry.yarnpkg.com/ts-morph/-/ts-morph-10.0.2.tgz#292418207db467326231b2be92828b5e295e7946"
integrity sha512-TVuIfEqtr9dW25K3Jajqpqx7t/zLRFxKu2rXQZSDjTm4MO4lfmuj1hn8WEryjeDDBFcNOCi+yOmYUYR4HucrAg==
ts-morph@^11.0.0:
version "11.0.0"
resolved "https://registry.yarnpkg.com/ts-morph/-/ts-morph-11.0.0.tgz#511b3caa194739fef0619367f8e65de9b475e1d4"
integrity sha512-u5y0jaft5c0sRFnU0K8cZhhsvPUtXjZK5L31JLIhP17qcqo9MDjwsSYLg3UryQDzlktv8wyf/UtoqpFLDYHijg==
dependencies:
"@ts-morph/common" "~0.9.0"
"@ts-morph/common" "~0.10.0"
code-block-writer "^10.1.1"
tsec@0.1.4:
@ -9511,6 +9572,11 @@ type-check@~0.3.2:
dependencies:
prelude-ls "~1.1.2"
type-detect@4.0.8, type-detect@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
type-fest@^0.11.0:
version "0.11.0"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1"
@ -9722,7 +9788,7 @@ util.promisify@~1.0.0:
has-symbols "^1.0.1"
object.getownpropertydescriptors "^2.1.0"
util@0.10.3, "util@>=0.10.3 <1":
util@0.10.3:
version "0.10.3"
resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9"
integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk=
@ -9979,14 +10045,6 @@ vscode-regexpp@^3.1.0:
resolved "https://registry.yarnpkg.com/vscode-regexpp/-/vscode-regexpp-3.1.0.tgz#42d059b6fffe99bd42939c0d013f632f0cad823f"
integrity sha512-pqtN65VC1jRLawfluX4Y80MMG0DHJydWhe5ZwMHewZD6sys4LbU6lHwFAHxeuaVE6Y6+xZOtAw+9hvq7/0ejkg==
vscode-ripgrep@^1.11.2:
version "1.11.2"
resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.11.2.tgz#a1d9c717a20f625b7e14680cc7db25ffafd132d4"
integrity sha512-qMARNpPh/m6h9NbAQs4unGUnlAP2vrxt3a3nzbscrJcd5X9onoSdAYKG9vCkcxFJtOcQQm44a2Vf369mrrz8Sw==
dependencies:
https-proxy-agent "^4.0.0"
proxy-from-env "^1.1.0"
vscode-ripgrep@^1.11.3:
version "1.11.3"
resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.11.3.tgz#a997f4f4535dfeb9d775f04053c1247454d7a37a"
@ -10002,14 +10060,14 @@ vscode-sqlite3@4.0.11:
dependencies:
nan "^2.14.0"
vscode-telemetry-extractor@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/vscode-telemetry-extractor/-/vscode-telemetry-extractor-1.7.0.tgz#f99b1a90a4cad0f75454f2f57615a155e55eb960"
integrity sha512-UC/N/uqPuQIuNnXg52XJnejeId2+Nuq04rj4H1rSZsqj9a56pigs6ogLPdZSi+OVLI21LU9PnJ/ZKrBrLm1roA==
vscode-telemetry-extractor@^1.8.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/vscode-telemetry-extractor/-/vscode-telemetry-extractor-1.8.0.tgz#5562106fe2eebfce0593f336c91f5a5ddc154cee"
integrity sha512-jWe+caeLyB/F3V0EqsdkCC98wXx9+XLbm6EoPngz0sC4GOM7lcDSnVhUXzrIhZD/TSRPSPGlxp5r4/CrvhbmMQ==
dependencies:
command-line-args "^5.1.1"
ts-morph "^10.0.2"
vscode-ripgrep "^1.11.2"
ts-morph "^11.0.0"
vscode-ripgrep "^1.11.3"
vscode-textmate@5.4.0:
version "5.4.0"