Merge remote-tracking branch 'origin/master' into tyriar/megan
This commit is contained in:
commit
59264e2ab1
|
@ -13,5 +13,6 @@
|
|||
**/extensions/**/out/**
|
||||
**/extensions/**/build/**
|
||||
**/extensions/markdown-language-features/media/**
|
||||
**/extensions/markdown-language-features/notebook-out/**
|
||||
**/extensions/typescript-basics/test/colorize-fixtures/**
|
||||
**/extensions/**/dist/**
|
||||
|
|
|
@ -86,6 +86,8 @@ module.exports.indentationFilter = [
|
|||
'!**/*.Dockerfile',
|
||||
'!**/*.dockerfile',
|
||||
'!extensions/markdown-language-features/media/*.js',
|
||||
'!extensions/markdown-language-features/notebook-out/*.js',
|
||||
'!extensions/markdown-notebook-math/notebook-out/*.js',
|
||||
'!extensions/simple-browser/media/*.js',
|
||||
];
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ exports.dirs = [
|
|||
'extensions/json-language-features',
|
||||
'extensions/json-language-features/server',
|
||||
'extensions/markdown-language-features',
|
||||
'extensions/markdown-notebook-math',
|
||||
'extensions/merge-conflict',
|
||||
'extensions/microsoft-authentication',
|
||||
'extensions/npm',
|
||||
|
|
964
extensions/markdown-language-features/notebook-out/index.js
Normal file
964
extensions/markdown-language-features/notebook-out/index.js
Normal file
File diff suppressed because one or more lines are too long
26
extensions/markdown-language-features/notebook/index.ts
Normal file
26
extensions/markdown-language-features/notebook/index.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as MarkdownIt from 'markdown-it';
|
||||
|
||||
declare const acquireNotebookRendererApi: any;
|
||||
type extendMarkdownItFnType = (
|
||||
(f: (md: MarkdownIt.MarkdownIt) => void) => void
|
||||
);
|
||||
|
||||
(function () {
|
||||
const markdownIt = new MarkdownIt();
|
||||
|
||||
(globalThis as any).extendMarkdownIt = ((f: (md: MarkdownIt.MarkdownIt) => void) => {
|
||||
f(markdownIt);
|
||||
}) as extendMarkdownItFnType;
|
||||
|
||||
const notebook = acquireNotebookRendererApi('notebookCoreTestRenderer');
|
||||
|
||||
notebook.onDidCreateMarkdown(({ element, content }: any) => {
|
||||
const rendered = markdownIt.render(content);
|
||||
element.innerHTML = rendered;
|
||||
});
|
||||
}());
|
|
@ -30,6 +30,13 @@
|
|||
"onCustomEditor:vscode.markdown.preview.editor"
|
||||
],
|
||||
"contributes": {
|
||||
"notebookMarkdownRenderer": [
|
||||
{
|
||||
"id": "markdownItRenderer",
|
||||
"displayName": "Markdown it renderer",
|
||||
"entrypoint": "./notebook-out/index.js"
|
||||
}
|
||||
],
|
||||
"commands": [
|
||||
{
|
||||
"command": "markdown.showPreview",
|
||||
|
|
27
extensions/markdown-language-features/webpack.notebook.js
Normal file
27
extensions/markdown-language-features/webpack.notebook.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
index: './notebook/index.ts'
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
use: 'ts-loader',
|
||||
exclude: /node_modules/
|
||||
}
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.tsx', '.ts', '.js']
|
||||
},
|
||||
output: {
|
||||
filename: '[name].js',
|
||||
path: path.resolve(__dirname, 'notebook-out')
|
||||
}
|
||||
};
|
12
extensions/markdown-notebook-math/.vscodeignore
Normal file
12
extensions/markdown-notebook-math/.vscodeignore
Normal file
|
@ -0,0 +1,12 @@
|
|||
test/**
|
||||
test-workspace/**
|
||||
src/**
|
||||
tsconfig.json
|
||||
out/test/**
|
||||
out/**
|
||||
extension.webpack.config.js
|
||||
extension-browser.webpack.config.js
|
||||
cgmanifest.json
|
||||
yarn.lock
|
||||
preview-src/**
|
||||
webpack.config.js
|
3
extensions/markdown-notebook-math/README.md
Normal file
3
extensions/markdown-notebook-math/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# Markdown Notebook Math support
|
||||
|
||||
**Notice:** This extension is bundled with Visual Studio Code. It can be disabled but not uninstalled.
|
BIN
extensions/markdown-notebook-math/icon.png
Normal file
BIN
extensions/markdown-notebook-math/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 903 B |
File diff suppressed because one or more lines are too long
22
extensions/markdown-notebook-math/notebook/extension.ts
Normal file
22
extensions/markdown-notebook-math/notebook/extension.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import type * as markdownIt from 'markdown-it';
|
||||
|
||||
import 'katex/dist/katex.min.css';
|
||||
|
||||
declare const extendMarkdownIt: undefined | (
|
||||
(f: (md: markdownIt.MarkdownIt) => void) => void
|
||||
);
|
||||
|
||||
(function () {
|
||||
const katex = require('@iktakahiro/markdown-it-katex');
|
||||
|
||||
if (typeof extendMarkdownIt !== 'undefined') {
|
||||
extendMarkdownIt((md: markdownIt.MarkdownIt) => {
|
||||
md.use(katex);
|
||||
});
|
||||
}
|
||||
}());
|
||||
|
12
extensions/markdown-notebook-math/notebook/tsconfig.json
Normal file
12
extensions/markdown-notebook-math/notebook/tsconfig.json
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"extends": "../../shared.tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist/",
|
||||
"jsx": "react",
|
||||
"lib": [
|
||||
"es2018",
|
||||
"DOM",
|
||||
"DOM.Iterable"
|
||||
]
|
||||
}
|
||||
}
|
45
extensions/markdown-notebook-math/package.json
Normal file
45
extensions/markdown-notebook-math/package.json
Normal file
|
@ -0,0 +1,45 @@
|
|||
{
|
||||
"name": "markdown-notebook-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": {
|
||||
"vscode": "^1.54.0"
|
||||
},
|
||||
"categories": [
|
||||
"Other"
|
||||
],
|
||||
"contributes": {
|
||||
"notebookMarkdownRenderer": [
|
||||
{
|
||||
"id": "markdownItRenderer-katex",
|
||||
"displayName": "Markdown it renderer",
|
||||
"entrypoint": "./notebook-out/extension.js"
|
||||
}
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"compile": "npm run build-notebook",
|
||||
"watch": "npm run build-notebook",
|
||||
"build-notebook": "npx webpack-cli --config webpack.notebook.js --mode production"
|
||||
},
|
||||
"dependencies": {
|
||||
"@iktakahiro/markdown-it-katex": "^4.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/markdown-it": "^0.0.0",
|
||||
"css-loader": "^5.0.2",
|
||||
"markdown-it": "^12.0.4",
|
||||
"style-loader": "^2.0.0",
|
||||
"url-loader": "^4.1.1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/microsoft/vscode.git"
|
||||
}
|
||||
}
|
4
extensions/markdown-notebook-math/package.nls.json
Normal file
4
extensions/markdown-notebook-math/package.nls.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"displayName": "Markdown Notebook math",
|
||||
"description": "Provides rich language support for Markdown."
|
||||
}
|
35
extensions/markdown-notebook-math/webpack.notebook.js
Normal file
35
extensions/markdown-notebook-math/webpack.notebook.js
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
extension: './notebook/extension.ts',
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
use: 'ts-loader',
|
||||
exclude: /node_modules/
|
||||
},
|
||||
{
|
||||
test: /\.css$/i,
|
||||
use: ['style-loader', 'css-loader'],
|
||||
},
|
||||
{
|
||||
test: /\.(woff|woff2|eot|ttf|otf)$/i,
|
||||
use: ['url-loader?limit=100000']
|
||||
},
|
||||
],
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.tsx', '.ts', '.js']
|
||||
},
|
||||
output: {
|
||||
filename: '[name].js',
|
||||
path: path.resolve(__dirname, 'notebook-out')
|
||||
}
|
||||
};
|
315
extensions/markdown-notebook-math/yarn.lock
Normal file
315
extensions/markdown-notebook-math/yarn.lock
Normal file
|
@ -0,0 +1,315 @@
|
|||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@iktakahiro/markdown-it-katex@^4.0.1":
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@iktakahiro/markdown-it-katex/-/markdown-it-katex-4.0.1.tgz#65ff9d12afd4c0b7684dd247abe7ce42fc1edac3"
|
||||
integrity sha512-kGFooO7fIOgY34PSG8ZNVsUlKhhNoqhzW2kq94TNGa8COzh73PO4KsEoPOsQVG1mEAe8tg7GqG0FoVao0aMHaw==
|
||||
dependencies:
|
||||
katex "^0.12.0"
|
||||
|
||||
"@types/json-schema@^7.0.6":
|
||||
version "7.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad"
|
||||
integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==
|
||||
|
||||
"@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=
|
||||
|
||||
ajv-keywords@^3.5.2:
|
||||
version "3.5.2"
|
||||
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d"
|
||||
integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==
|
||||
|
||||
ajv@^6.12.5:
|
||||
version "6.12.6"
|
||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
|
||||
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
|
||||
dependencies:
|
||||
fast-deep-equal "^3.1.1"
|
||||
fast-json-stable-stringify "^2.0.0"
|
||||
json-schema-traverse "^0.4.1"
|
||||
uri-js "^4.2.2"
|
||||
|
||||
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==
|
||||
|
||||
big.js@^5.2.2:
|
||||
version "5.2.2"
|
||||
resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
|
||||
integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
|
||||
|
||||
camelcase@^6.2.0:
|
||||
version "6.2.0"
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809"
|
||||
integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==
|
||||
|
||||
colorette@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.1.tgz#4d0b921325c14faf92633086a536db6e89564b1b"
|
||||
integrity sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==
|
||||
|
||||
commander@^2.19.0:
|
||||
version "2.20.3"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
|
||||
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
|
||||
|
||||
css-loader@^5.0.2:
|
||||
version "5.0.2"
|
||||
resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.0.2.tgz#24f758dae349bad0a440c50d7e2067742e0899cb"
|
||||
integrity sha512-gbkBigdcHbmNvZ1Cg6aV6qh6k9N6XOr8YWzISLQGrwk2mgOH8LLrizhkxbDhQtaLtktyKHD4970S0xwz5btfTA==
|
||||
dependencies:
|
||||
camelcase "^6.2.0"
|
||||
cssesc "^3.0.0"
|
||||
icss-utils "^5.1.0"
|
||||
loader-utils "^2.0.0"
|
||||
postcss "^8.2.4"
|
||||
postcss-modules-extract-imports "^3.0.0"
|
||||
postcss-modules-local-by-default "^4.0.0"
|
||||
postcss-modules-scope "^3.0.0"
|
||||
postcss-modules-values "^4.0.0"
|
||||
postcss-value-parser "^4.1.0"
|
||||
schema-utils "^3.0.0"
|
||||
semver "^7.3.4"
|
||||
|
||||
cssesc@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
|
||||
integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
|
||||
|
||||
emojis-list@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78"
|
||||
integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==
|
||||
|
||||
entities@~2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5"
|
||||
integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==
|
||||
|
||||
fast-deep-equal@^3.1.1:
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
|
||||
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
|
||||
|
||||
fast-json-stable-stringify@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
|
||||
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
|
||||
|
||||
icss-utils@^5.0.0, icss-utils@^5.1.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae"
|
||||
integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==
|
||||
|
||||
indexes-of@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607"
|
||||
integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc=
|
||||
|
||||
json-schema-traverse@^0.4.1:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
|
||||
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
|
||||
|
||||
json5@^2.1.2:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3"
|
||||
integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==
|
||||
dependencies:
|
||||
minimist "^1.2.5"
|
||||
|
||||
katex@^0.12.0:
|
||||
version "0.12.0"
|
||||
resolved "https://registry.yarnpkg.com/katex/-/katex-0.12.0.tgz#2fb1c665dbd2b043edcf8a1f5c555f46beaa0cb9"
|
||||
integrity sha512-y+8btoc/CK70XqcHqjxiGWBOeIL8upbS0peTPXTvgrh21n1RiWWcIpSWM+4uXq+IAgNh9YYQWdc7LVDPDAEEAg==
|
||||
dependencies:
|
||||
commander "^2.19.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"
|
||||
|
||||
loader-utils@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0"
|
||||
integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==
|
||||
dependencies:
|
||||
big.js "^5.2.2"
|
||||
emojis-list "^3.0.0"
|
||||
json5 "^2.1.2"
|
||||
|
||||
lru-cache@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
|
||||
integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
|
||||
dependencies:
|
||||
yallist "^4.0.0"
|
||||
|
||||
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=
|
||||
|
||||
mime-db@1.45.0:
|
||||
version "1.45.0"
|
||||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.45.0.tgz#cceeda21ccd7c3a745eba2decd55d4b73e7879ea"
|
||||
integrity sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==
|
||||
|
||||
mime-types@^2.1.27:
|
||||
version "2.1.28"
|
||||
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.28.tgz#1160c4757eab2c5363888e005273ecf79d2a0ecd"
|
||||
integrity sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ==
|
||||
dependencies:
|
||||
mime-db "1.45.0"
|
||||
|
||||
minimist@^1.2.5:
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
|
||||
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
|
||||
|
||||
nanoid@^3.1.20:
|
||||
version "3.1.20"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788"
|
||||
integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==
|
||||
|
||||
postcss-modules-extract-imports@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d"
|
||||
integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==
|
||||
|
||||
postcss-modules-local-by-default@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz#ebbb54fae1598eecfdf691a02b3ff3b390a5a51c"
|
||||
integrity sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==
|
||||
dependencies:
|
||||
icss-utils "^5.0.0"
|
||||
postcss-selector-parser "^6.0.2"
|
||||
postcss-value-parser "^4.1.0"
|
||||
|
||||
postcss-modules-scope@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz#9ef3151456d3bbfa120ca44898dfca6f2fa01f06"
|
||||
integrity sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==
|
||||
dependencies:
|
||||
postcss-selector-parser "^6.0.4"
|
||||
|
||||
postcss-modules-values@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c"
|
||||
integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==
|
||||
dependencies:
|
||||
icss-utils "^5.0.0"
|
||||
|
||||
postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4:
|
||||
version "6.0.4"
|
||||
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz#56075a1380a04604c38b063ea7767a129af5c2b3"
|
||||
integrity sha512-gjMeXBempyInaBqpp8gODmwZ52WaYsVOsfr4L4lDQ7n3ncD6mEyySiDtgzCT+NYC0mmeOLvtsF8iaEf0YT6dBw==
|
||||
dependencies:
|
||||
cssesc "^3.0.0"
|
||||
indexes-of "^1.0.1"
|
||||
uniq "^1.0.1"
|
||||
util-deprecate "^1.0.2"
|
||||
|
||||
postcss-value-parser@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb"
|
||||
integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==
|
||||
|
||||
postcss@^8.2.4:
|
||||
version "8.2.6"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.6.tgz#5d69a974543b45f87e464bc4c3e392a97d6be9fe"
|
||||
integrity sha512-xpB8qYxgPuly166AGlpRjUdEYtmOWx2iCwGmrv4vqZL9YPVviDVPZPRXxnXr6xPZOdxQ9lp3ZBFCRgWJ7LE3Sg==
|
||||
dependencies:
|
||||
colorette "^1.2.1"
|
||||
nanoid "^3.1.20"
|
||||
source-map "^0.6.1"
|
||||
|
||||
punycode@^2.1.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
||||
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
|
||||
|
||||
schema-utils@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.0.0.tgz#67502f6aa2b66a2d4032b4279a2944978a0913ef"
|
||||
integrity sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==
|
||||
dependencies:
|
||||
"@types/json-schema" "^7.0.6"
|
||||
ajv "^6.12.5"
|
||||
ajv-keywords "^3.5.2"
|
||||
|
||||
semver@^7.3.4:
|
||||
version "7.3.4"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97"
|
||||
integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
source-map@^0.6.1:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
||||
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
||||
|
||||
style-loader@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-2.0.0.tgz#9669602fd4690740eaaec137799a03addbbc393c"
|
||||
integrity sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ==
|
||||
dependencies:
|
||||
loader-utils "^2.0.0"
|
||||
schema-utils "^3.0.0"
|
||||
|
||||
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==
|
||||
|
||||
uniq@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff"
|
||||
integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=
|
||||
|
||||
uri-js@^4.2.2:
|
||||
version "4.4.1"
|
||||
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
|
||||
integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
|
||||
dependencies:
|
||||
punycode "^2.1.0"
|
||||
|
||||
url-loader@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-4.1.1.tgz#28505e905cae158cf07c92ca622d7f237e70a4e2"
|
||||
integrity sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==
|
||||
dependencies:
|
||||
loader-utils "^2.0.0"
|
||||
mime-types "^2.1.27"
|
||||
schema-utils "^3.0.0"
|
||||
|
||||
util-deprecate@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
|
||||
|
||||
yallist@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
|
||||
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
|
|
@ -602,6 +602,18 @@
|
|||
"description": "%configuration.suggest.completeJSDocs%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"javascript.suggest.jsdoc.generateReturns": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"markdownDescription": "%configuration.suggest.jsdoc.generateReturns%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"typescript.suggest.jsdoc.generateReturns": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"markdownDescription": "%configuration.suggest.jsdoc.generateReturns%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"typescript.locale": {
|
||||
"type": [
|
||||
"string",
|
||||
|
|
|
@ -68,6 +68,7 @@
|
|||
"configuration.javascript.checkJs.experimentalDecorators.deprecation": "This setting has been deprecated in favor of `js/ts.implicitProjectConfig.experimentalDecorators`.",
|
||||
"configuration.implicitProjectConfig.strictNullChecks": "Enable/disable [strict null checks](https://www.typescriptlang.org/tsconfig#strictNullChecks) in JavaScript and TypeScript files that are not part of a project. Existing `jsconfig.json` or `tsconfig.json` files override this setting.",
|
||||
"configuration.implicitProjectConfig.strictFunctionTypes": "Enable/disable [strict function types](https://www.typescriptlang.org/tsconfig#strictFunctionTypes) in JavaScript and TypeScript files that are not part of a project. Existing `jsconfig.json` or `tsconfig.json` files override this setting.",
|
||||
"configuration.suggest.jsdoc.generateReturns": "Enable/disable generating `@return` annotations for JSDoc templates. Requires using TypeScript 4.2+ in the workspace.",
|
||||
"configuration.suggest.autoImports": "Enable/disable auto import suggestions.",
|
||||
"taskDefinition.tsconfig.description": "The tsconfig file that defines the TS build.",
|
||||
"javascript.suggestionActions.enabled": "Enable/disable suggestion diagnostics for JavaScript files in the editor.",
|
||||
|
|
|
@ -182,6 +182,7 @@ export default class FileConfigurationManager extends Disposable {
|
|||
allowRenameOfImportPath: true,
|
||||
includeAutomaticOptionalChainCompletions: config.get<boolean>('suggest.includeAutomaticOptionalChainCompletions', true),
|
||||
provideRefactorNotApplicableReason: true,
|
||||
generateReturnInDocTemplate: config.get<boolean>('suggest.jsdoc.generateReturns', true),
|
||||
};
|
||||
|
||||
return preferences;
|
||||
|
|
|
@ -9,6 +9,7 @@ import { ITypeScriptServiceClient } from '../typescriptService';
|
|||
import { conditionalRegistration, requireConfiguration } from '../utils/dependentRegistration';
|
||||
import { DocumentSelector } from '../utils/documentSelector';
|
||||
import * as typeConverters from '../utils/typeConverters';
|
||||
import FileConfigurationManager from './fileConfigurationManager';
|
||||
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
@ -37,6 +38,7 @@ class JsDocCompletionProvider implements vscode.CompletionItemProvider {
|
|||
|
||||
constructor(
|
||||
private readonly client: ITypeScriptServiceClient,
|
||||
private readonly fileConfigurationManager: FileConfigurationManager,
|
||||
) { }
|
||||
|
||||
public async provideCompletionItems(
|
||||
|
@ -53,8 +55,12 @@ class JsDocCompletionProvider implements vscode.CompletionItemProvider {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
const args = typeConverters.Position.toFileLocationRequestArgs(file, position);
|
||||
const response = await this.client.execute('docCommentTemplate', args, token);
|
||||
const response = await this.client.interruptGetErr(async () => {
|
||||
await this.fileConfigurationManager.ensureConfigurationForDocument(document, token);
|
||||
|
||||
const args = typeConverters.Position.toFileLocationRequestArgs(file, position);
|
||||
return this.client.execute('docCommentTemplate', args, token);
|
||||
});
|
||||
if (response.type !== 'response' || !response.body) {
|
||||
return undefined;
|
||||
}
|
||||
|
@ -107,6 +113,9 @@ export function templateToSnippet(template: string): vscode.SnippetString {
|
|||
out += post + ` \${${snippetIndex++}}`;
|
||||
return out;
|
||||
});
|
||||
|
||||
template = template.replace(/\* @returns[ \t]*$/gm, `* @returns \${${snippetIndex++}}`);
|
||||
|
||||
return new vscode.SnippetString(template);
|
||||
}
|
||||
|
||||
|
@ -114,12 +123,14 @@ export function register(
|
|||
selector: DocumentSelector,
|
||||
modeId: string,
|
||||
client: ITypeScriptServiceClient,
|
||||
fileConfigurationManager: FileConfigurationManager,
|
||||
|
||||
): vscode.Disposable {
|
||||
return conditionalRegistration([
|
||||
requireConfiguration(modeId, 'suggest.completeJSDocs')
|
||||
], () => {
|
||||
return vscode.languages.registerCompletionItemProvider(selector.syntax,
|
||||
new JsDocCompletionProvider(client),
|
||||
new JsDocCompletionProvider(client, fileConfigurationManager),
|
||||
'*');
|
||||
});
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ export default class LanguageProvider extends Disposable {
|
|||
import('./languageFeatures/formatting').then(provider => this._register(provider.register(selector, this.description.id, this.client, this.fileConfigurationManager))),
|
||||
import('./languageFeatures/hover').then(provider => this._register(provider.register(selector, this.client))),
|
||||
import('./languageFeatures/implementations').then(provider => this._register(provider.register(selector, this.client))),
|
||||
import('./languageFeatures/jsDocCompletions').then(provider => this._register(provider.register(selector, this.description.id, this.client))),
|
||||
import('./languageFeatures/jsDocCompletions').then(provider => this._register(provider.register(selector, this.description.id, this.client, this.fileConfigurationManager))),
|
||||
import('./languageFeatures/organizeImports').then(provider => this._register(provider.register(selector, this.client, this.commandManager, this.fileConfigurationManager, this.telemetryReporter))),
|
||||
import('./languageFeatures/quickFix').then(provider => this._register(provider.register(selector, this.client, this.fileConfigurationManager, this.commandManager, this.client.diagnosticsManager, this.telemetryReporter))),
|
||||
import('./languageFeatures/refactor').then(provider => this._register(provider.register(selector, this.client, this.fileConfigurationManager, this.commandManager, this.telemetryReporter))),
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "code-oss-dev",
|
||||
"version": "1.54.0",
|
||||
"distro": "f542aeaaf397d5c71e480442fe772dbe362726db",
|
||||
"distro": "4e0b11c36e1bc4032ef3c28c82fdf336f7f48cf3",
|
||||
"author": {
|
||||
"name": "Microsoft Corporation"
|
||||
},
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
|
||||
/* TODO: actions should be part of the pane, but they aren't yet */
|
||||
.monaco-pane-view .pane:hover > .pane-header.expanded > .actions,
|
||||
.monaco-pane-view .pane:focus-within > .pane-header.expanded > .actions,
|
||||
.monaco-pane-view .pane > .pane-header.actions-always-visible.expanded > .actions,
|
||||
.monaco-pane-view .pane > .pane-header.focused.expanded > .actions {
|
||||
display: initial;
|
||||
|
|
|
@ -10,8 +10,8 @@ import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
|||
import { LinkedList } from 'vs/base/common/linkedList';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
export function isThenable<T>(obj: any): obj is Promise<T> {
|
||||
return obj && typeof (<Promise<any>>obj).then === 'function';
|
||||
export function isThenable<T>(obj: unknown): obj is Promise<T> {
|
||||
return !!obj && typeof (obj as unknown as Promise<T>).then === 'function';
|
||||
}
|
||||
|
||||
export interface CancelablePromise<T> extends Promise<T> {
|
||||
|
@ -166,10 +166,10 @@ export class Throttler {
|
|||
this.activePromise = promiseFactory();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.activePromise!.then((result: any) => {
|
||||
this.activePromise!.then((result: T) => {
|
||||
this.activePromise = null;
|
||||
resolve(result);
|
||||
}, (err: any) => {
|
||||
}, (err: unknown) => {
|
||||
this.activePromise = null;
|
||||
reject(err);
|
||||
});
|
||||
|
@ -179,7 +179,7 @@ export class Throttler {
|
|||
|
||||
export class Sequencer {
|
||||
|
||||
private current: Promise<any> = Promise.resolve(null);
|
||||
private current: Promise<unknown> = Promise.resolve(null);
|
||||
|
||||
queue<T>(promiseTask: ITask<Promise<T>>): Promise<T> {
|
||||
return this.current = this.current.then(() => promiseTask(), () => promiseTask());
|
||||
|
@ -188,7 +188,7 @@ export class Sequencer {
|
|||
|
||||
export class SequencerByKey<TKey> {
|
||||
|
||||
private promiseMap = new Map<TKey, Promise<any>>();
|
||||
private promiseMap = new Map<TKey, Promise<unknown>>();
|
||||
|
||||
queue<T>(key: TKey, promiseTask: ITask<Promise<T>>): Promise<T> {
|
||||
const runningPromise = this.promiseMap.get(key) ?? Promise.resolve();
|
||||
|
@ -321,7 +321,7 @@ export class ThrottledDelayer<T> {
|
|||
}
|
||||
|
||||
trigger(promiseFactory: ITask<Promise<T>>, delay?: number): Promise<T> {
|
||||
return this.delayer.trigger(() => this.throttler.queue(promiseFactory), delay) as any as Promise<T>;
|
||||
return this.delayer.trigger(() => this.throttler.queue(promiseFactory), delay) as unknown as Promise<T>;
|
||||
}
|
||||
|
||||
isTriggered(): boolean {
|
||||
|
@ -488,7 +488,7 @@ export function firstParallel<T>(promiseList: Promise<T>[], shouldStop: (t: T) =
|
|||
interface ILimitedTaskFactory<T> {
|
||||
factory: ITask<Promise<T>>;
|
||||
c: (value: T | Promise<T>) => void;
|
||||
e: (error?: any) => void;
|
||||
e: (error?: unknown) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -667,7 +667,7 @@ export class IntervalTimer implements IDisposable {
|
|||
|
||||
export class RunOnceScheduler {
|
||||
|
||||
protected runner: ((...args: any[]) => void) | null;
|
||||
protected runner: ((...args: unknown[]) => void) | null;
|
||||
|
||||
private timeoutToken: any;
|
||||
private timeout: number;
|
||||
|
@ -827,7 +827,7 @@ export class IdleValue<T> {
|
|||
|
||||
private _didRun: boolean = false;
|
||||
private _value?: T;
|
||||
private _error: any;
|
||||
private _error: unknown;
|
||||
|
||||
constructor(executor: () => T) {
|
||||
this._executor = () => {
|
||||
|
@ -1017,7 +1017,7 @@ export class IntervalCounter {
|
|||
|
||||
//#region
|
||||
|
||||
export type ValueCallback<T = any> = (value: T | Promise<T>) => void;
|
||||
export type ValueCallback<T = unknown> = (value: T | Promise<T>) => void;
|
||||
|
||||
/**
|
||||
* Creates a promise whose resolution or rejection can be controlled imperatively.
|
||||
|
@ -1025,7 +1025,7 @@ export type ValueCallback<T = any> = (value: T | Promise<T>) => void;
|
|||
export class DeferredPromise<T> {
|
||||
|
||||
private completeCallback!: ValueCallback<T>;
|
||||
private errorCallback!: (err: any) => void;
|
||||
private errorCallback!: (err: unknown) => void;
|
||||
private rejected = false;
|
||||
private resolved = false;
|
||||
|
||||
|
@ -1058,7 +1058,7 @@ export class DeferredPromise<T> {
|
|||
});
|
||||
}
|
||||
|
||||
public error(err: any) {
|
||||
public error(err: unknown) {
|
||||
return new Promise<void>(resolve => {
|
||||
this.errorCallback(err);
|
||||
this.rejected = true;
|
||||
|
@ -1080,14 +1080,14 @@ export class DeferredPromise<T> {
|
|||
//#region
|
||||
|
||||
export interface IWaitUntil {
|
||||
waitUntil(thenable: Promise<any>): void;
|
||||
waitUntil(thenable: Promise<unknown>): void;
|
||||
}
|
||||
|
||||
export class AsyncEmitter<T extends IWaitUntil> extends Emitter<T> {
|
||||
|
||||
private _asyncDeliveryQueue?: LinkedList<[Listener<T>, Omit<T, 'waitUntil'>]>;
|
||||
|
||||
async fireAsync(data: Omit<T, 'waitUntil'>, token: CancellationToken, promiseJoin?: (p: Promise<any>, listener: Function) => Promise<any>): Promise<void> {
|
||||
async fireAsync(data: Omit<T, 'waitUntil'>, token: CancellationToken, promiseJoin?: (p: Promise<unknown>, listener: Function) => Promise<unknown>): Promise<void> {
|
||||
if (!this._listeners) {
|
||||
return;
|
||||
}
|
||||
|
@ -1103,11 +1103,11 @@ export class AsyncEmitter<T extends IWaitUntil> extends Emitter<T> {
|
|||
while (this._asyncDeliveryQueue.size > 0 && !token.isCancellationRequested) {
|
||||
|
||||
const [listener, data] = this._asyncDeliveryQueue.shift()!;
|
||||
const thenables: Promise<any>[] = [];
|
||||
const thenables: Promise<unknown>[] = [];
|
||||
|
||||
const event = <T>{
|
||||
...data,
|
||||
waitUntil: (p: Promise<any>): void => {
|
||||
waitUntil: (p: Promise<unknown>): void => {
|
||||
if (Object.isFrozen(thenables)) {
|
||||
throw new Error('waitUntil can NOT be called asynchronous');
|
||||
}
|
||||
|
@ -1208,7 +1208,7 @@ export namespace Promises {
|
|||
return undefined; // do not rethrow so that other promises can settle
|
||||
})));
|
||||
|
||||
if (firstError) {
|
||||
if (typeof firstError !== 'undefined') {
|
||||
throw firstError;
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,6 @@ export function toSlashes(osPath: string) {
|
|||
* or `getRoot('\\server\shares\path') === \\server\shares\`
|
||||
*/
|
||||
export function getRoot(path: string, sep: string = posix.sep): string {
|
||||
|
||||
if (!path) {
|
||||
return '';
|
||||
}
|
||||
|
|
|
@ -15,15 +15,15 @@ export function isArray<T>(array: T | {}): array is T extends readonly any[] ? (
|
|||
/**
|
||||
* @returns whether the provided parameter is a JavaScript String or not.
|
||||
*/
|
||||
export function isString(str: any): str is string {
|
||||
export function isString(str: unknown): str is string {
|
||||
return (typeof str === 'string');
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns whether the provided parameter is a JavaScript Array and each element in the array is a string.
|
||||
*/
|
||||
export function isStringArray(value: any): value is string[] {
|
||||
return Array.isArray(value) && (<any[]>value).every(elem => isString(elem));
|
||||
export function isStringArray(value: unknown): value is string[] {
|
||||
return Array.isArray(value) && (<unknown[]>value).every(elem => isString(elem));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -31,7 +31,7 @@ export function isStringArray(value: any): value is string[] {
|
|||
* @returns whether the provided parameter is of type `object` but **not**
|
||||
* `null`, an `array`, a `regexp`, nor a `date`.
|
||||
*/
|
||||
export function isObject(obj: any): obj is Object {
|
||||
export function isObject(obj: unknown): obj is Object {
|
||||
// The method can't do a type cast since there are type (like strings) which
|
||||
// are subclasses of any put not positvely matched by the function. Hence type
|
||||
// narrowing results in wrong results.
|
||||
|
@ -46,21 +46,21 @@ export function isObject(obj: any): obj is Object {
|
|||
* In **contrast** to just checking `typeof` this will return `false` for `NaN`.
|
||||
* @returns whether the provided parameter is a JavaScript Number or not.
|
||||
*/
|
||||
export function isNumber(obj: any): obj is number {
|
||||
export function isNumber(obj: unknown): obj is number {
|
||||
return (typeof obj === 'number' && !isNaN(obj));
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns whether the provided parameter is a JavaScript Boolean or not.
|
||||
*/
|
||||
export function isBoolean(obj: any): obj is boolean {
|
||||
export function isBoolean(obj: unknown): obj is boolean {
|
||||
return (obj === true || obj === false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns whether the provided parameter is undefined.
|
||||
*/
|
||||
export function isUndefined(obj: any): obj is undefined {
|
||||
export function isUndefined(obj: unknown): obj is undefined {
|
||||
return (typeof obj === 'undefined');
|
||||
}
|
||||
|
||||
|
@ -74,12 +74,12 @@ export function isDefined<T>(arg: T | null | undefined): arg is T {
|
|||
/**
|
||||
* @returns whether the provided parameter is undefined or null.
|
||||
*/
|
||||
export function isUndefinedOrNull(obj: any): obj is undefined | null {
|
||||
export function isUndefinedOrNull(obj: unknown): obj is undefined | null {
|
||||
return (isUndefined(obj) || obj === null);
|
||||
}
|
||||
|
||||
|
||||
export function assertType(condition: any, type?: string): asserts condition {
|
||||
export function assertType(condition: unknown, type?: string): asserts condition {
|
||||
if (!condition) {
|
||||
throw new Error(type ? `Unexpected type, expected '${type}'` : 'Unexpected type');
|
||||
}
|
||||
|
@ -123,7 +123,7 @@ const hasOwnProperty = Object.prototype.hasOwnProperty;
|
|||
/**
|
||||
* @returns whether the provided parameter is an empty JavaScript Object or not.
|
||||
*/
|
||||
export function isEmptyObject(obj: any): obj is any {
|
||||
export function isEmptyObject(obj: unknown): obj is object {
|
||||
if (!isObject(obj)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -140,27 +140,27 @@ export function isEmptyObject(obj: any): obj is any {
|
|||
/**
|
||||
* @returns whether the provided parameter is a JavaScript Function or not.
|
||||
*/
|
||||
export function isFunction(obj: any): obj is Function {
|
||||
export function isFunction(obj: unknown): obj is Function {
|
||||
return (typeof obj === 'function');
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns whether the provided parameters is are JavaScript Function or not.
|
||||
*/
|
||||
export function areFunctions(...objects: any[]): boolean {
|
||||
export function areFunctions(...objects: unknown[]): boolean {
|
||||
return objects.length > 0 && objects.every(isFunction);
|
||||
}
|
||||
|
||||
export type TypeConstraint = string | Function;
|
||||
|
||||
export function validateConstraints(args: any[], constraints: Array<TypeConstraint | undefined>): void {
|
||||
export function validateConstraints(args: unknown[], constraints: Array<TypeConstraint | undefined>): void {
|
||||
const len = Math.min(args.length, constraints.length);
|
||||
for (let i = 0; i < len; i++) {
|
||||
validateConstraint(args[i], constraints[i]);
|
||||
}
|
||||
}
|
||||
|
||||
export function validateConstraint(arg: any, constraint: TypeConstraint | undefined): void {
|
||||
export function validateConstraint(arg: unknown, constraint: TypeConstraint | undefined): void {
|
||||
|
||||
if (isString(constraint)) {
|
||||
if (typeof arg !== constraint) {
|
||||
|
@ -174,7 +174,7 @@ export function validateConstraint(arg: any, constraint: TypeConstraint | undefi
|
|||
} catch {
|
||||
// ignore
|
||||
}
|
||||
if (!isUndefinedOrNull(arg) && arg.constructor === constraint) {
|
||||
if (!isUndefinedOrNull(arg) && (arg as any).constructor === constraint) {
|
||||
return;
|
||||
}
|
||||
if (constraint.length === 1 && constraint.call(undefined, arg) === true) {
|
||||
|
@ -204,8 +204,8 @@ export function getAllMethodNames(obj: object): string[] {
|
|||
return methods;
|
||||
}
|
||||
|
||||
export function createProxyObject<T extends object>(methodNames: string[], invoke: (method: string, args: any[]) => any): T {
|
||||
const createProxyMethod = (method: string): () => any => {
|
||||
export function createProxyObject<T extends object>(methodNames: string[], invoke: (method: string, args: unknown[]) => unknown): T {
|
||||
const createProxyMethod = (method: string): () => unknown => {
|
||||
return function () {
|
||||
const args = Array.prototype.slice.call(arguments, 0);
|
||||
return invoke(method, args);
|
||||
|
@ -242,7 +242,7 @@ export type AddFirstParameterToFunctions<Target, TargetFunctionsReturnType, Firs
|
|||
[K in keyof Target]:
|
||||
|
||||
// Function: add param to function
|
||||
Target[K] extends (...args: any) => TargetFunctionsReturnType ? (firstArg: FirstParameter, ...args: Parameters<Target[K]>) => ReturnType<Target[K]> :
|
||||
Target[K] extends (...args: any[]) => TargetFunctionsReturnType ? (firstArg: FirstParameter, ...args: Parameters<Target[K]>) => ReturnType<Target[K]> :
|
||||
|
||||
// Else: just leave as is
|
||||
Target[K]
|
||||
|
|
|
@ -71,10 +71,8 @@ export interface IIPCOptions {
|
|||
debugBrk?: number;
|
||||
|
||||
/**
|
||||
* See https://github.com/microsoft/vscode/issues/27665
|
||||
* Allows to pass in fresh execArgv to the forked process such that it doesn't inherit them from `process.execArgv`.
|
||||
* e.g. Launching the extension host process with `--inspect-brk=xxx` and then forking a process from the extension host
|
||||
* results in the forked process inheriting `--inspect-brk=xxx`.
|
||||
* If set, starts the fork with empty execArgv. If not set, execArgv from the parent proces are inherited,
|
||||
* except --inspect= and --inspect-brk= which are filtered as they would result in a port conflict.
|
||||
*/
|
||||
freshExecArgv?: boolean;
|
||||
|
||||
|
@ -198,6 +196,12 @@ export class Client implements IChannelClient, IDisposable {
|
|||
forkOpts.execArgv = ['--nolazy', '--inspect-brk=' + this.options.debugBrk];
|
||||
}
|
||||
|
||||
if (forkOpts.execArgv === undefined) {
|
||||
// if not set, the forked process inherits the execArgv of the parent process
|
||||
// --inspect and --inspect-brk can not be inherited as the port would conflict
|
||||
forkOpts.execArgv = process.execArgv.filter(a => !/^--inspect(-brk)?=/.test(a)); // remove
|
||||
}
|
||||
|
||||
if (isMacintosh && forkOpts.env) {
|
||||
// Unset `DYLD_LIBRARY_PATH`, as it leads to process crashes
|
||||
// See https://github.com/microsoft/vscode/issues/105848
|
||||
|
|
|
@ -275,7 +275,7 @@ class WorkspaceProvider implements IWorkspaceProvider {
|
|||
|
||||
static QUERY_PARAM_PAYLOAD = 'payload';
|
||||
|
||||
readonly trusted = undefined;
|
||||
readonly trusted = true;
|
||||
|
||||
constructor(
|
||||
public readonly workspace: IWorkspace,
|
||||
|
|
|
@ -15,7 +15,6 @@ import { URI } from 'vs/base/common/uri';
|
|||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { EditorOpenContext } from 'vs/platform/editor/common/editor';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IExternalOpener, IExternalUriResolver, IOpener, IOpenerService, IResolvedExternalUri, IValidator, matchesScheme, OpenOptions, ResolveExternalUriOptions } from 'vs/platform/opener/common/opener';
|
||||
|
||||
class CommandOpener implements IOpener {
|
||||
|
@ -106,8 +105,7 @@ export class OpenerService implements IOpenerService {
|
|||
|
||||
constructor(
|
||||
@ICodeEditorService editorService: ICodeEditorService,
|
||||
@ICommandService commandService: ICommandService,
|
||||
@ILogService private logService: ILogService
|
||||
@ICommandService commandService: ICommandService
|
||||
) {
|
||||
// Default external opener is going through window.open()
|
||||
this._defaultExternalOpener = {
|
||||
|
@ -169,7 +167,6 @@ export class OpenerService implements IOpenerService {
|
|||
const targetURI = typeof target === 'string' ? URI.parse(target) : target;
|
||||
// validate against the original URI that this URI resolves to, if one exists
|
||||
const validationTarget = this._resolvedUriTargets.get(targetURI) ?? targetURI;
|
||||
this.logService.trace(`OpenerService#open: ${targetURI.authority} validating via ${validationTarget.authority}`);
|
||||
for (const validator of this._validators) {
|
||||
if (!(await validator.shouldOpen(validationTarget))) {
|
||||
return false;
|
||||
|
@ -192,7 +189,6 @@ export class OpenerService implements IOpenerService {
|
|||
const result = await resolver.resolveExternalUri(resource, options);
|
||||
if (result) {
|
||||
if (!this._resolvedUriTargets.has(result.resolved)) {
|
||||
this.logService.trace(`OpenerService#resolveExternalUri: ${resource.authority} resolved to ${result.resolved.authority}`);
|
||||
this._resolvedUriTargets.set(result.resolved, resource);
|
||||
}
|
||||
return result;
|
||||
|
|
|
@ -1140,15 +1140,15 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
|
|||
const clonedOptions = { ...options };
|
||||
clonedOptions.inDiffEditor = true;
|
||||
clonedOptions.automaticLayout = false;
|
||||
clonedOptions.scrollbar = clonedOptions.scrollbar || {};
|
||||
// Clone scrollbar options before changing them
|
||||
clonedOptions.scrollbar = { ...(clonedOptions.scrollbar || {}) };
|
||||
clonedOptions.scrollbar.vertical = 'visible';
|
||||
clonedOptions.folding = false;
|
||||
clonedOptions.codeLens = this._diffCodeLens;
|
||||
clonedOptions.fixedOverflowWidgets = true;
|
||||
// clonedOptions.lineDecorationsWidth = '2ch';
|
||||
if (!clonedOptions.minimap) {
|
||||
clonedOptions.minimap = {};
|
||||
}
|
||||
// Clone minimap options before changing them
|
||||
clonedOptions.minimap = { ...(clonedOptions.minimap || {}) };
|
||||
clonedOptions.minimap.enabled = false;
|
||||
return clonedOptions;
|
||||
}
|
||||
|
|
|
@ -43,7 +43,6 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService
|
|||
import { StandaloneThemeServiceImpl } from 'vs/editor/standalone/browser/standaloneThemeServiceImpl';
|
||||
import { splitLines } from 'vs/base/common/strings';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
||||
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
|
||||
|
||||
|
@ -57,7 +56,7 @@ function withAllStandaloneServices<T extends IEditor>(domElement: HTMLElement, o
|
|||
}
|
||||
|
||||
if (!services.has(IOpenerService)) {
|
||||
services.set(IOpenerService, new OpenerService(services.get(ICodeEditorService), services.get(ICommandService), services.get(ILogService)));
|
||||
services.set(IOpenerService, new OpenerService(services.get(ICodeEditorService), services.get(ICommandService)));
|
||||
}
|
||||
|
||||
let result = callback(services);
|
||||
|
|
|
@ -8,7 +8,6 @@ import { URI } from 'vs/base/common/uri';
|
|||
import { OpenerService } from 'vs/editor/browser/services/openerService';
|
||||
import { TestCodeEditorService } from 'vs/editor/test/browser/editorTestServices';
|
||||
import { CommandsRegistry, ICommandService, NullCommandService } from 'vs/platform/commands/common/commands';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
import { matchesScheme } from 'vs/platform/opener/common/opener';
|
||||
|
||||
suite('OpenerService', function () {
|
||||
|
@ -31,13 +30,13 @@ suite('OpenerService', function () {
|
|||
});
|
||||
|
||||
test('delegate to editorService, scheme:///fff', async function () {
|
||||
const openerService = new OpenerService(editorService, NullCommandService, new NullLogService());
|
||||
const openerService = new OpenerService(editorService, NullCommandService);
|
||||
await openerService.open(URI.parse('another:///somepath'));
|
||||
assert.equal(editorService.lastInput!.options!.selection, undefined);
|
||||
});
|
||||
|
||||
test('delegate to editorService, scheme:///fff#L123', async function () {
|
||||
const openerService = new OpenerService(editorService, NullCommandService, new NullLogService());
|
||||
const openerService = new OpenerService(editorService, NullCommandService);
|
||||
|
||||
await openerService.open(URI.parse('file:///somepath#L23'));
|
||||
assert.equal(editorService.lastInput!.options!.selection!.startLineNumber, 23);
|
||||
|
@ -59,7 +58,7 @@ suite('OpenerService', function () {
|
|||
});
|
||||
|
||||
test('delegate to editorService, scheme:///fff#123,123', async function () {
|
||||
const openerService = new OpenerService(editorService, NullCommandService, new NullLogService());
|
||||
const openerService = new OpenerService(editorService, NullCommandService);
|
||||
|
||||
await openerService.open(URI.parse('file:///somepath#23'));
|
||||
assert.equal(editorService.lastInput!.options!.selection!.startLineNumber, 23);
|
||||
|
@ -77,7 +76,7 @@ suite('OpenerService', function () {
|
|||
});
|
||||
|
||||
test('delegate to commandsService, command:someid', async function () {
|
||||
const openerService = new OpenerService(editorService, commandService, new NullLogService());
|
||||
const openerService = new OpenerService(editorService, commandService);
|
||||
|
||||
const id = `aCommand${Math.random()}`;
|
||||
CommandsRegistry.registerCommand(id, function () { });
|
||||
|
@ -99,7 +98,7 @@ suite('OpenerService', function () {
|
|||
});
|
||||
|
||||
test('links are protected by validators', async function () {
|
||||
const openerService = new OpenerService(editorService, commandService, new NullLogService());
|
||||
const openerService = new OpenerService(editorService, commandService);
|
||||
|
||||
openerService.registerValidator({ shouldOpen: () => Promise.resolve(false) });
|
||||
|
||||
|
@ -110,7 +109,7 @@ suite('OpenerService', function () {
|
|||
});
|
||||
|
||||
test('links validated by validators go to openers', async function () {
|
||||
const openerService = new OpenerService(editorService, commandService, new NullLogService());
|
||||
const openerService = new OpenerService(editorService, commandService);
|
||||
|
||||
openerService.registerValidator({ shouldOpen: () => Promise.resolve(true) });
|
||||
|
||||
|
@ -129,7 +128,7 @@ suite('OpenerService', function () {
|
|||
});
|
||||
|
||||
test('links validated by multiple validators', async function () {
|
||||
const openerService = new OpenerService(editorService, commandService, new NullLogService());
|
||||
const openerService = new OpenerService(editorService, commandService);
|
||||
|
||||
let v1 = 0;
|
||||
openerService.registerValidator({
|
||||
|
@ -166,7 +165,7 @@ suite('OpenerService', function () {
|
|||
});
|
||||
|
||||
test('links invalidated by first validator do not continue validating', async function () {
|
||||
const openerService = new OpenerService(editorService, commandService, new NullLogService());
|
||||
const openerService = new OpenerService(editorService, commandService);
|
||||
|
||||
let v1 = 0;
|
||||
openerService.registerValidator({
|
||||
|
|
|
@ -61,9 +61,7 @@ export class NsfwWatcherService extends Disposable implements IWatcherService {
|
|||
});
|
||||
|
||||
// Logging
|
||||
if (this.verboseLogging) {
|
||||
this.log(`Start watching: [${rootsToStartWatching.map(r => r.path).join(',')}]\nStop watching: [${rootsToStopWatching.join(',')}]`);
|
||||
}
|
||||
this.debug(`Start watching: [${rootsToStartWatching.map(r => r.path).join(',')}]\nStop watching: [${rootsToStopWatching.join(',')}]`);
|
||||
|
||||
// Stop watching some roots
|
||||
rootsToStopWatching.forEach(root => {
|
||||
|
@ -133,9 +131,7 @@ export class NsfwWatcherService extends Disposable implements IWatcherService {
|
|||
}
|
||||
}
|
||||
|
||||
if (this.verboseLogging) {
|
||||
this.log(`Start watching with nsfw: ${request.path}`);
|
||||
}
|
||||
this.debug(`Start watching with nsfw: ${request.path}`);
|
||||
|
||||
nsfw(request.path, events => {
|
||||
for (const e of events) {
|
||||
|
@ -249,4 +245,8 @@ export class NsfwWatcherService extends Disposable implements IWatcherService {
|
|||
private error(message: string) {
|
||||
this._onDidLogMessage.fire({ type: 'error', message: `[File Watcher (nsfw)] ` + message });
|
||||
}
|
||||
|
||||
private debug(message: string) {
|
||||
this._onDidLogMessage.fire({ type: 'debug', message: `[File Watcher (chokidar)] ` + message });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -146,9 +146,7 @@ export class ChokidarWatcherService extends Disposable implements IWatcherServic
|
|||
this.warn(`Watcher basePath does not match version on disk and was corrected (original: ${basePath}, real: ${realBasePath})`);
|
||||
}
|
||||
|
||||
if (this.verboseLogging) {
|
||||
this.log(`Start watching with chokidar: ${realBasePath}, excludes: ${excludes.join(',')}, usePolling: ${usePolling ? 'true, interval ' + pollingInterval : 'false'}`);
|
||||
}
|
||||
this.debug(`Start watching with chokidar: ${realBasePath}, excludes: ${excludes.join(',')}, usePolling: ${usePolling ? 'true, interval ' + pollingInterval : 'false'}`);
|
||||
|
||||
let chokidarWatcher: chokidar.FSWatcher | null = chokidar.watch(realBasePath, watcherOpts);
|
||||
this._watcherCount++;
|
||||
|
@ -301,6 +299,10 @@ export class ChokidarWatcherService extends Disposable implements IWatcherServic
|
|||
this._onDidLogMessage.fire({ type: 'trace', message: `[File Watcher (chokidar)] ` + message });
|
||||
}
|
||||
|
||||
private debug(message: string) {
|
||||
this._onDidLogMessage.fire({ type: 'debug', message: `[File Watcher (chokidar)] ` + message });
|
||||
}
|
||||
|
||||
private warn(message: string) {
|
||||
this._onDidLogMessage.fire({ type: 'warn', message: `[File Watcher (chokidar)] ` + message });
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ export interface IDiskFileChange {
|
|||
}
|
||||
|
||||
export interface ILogMessage {
|
||||
type: 'trace' | 'warn' | 'error';
|
||||
type: 'trace' | 'warn' | 'error' | 'info' | 'debug';
|
||||
message: string;
|
||||
}
|
||||
|
||||
|
|
|
@ -51,6 +51,17 @@ export class MainThreadTesting extends Disposable implements MainThreadTestingSh
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
$retireTest(extId: string): void {
|
||||
for (const result of this.resultService.results) {
|
||||
if (result instanceof LiveTestResult) {
|
||||
result.retire(extId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
|
|
|
@ -1843,6 +1843,7 @@ export interface MainThreadTestingShape {
|
|||
$publishDiff(resource: ExtHostTestingResource, uri: UriComponents, diff: TestsDiff): void;
|
||||
$updateTestStateInRun(runId: string, testId: string, state: ITestState): void;
|
||||
$runTests(req: RunTestsRequest, token: CancellationToken): Promise<string>;
|
||||
$retireTest(extId: string): void;
|
||||
}
|
||||
|
||||
// --- proxy identifiers
|
||||
|
|
|
@ -170,6 +170,14 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
|||
collection.addRoot(hierarchy.root, id);
|
||||
Promise.resolve(hierarchy.discoveredInitialTests).then(() => collection.pushDiff([TestDiffOpType.DeltaDiscoverComplete, -1]));
|
||||
hierarchy.onDidChangeTest(e => collection.onItemChange(e, id));
|
||||
hierarchy.onDidInvalidateTest?.(e => {
|
||||
const internal = collection.getTestByReference(e);
|
||||
if (!internal) {
|
||||
console.warn(`Received a TestProvider.onDidInvalidateTest for a test that does not currently exist.`);
|
||||
} else {
|
||||
this.proxy.$retireTest(internal.item.extId);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
|
|
@ -192,7 +192,8 @@ export abstract class ViewPane extends Pane implements IView {
|
|||
return this._titleDescription;
|
||||
}
|
||||
|
||||
private readonly menuActions: ViewMenuActions;
|
||||
readonly menuActions: ViewMenuActions;
|
||||
|
||||
private progressBar!: ProgressBar;
|
||||
private progressIndicator!: IProgressIndicator;
|
||||
|
||||
|
@ -478,7 +479,7 @@ export abstract class ViewPane extends Pane implements IView {
|
|||
|
||||
private setActions(): void {
|
||||
if (this.toolbar) {
|
||||
this.toolbar.setActions(prepareActions(this.getActions()), prepareActions(this.getSecondaryActions()));
|
||||
this.toolbar.setActions(prepareActions(this.menuActions.getPrimaryActions()), prepareActions(this.menuActions.getSecondaryActions()));
|
||||
this.toolbar.context = this.getActionsContext();
|
||||
}
|
||||
}
|
||||
|
@ -496,18 +497,6 @@ export abstract class ViewPane extends Pane implements IView {
|
|||
this._onDidChangeTitleArea.fire();
|
||||
}
|
||||
|
||||
getActions(): IAction[] {
|
||||
return this.menuActions.getPrimaryActions();
|
||||
}
|
||||
|
||||
getSecondaryActions(): IAction[] {
|
||||
return this.menuActions.getSecondaryActions();
|
||||
}
|
||||
|
||||
getContextMenuActions(): IAction[] {
|
||||
return this.menuActions.getContextMenuActions();
|
||||
}
|
||||
|
||||
getActionViewItem(action: IAction): IActionViewItem | undefined {
|
||||
return createActionViewItem(this.instantiationService, action);
|
||||
}
|
||||
|
|
|
@ -583,13 +583,13 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
|
|||
const result = [];
|
||||
result.push(...this.menuActions.getPrimaryActions());
|
||||
if (this.isViewMergedWithContainer()) {
|
||||
result.push(...this.paneItems[0].pane.getActions());
|
||||
result.push(...this.paneItems[0].pane.menuActions.getPrimaryActions());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
getSecondaryActions(): IAction[] {
|
||||
const viewPaneActions = this.isViewMergedWithContainer() ? this.paneItems[0].pane.getSecondaryActions() : [];
|
||||
const viewPaneActions = this.isViewMergedWithContainer() ? this.paneItems[0].pane.menuActions.getSecondaryActions() : [];
|
||||
let menuActions = this.menuActions.getSecondaryActions();
|
||||
|
||||
const viewsSubmenuActionIndex = menuActions.findIndex(action => action instanceof SubmenuItemAction && action.item.submenu === ViewsSubMenu);
|
||||
|
@ -768,7 +768,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
|
|||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
const actions: IAction[] = viewPane.getContextMenuActions();
|
||||
const actions: IAction[] = viewPane.menuActions.getContextMenuActions();
|
||||
|
||||
let anchor: { x: number, y: number } = { x: event.posx, y: event.posy };
|
||||
this.contextMenuService.showContextMenu({
|
||||
|
|
|
@ -13,7 +13,7 @@ import { IExtensionManagementServerService } from 'vs/workbench/services/extensi
|
|||
import { IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { extensionButtonProminentBackground, extensionButtonProminentForeground, ExtensionToolTipAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions';
|
||||
import { IThemeService, IColorTheme, ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
import { IThemeService, IColorTheme, ThemeIcon, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { EXTENSION_BADGE_REMOTE_BACKGROUND, EXTENSION_BADGE_REMOTE_FOREGROUND } from 'vs/workbench/common/theme';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
@ -21,6 +21,7 @@ import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge';
|
|||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IUserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { installCountIcon, ratingIcon, remoteIcon, starEmptyIcon, starFullIcon, starHalfIcon, syncIgnoredIcon } from 'vs/workbench/contrib/extensions/browser/extensionsIcons';
|
||||
import { registerColor } from 'vs/platform/theme/common/colorRegistry';
|
||||
|
||||
export abstract class ExtensionWidget extends Disposable implements IExtensionContainer {
|
||||
private _extension: IExtension | null = null;
|
||||
|
@ -368,3 +369,13 @@ export class SyncIgnoredWidget extends ExtensionWidget {
|
|||
this.element.classList.toggle('hide', !(this.extension && this.extension.state === ExtensionState.Installed && this.userDataAutoSyncEnablementService.isEnabled() && this.extensionsWorkbenchService.isExtensionIgnoredToSync(this.extension)));
|
||||
}
|
||||
}
|
||||
|
||||
// Rating icon
|
||||
export const extensionRatingIconColor = registerColor('extensionIcon.starForeground', { light: '#DF6100', dark: '#FF8E00', hc: '#FF8E00' }, localize('extensionIconStarForeground', "The icon color for extension ratings."), true);
|
||||
|
||||
registerThemingParticipant((theme, collector) => {
|
||||
const extensionRatingIcon = theme.getColor(extensionRatingIconColor);
|
||||
if (extensionRatingIcon) {
|
||||
collector.addRule(`.extension-ratings .codicon-extensions-star-full, .extension-ratings .codicon-extensions-star-half { color: ${extensionRatingIcon}; }`);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -27,12 +27,6 @@
|
|||
margin-left: 0;
|
||||
}
|
||||
|
||||
/* TODO @misolori make this a color token */
|
||||
.extension-ratings .codicon-extensions-star-full,
|
||||
.extension-ratings .codicon-extensions-star-half {
|
||||
color: #FF8E00 !important;
|
||||
}
|
||||
|
||||
.extension-install-count .codicon,
|
||||
.extension-action.codicon-extensions-info-message,
|
||||
.extension-action.codicon-extensions-warning-message,
|
||||
|
|
|
@ -23,7 +23,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
|
|||
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { BareFontInfo } from 'vs/editor/common/config/fontInfo';
|
||||
import { getPixelRatio, getZoomLevel } from 'vs/base/browser/browser';
|
||||
import { IDisplayOutputLayoutUpdateRequest, IDisplayOutputViewModel, IGenericCellViewModel, IInsetRenderOutput, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { CellEditState, IDisplayOutputLayoutUpdateRequest, IDisplayOutputViewModel, IGenericCellViewModel, IInsetRenderOutput, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { DiffSide, DIFF_CELL_MARGIN, IDiffCellInfo, INotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
|
@ -134,6 +134,19 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD
|
|||
}
|
||||
}
|
||||
|
||||
setMarkdownCellEditState(cellId: string, editState: CellEditState): void {
|
||||
// throw new Error('Method not implemented.');
|
||||
}
|
||||
markdownCellDragStart(cellId: string, position: { clientY: number }): void {
|
||||
// throw new Error('Method not implemented.');
|
||||
}
|
||||
markdownCellDrag(cellId: string, position: { clientY: number }): void {
|
||||
// throw new Error('Method not implemented.');
|
||||
}
|
||||
markdownCellDragEnd(cellId: string, position: { clientY: number }): void {
|
||||
// throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
protected createEditor(parent: HTMLElement): void {
|
||||
this._rootElement = DOM.append(parent, DOM.$('.notebook-text-diff-editor'));
|
||||
this._overflowContainer = document.createElement('div');
|
||||
|
@ -589,6 +602,10 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD
|
|||
});
|
||||
}
|
||||
|
||||
updateMarkdownCellHeight() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
getCellByInfo(cellInfo: IDiffCellInfo): IGenericCellViewModel {
|
||||
return cellInfo.diffElement.getCellByUri(cellInfo.cellUri);
|
||||
}
|
||||
|
|
|
@ -38,6 +38,12 @@ export interface INotebookRendererContribution {
|
|||
readonly [NotebookRendererContribution.entrypoint]: string;
|
||||
}
|
||||
|
||||
export interface INotebookMarkdownRendererContribution {
|
||||
readonly [NotebookRendererContribution.id]?: string;
|
||||
readonly [NotebookRendererContribution.viewType]?: string;
|
||||
readonly [NotebookRendererContribution.entrypoint]: string;
|
||||
}
|
||||
|
||||
const notebookProviderContribution: IJSONSchema = {
|
||||
description: nls.localize('contributes.notebook.provider', 'Contributes notebook document provider.'),
|
||||
type: 'array',
|
||||
|
@ -144,3 +150,9 @@ export const notebookRendererExtensionPoint = ExtensionsRegistry.registerExtensi
|
|||
extensionPoint: 'notebookOutputRenderer',
|
||||
jsonSchema: notebookRendererContribution
|
||||
});
|
||||
|
||||
export const notebookMarkdownRendererExtensionPoint = ExtensionsRegistry.registerExtensionPoint<INotebookMarkdownRendererContribution[]>(
|
||||
{
|
||||
extensionPoint: 'notebookMarkdownRenderer',
|
||||
jsonSchema: notebookRendererContribution //
|
||||
});
|
||||
|
|
|
@ -167,6 +167,11 @@ export interface ICommonNotebookEditor {
|
|||
focusNotebookCell(cell: IGenericCellViewModel, focus: 'editor' | 'container' | 'output'): void;
|
||||
focusNextNotebookCell(cell: IGenericCellViewModel, focus: 'editor' | 'container' | 'output'): void;
|
||||
updateOutputHeight(cellInfo: ICommonCellInfo, output: IDisplayOutputViewModel, height: number, isInit: boolean): void;
|
||||
updateMarkdownCellHeight(cellId: string, height: number, isInit: boolean): void;
|
||||
setMarkdownCellEditState(cellId: string, editState: CellEditState): void;
|
||||
markdownCellDragStart(cellId: string, position: { clientY: number }): void;
|
||||
markdownCellDrag(cellId: string, position: { clientY: number }): void;
|
||||
markdownCellDragEnd(cellId: string, position: { clientY: number, ctrlKey: boolean, altKey: boolean }): void;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
@ -462,6 +467,10 @@ export interface INotebookEditor extends IEditor, ICommonNotebookEditor {
|
|||
*/
|
||||
layoutNotebookCell(cell: ICellViewModel, height: number): Promise<void>;
|
||||
|
||||
createMarkdownPreview(cell: ICellViewModel): Promise<void>;
|
||||
hideMarkdownPreview(cell: ICellViewModel): Promise<void>;
|
||||
removeMarkdownPreview(cell: ICellViewModel): Promise<void>;
|
||||
|
||||
/**
|
||||
* Render the output in webview layer
|
||||
*/
|
||||
|
@ -684,6 +693,7 @@ export interface BaseCellRenderTemplate {
|
|||
export interface MarkdownCellRenderTemplate extends BaseCellRenderTemplate {
|
||||
editorContainer: HTMLElement;
|
||||
foldingIndicator: HTMLElement;
|
||||
focusIndicatorBottom: HTMLElement;
|
||||
currentEditor?: ICodeEditor;
|
||||
}
|
||||
|
||||
|
|
|
@ -64,6 +64,7 @@ import { configureKernelIcon, errorStateIcon, successStateIcon } from 'vs/workbe
|
|||
import { debugIconStartForeground } from 'vs/workbench/contrib/debug/browser/debugColors';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { extname } from 'vs/base/common/resources';
|
||||
import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel';
|
||||
|
||||
const $ = DOM.$;
|
||||
|
||||
|
@ -1016,6 +1017,19 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
|
|||
this._updateForMetadata();
|
||||
}));
|
||||
|
||||
const useRenderer = this.configurationService.getValue<string>('notebook.experimental.useMarkdownRenderer');
|
||||
|
||||
if (useRenderer) {
|
||||
await this._resolveWebview();
|
||||
|
||||
await this._webview!.initializeMarkdown(this.viewModel.viewCells
|
||||
.filter(cell => cell.cellKind === CellKind.Markdown)
|
||||
.map(cell => ({ cellId: cell.id, content: cell.getText() }))
|
||||
// TODO: look at cell position cache instead of just getting first five cells
|
||||
.slice(0, 5));
|
||||
}
|
||||
|
||||
|
||||
// restore view states, including contributions
|
||||
|
||||
{
|
||||
|
@ -1105,6 +1119,18 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
|
|||
this._webview?.updateViewScrollTop(-scrollTop, false, updateItems);
|
||||
}
|
||||
}
|
||||
|
||||
if (this._webview?.markdownPreviewMapping) {
|
||||
const updateItems: { id: string, top: number }[] = [];
|
||||
this._webview!.markdownPreviewMapping.forEach(cellId => {
|
||||
const cell = this.viewModel?.viewCells.find(cell => cell.id === cellId);
|
||||
if (cell) {
|
||||
const cellTop = this._list.getAbsoluteTopOfElement(cell);
|
||||
updateItems.push({ id: cellId, top: cellTop });
|
||||
}
|
||||
});
|
||||
this._webview?.updateMarkdownScrollTop(updateItems);
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
|
@ -1957,6 +1983,41 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
|
|||
this._list.triggerScrollFromMouseWheelEvent(event);
|
||||
}
|
||||
|
||||
async createMarkdownPreview(cell: MarkdownCellViewModel) {
|
||||
if (!this._webview) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this._resolveWebview();
|
||||
|
||||
const cellTop = this._list.getAbsoluteTopOfElement(cell);
|
||||
if (this._webview.markdownPreviewMapping.has(cell.id)) {
|
||||
await this._webview!.showMarkdownPreview(cell.id, cell.getText(), cellTop);
|
||||
} else {
|
||||
await this._webview!.createMarkdownPreview(cell.id, cell.getText(), cellTop);
|
||||
}
|
||||
}
|
||||
|
||||
async hideMarkdownPreview(cell: MarkdownCellViewModel) {
|
||||
if (!this._webview) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this._resolveWebview();
|
||||
|
||||
await this._webview!.hideMarkdownPreview(cell.id);
|
||||
}
|
||||
|
||||
async removeMarkdownPreview(cell: MarkdownCellViewModel) {
|
||||
if (!this._webview) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this._resolveWebview();
|
||||
|
||||
await this._webview!.removeMarkdownPreview(cell.id);
|
||||
}
|
||||
|
||||
async createInset(cell: CodeCellViewModel, output: IInsetRenderOutput, offset: number): Promise<void> {
|
||||
this._insetModifyQueueByOutputId.queue(output.source.model.outputId, async () => {
|
||||
if (!this._webview) {
|
||||
|
@ -2048,6 +2109,45 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
|
|||
}
|
||||
}
|
||||
|
||||
updateMarkdownCellHeight(cellId: string, height: number, isInit: boolean) {
|
||||
const cell = this.viewModel?.viewCells.find(vc => vc.id === cellId);
|
||||
|
||||
if (cell && cell instanceof MarkdownCellViewModel) {
|
||||
cell.renderedMarkdownHeight = height;
|
||||
}
|
||||
}
|
||||
|
||||
setMarkdownCellEditState(cellId: string, editState: CellEditState): void {
|
||||
const cell = this.viewModel?.viewCells.find(vc => vc.id === cellId);
|
||||
|
||||
if (cell && cell instanceof MarkdownCellViewModel) {
|
||||
cell.editState = editState;
|
||||
}
|
||||
}
|
||||
|
||||
markdownCellDragStart(cellId: string, ctx: { clientY: number }): void {
|
||||
const cell = this.viewModel?.viewCells.find(vc => vc.id === cellId);
|
||||
|
||||
if (cell && cell instanceof MarkdownCellViewModel) {
|
||||
this._dndController?.startExplicitDrag(cell, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
markdownCellDrag(cellId: string, ctx: { clientY: number }): void {
|
||||
const cell = this.viewModel?.viewCells.find(vc => vc.id === cellId);
|
||||
|
||||
if (cell && cell instanceof MarkdownCellViewModel) {
|
||||
this._dndController?.explicitDrag(cell, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
markdownCellDragEnd(cellId: string, ctx: { clientY: number, ctrlKey: boolean, altKey: boolean }): void {
|
||||
const cell = this.viewModel?.viewCells.find(vc => vc.id === cellId);
|
||||
|
||||
if (cell && cell instanceof MarkdownCellViewModel) {
|
||||
this._dndController?.endExplicitDrag(cell, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
|
@ -2303,8 +2403,7 @@ registerThemingParticipant((theme, collector) => {
|
|||
collector.addRule(`
|
||||
.monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-focus-indicator-top:before,
|
||||
.monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-focus-indicator-bottom:before,
|
||||
.monaco-workbench .notebookOverlay .monaco-list:focus-within .markdown-cell-row.focused .cell-inner-container:not(.cell-editor-focus):before,
|
||||
.monaco-workbench .notebookOverlay .monaco-list:focus-within .markdown-cell-row.focused .cell-inner-container:not(.cell-editor-focus):after {
|
||||
.monaco-workbench .notebookOverlay .monaco-list:focus-within .markdown-cell-row.focused .cell-inner-container:not(.cell-editor-focus):before {
|
||||
border-color: ${focusedCellBorderColor} !important;
|
||||
}`);
|
||||
|
||||
|
@ -2321,8 +2420,7 @@ registerThemingParticipant((theme, collector) => {
|
|||
collector.addRule(`
|
||||
.monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-editor-focus .cell-focus-indicator-top:before,
|
||||
.monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-editor-focus .cell-focus-indicator-bottom:before,
|
||||
.monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-inner-container.cell-editor-focus:before,
|
||||
.monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-inner-container.cell-editor-focus:after {
|
||||
.monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-inner-container.cell-editor-focus:before {
|
||||
border-color: ${selectedCellBorderColor} !important;
|
||||
}`);
|
||||
|
||||
|
|
|
@ -26,13 +26,14 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
|
|||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { NotebookExtensionDescription } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { Memento } from 'vs/workbench/common/memento';
|
||||
import { INotebookEditorContribution, notebookProviderExtensionPoint, notebookRendererExtensionPoint } from 'vs/workbench/contrib/notebook/browser/extensionPoint';
|
||||
import { INotebookEditorContribution, notebookMarkdownRendererExtensionPoint, notebookProviderExtensionPoint, notebookRendererExtensionPoint } from 'vs/workbench/contrib/notebook/browser/extensionPoint';
|
||||
import { CellEditState, getActiveNotebookEditor, ICellViewModel, INotebookEditor, NotebookEditorOptions, updateEditorTopPadding } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { NotebookKernelProviderAssociationRegistry, NotebookViewTypesExtensionRegistry, updateNotebookKernelProvideAssociationSchema } from 'vs/workbench/contrib/notebook/browser/notebookKernelAssociation';
|
||||
import { CellViewModel } 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 { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, BUILTIN_RENDERER_ID, CellEditType, CellKind, CellOutputKind, DisplayOrderKey, ICellEditOperation, IDisplayOutput, INotebookDecorationRenderOptions, INotebookKernelInfo2, INotebookKernelProvider, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, ITransformedDisplayOutputDto, mimeTypeIsAlwaysSecure, mimeTypeSupportedByCore, notebookDocumentFilterMatch, NotebookEditorPriority, NOTEBOOK_DISPLAY_ORDER, RENDERER_NOT_AVAILABLE, sortMimeTypes } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, BUILTIN_RENDERER_ID, CellEditType, CellKind, CellOutputKind, DisplayOrderKey, ICellEditOperation, IDisplayOutput, INotebookDecorationRenderOptions, INotebookKernelInfo2, INotebookKernelProvider, INotebookMarkdownRendererInfo, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, ITransformedDisplayOutputDto, mimeTypeIsAlwaysSecure, mimeTypeSupportedByCore, notebookDocumentFilterMatch, NotebookEditorPriority, NOTEBOOK_DISPLAY_ORDER, RENDERER_NOT_AVAILABLE, sortMimeTypes } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { NotebookMarkdownRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookMarkdownRenderer';
|
||||
import { NotebookOutputRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookOutputRenderer';
|
||||
import { NotebookEditorDescriptor, NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider';
|
||||
import { IMainNotebookController, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
|
||||
|
@ -235,11 +236,13 @@ class ModelData implements IDisposable {
|
|||
this._modelEventListeners.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
export class NotebookService extends Disposable implements INotebookService, ICustomEditorViewTypesHandler {
|
||||
declare readonly _serviceBrand: undefined;
|
||||
private readonly _notebookProviders = new Map<string, { controller: IMainNotebookController, extensionData: NotebookExtensionDescription; }>();
|
||||
notebookProviderInfoStore: NotebookProviderInfoStore;
|
||||
notebookRenderersInfoStore: NotebookOutputRendererInfoStore = new NotebookOutputRendererInfoStore();
|
||||
private readonly markdownRenderersInfos = new Set<INotebookMarkdownRendererInfo>();
|
||||
notebookKernelProviderInfoStore: NotebookKernelProviderInfoStore = new NotebookKernelProviderInfoStore();
|
||||
private readonly _models = new ResourceMap<ModelData>();
|
||||
private _onDidChangeActiveEditor = new Emitter<string | null>();
|
||||
|
@ -320,6 +323,32 @@ export class NotebookService extends Disposable implements INotebookService, ICu
|
|||
}
|
||||
});
|
||||
|
||||
notebookMarkdownRendererExtensionPoint.setHandler((renderers) => {
|
||||
this.markdownRenderersInfos.clear();
|
||||
|
||||
for (const extension of renderers) {
|
||||
for (const notebookContribution of extension.value) {
|
||||
if (!notebookContribution.entrypoint) { // avoid crashing
|
||||
console.error(`Cannot register renderer for ${extension.description.identifier.value} since it did not have an entrypoint. This is now required: https://github.com/microsoft/vscode/issues/102644`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const id = notebookContribution.id ?? notebookContribution.viewType;
|
||||
if (!id) {
|
||||
console.error(`Notebook renderer from ${extension.description.identifier.value} is missing an 'id'`);
|
||||
continue;
|
||||
}
|
||||
|
||||
this.markdownRenderersInfos.add(new NotebookMarkdownRendererInfo({
|
||||
id,
|
||||
extension: extension.description,
|
||||
entrypoint: notebookContribution.entrypoint,
|
||||
displayName: 'todo',
|
||||
}));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this._editorService.registerCustomEditorViewTypesHandler('Notebook', this);
|
||||
|
||||
const updateOrder = () => {
|
||||
|
@ -747,6 +776,10 @@ export class NotebookService extends Disposable implements INotebookService, ICu
|
|||
return this.notebookRenderersInfoStore.get(id);
|
||||
}
|
||||
|
||||
getMarkdownRendererInfo(): INotebookMarkdownRendererInfo[] {
|
||||
return Array.from(this.markdownRenderersInfos);
|
||||
}
|
||||
|
||||
async resolveNotebook(viewType: string, uri: URI, forceReload: boolean, backupId?: string): Promise<NotebookTextModel> {
|
||||
|
||||
if (!await this.canResolve(viewType)) {
|
||||
|
|
|
@ -17,9 +17,9 @@ import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
|
|||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IOpenerService, matchesScheme } from 'vs/platform/opener/common/opener';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { ICommonCellInfo, ICommonNotebookEditor, IDisplayOutputLayoutUpdateRequest, IDisplayOutputViewModel, IGenericCellViewModel, IInsetRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { preloadsScriptStr } from 'vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads';
|
||||
import { transformWebviewThemeVars } from 'vs/workbench/contrib/notebook/browser/view/renderers/webviewThemeMapping';
|
||||
import { CellEditState, ICommonCellInfo, ICommonNotebookEditor, IDisplayOutputLayoutUpdateRequest, IDisplayOutputViewModel, IGenericCellViewModel, IInsetRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { CellOutputKind, IDisplayOutput, INotebookRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
|
||||
import { IWebviewService, WebviewContentPurpose, WebviewElement } from 'vs/workbench/contrib/webview/browser/webview';
|
||||
|
@ -37,6 +37,7 @@ export interface IDimensionMessage {
|
|||
id: string;
|
||||
init: boolean;
|
||||
data: DOM.Dimension;
|
||||
isOutput: boolean;
|
||||
}
|
||||
|
||||
export interface IMouseEnterMessage {
|
||||
|
@ -79,6 +80,38 @@ export interface IClickedDataUrlMessage {
|
|||
downloadName?: string;
|
||||
}
|
||||
|
||||
export interface IToggleMarkdownPreviewMessage {
|
||||
__vscode_notebook_message: boolean;
|
||||
type: 'toggleMarkdownPreview';
|
||||
cellId: string;
|
||||
}
|
||||
|
||||
export interface ICellDragStartMessage {
|
||||
__vscode_notebook_message: boolean;
|
||||
type: 'cell-drag-start';
|
||||
cellId: string;
|
||||
position: { clientX: number, clientY: number };
|
||||
}
|
||||
|
||||
export interface ICellDragMessage {
|
||||
__vscode_notebook_message: boolean;
|
||||
type: 'cell-drag';
|
||||
cellId: string;
|
||||
position: { clientX: number, clientY: number };
|
||||
}
|
||||
|
||||
export interface ICellDragEndMessage {
|
||||
readonly __vscode_notebook_message: boolean;
|
||||
readonly type: 'cell-drag-end';
|
||||
readonly cellId: string;
|
||||
readonly ctrlKey: boolean
|
||||
readonly altKey: boolean;
|
||||
readonly position: {
|
||||
readonly clientX: number;
|
||||
readonly clientY: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface IClearMessage {
|
||||
type: 'clear';
|
||||
}
|
||||
|
@ -111,6 +144,11 @@ export interface IViewScrollTopRequestMessage {
|
|||
version: number;
|
||||
}
|
||||
|
||||
export interface IViewScrollMarkdownRequestMessage {
|
||||
type: 'view-scroll-markdown';
|
||||
cells: { id: string; top: number }[];
|
||||
}
|
||||
|
||||
export interface IScrollRequestMessage {
|
||||
type: 'scroll';
|
||||
id: string;
|
||||
|
@ -170,6 +208,35 @@ export interface ICustomRendererMessage {
|
|||
message: unknown;
|
||||
}
|
||||
|
||||
export interface ICreateMarkdownMessage {
|
||||
type: 'createMarkdownPreview',
|
||||
id: string;
|
||||
content: string;
|
||||
top: number;
|
||||
}
|
||||
export interface IRemoveMarkdownMessage {
|
||||
type: 'removeMarkdownPreview',
|
||||
id: string;
|
||||
}
|
||||
|
||||
|
||||
export interface IHideMarkdownMessage {
|
||||
type: 'hideMarkdownPreview',
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface IShowMarkdownMessage {
|
||||
type: 'showMarkdownPreview',
|
||||
id: string;
|
||||
content: string;
|
||||
top: number;
|
||||
}
|
||||
|
||||
export interface IInitializeMarkdownMessage {
|
||||
type: 'initializeMarkdownPreview';
|
||||
cells: Array<{ cellId: string, content: string }>;
|
||||
}
|
||||
|
||||
export type FromWebviewMessage =
|
||||
| WebviewIntialized
|
||||
| IDimensionMessage
|
||||
|
@ -179,8 +246,12 @@ export type FromWebviewMessage =
|
|||
| IScrollAckMessage
|
||||
| IBlurOutputMessage
|
||||
| ICustomRendererMessage
|
||||
| IClickedDataUrlMessage;
|
||||
|
||||
| IClickedDataUrlMessage
|
||||
| IToggleMarkdownPreviewMessage
|
||||
| ICellDragStartMessage
|
||||
| ICellDragMessage
|
||||
| ICellDragEndMessage
|
||||
;
|
||||
export type ToWebviewMessage =
|
||||
| IClearMessage
|
||||
| IFocusOutputMessage
|
||||
|
@ -192,7 +263,13 @@ export type ToWebviewMessage =
|
|||
| IShowOutputMessage
|
||||
| IUpdatePreloadResourceMessage
|
||||
| IUpdateDecorationsMessage
|
||||
| ICustomRendererMessage;
|
||||
| ICustomRendererMessage
|
||||
| ICreateMarkdownMessage
|
||||
| IRemoveMarkdownMessage
|
||||
| IShowMarkdownMessage
|
||||
| IHideMarkdownMessage
|
||||
| IInitializeMarkdownMessage
|
||||
| IViewScrollMarkdownRequestMessage;
|
||||
|
||||
export type AnyMessage = FromWebviewMessage | ToWebviewMessage;
|
||||
|
||||
|
@ -221,6 +298,7 @@ export class BackLayerWebView<T extends ICommonCellInfo> extends Disposable {
|
|||
element: HTMLElement;
|
||||
webview: WebviewElement | undefined = undefined;
|
||||
insetMapping: Map<IDisplayOutputViewModel, ICachedInset<T>> = new Map();
|
||||
markdownPreviewMapping: Set<string> = new Set();
|
||||
hiddenInsetMapping: Set<IDisplayOutputViewModel> = new Set();
|
||||
reversedInsetMapping: Map<string, IDisplayOutputViewModel> = new Map();
|
||||
localResourceRootsCache: URI[] | undefined = undefined;
|
||||
|
@ -256,20 +334,173 @@ export class BackLayerWebView<T extends ICommonCellInfo> extends Disposable {
|
|||
this.element.style.height = '1400px';
|
||||
this.element.style.position = 'absolute';
|
||||
}
|
||||
generateContent(coreDependencies: string, baseUrl: string) {
|
||||
private generateContent(coreDependencies: string, baseUrl: string) {
|
||||
const markdownRenderersSrc = this.getMarkdownRendererScripts();
|
||||
return html`
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<base href="${baseUrl}/"/>
|
||||
<style>
|
||||
#container > div > div {
|
||||
#container > div > div.output {
|
||||
width: 100%;
|
||||
padding: ${this.options.outputNodePadding}px ${this.options.outputNodePadding}px ${this.options.outputNodePadding}px ${this.options.outputNodeLeftPadding}px;
|
||||
box-sizing: border-box;
|
||||
background-color: var(--vscode-notebook-outputContainerBackgroundColor);
|
||||
}
|
||||
|
||||
#container > div > div.preview {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
user-select: text;
|
||||
-webkit-user-select: text;
|
||||
-ms-user-select: text;
|
||||
white-space: initial;
|
||||
padding-left: 0px !important;
|
||||
}
|
||||
|
||||
/* markdown */
|
||||
|
||||
#container > div > div.preview img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
#container > div > div.preview a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#container > div > div.preview a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#container > div > div.preview a:focus,
|
||||
#container > div > div.preview input:focus,
|
||||
#container > div > div.preview select:focus,
|
||||
#container > div > div.preview textarea:focus {
|
||||
outline: 1px solid -webkit-focus-ring-color;
|
||||
outline-offset: -1px;
|
||||
}
|
||||
|
||||
#container > div > div.preview hr {
|
||||
border: 0;
|
||||
height: 2px;
|
||||
border-bottom: 2px solid;
|
||||
}
|
||||
|
||||
#container > div > div.preview h1 {
|
||||
padding-bottom: 0.3em;
|
||||
line-height: 1.2;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: solid;
|
||||
border-color: rgba(255, 255, 255, 0.18);
|
||||
}
|
||||
|
||||
#container > div > div.preview h1 {
|
||||
border-color: rgba(0, 0, 0, 0.18);
|
||||
}
|
||||
|
||||
#container > div > div.preview h1,
|
||||
#container > div > div.preview h2,
|
||||
#container > div > div.preview h3 {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
#container > div > div.preview div {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Adjust margin of first item in markdown cell */
|
||||
#container > div > div.preview *:first-child {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
/* h1 tags don't need top margin */
|
||||
#container > div > div.preview h1:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* Removes bottom margin when only one item exists in markdown cell */
|
||||
#container > div > div.preview *:only-child,
|
||||
#container > div > div.preview *:last-child {
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
/* makes all markdown cells consistent */
|
||||
#container > div > div.preview div {
|
||||
min-height: 24px;
|
||||
}
|
||||
|
||||
#container > div > div.preview table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
#container > div > div.preview table th,
|
||||
#container > div > div.preview table td {
|
||||
border: 1px solid;
|
||||
}
|
||||
|
||||
#container > div > div.preview table > thead > tr > th {
|
||||
text-align: left;
|
||||
border-bottom: 1px solid;
|
||||
}
|
||||
|
||||
#container > div > div.preview table > thead > tr > th,
|
||||
#container > div > div.preview table > thead > tr > td,
|
||||
#container > div > div.preview table > tbody > tr > th,
|
||||
#container > div > div.preview table > tbody > tr > td {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
#container > div > div.preview table > tbody > tr + tr > td {
|
||||
border-top: 1px solid;
|
||||
}
|
||||
|
||||
#container > div > div.preview blockquote {
|
||||
margin: 0 7px 0 5px;
|
||||
padding: 0 16px 0 10px;
|
||||
border-left-width: 5px;
|
||||
border-left-style: solid;
|
||||
}
|
||||
|
||||
#container > div > div.preview code,
|
||||
#container > div > div.preview .code {
|
||||
font-family: var(--monaco-monospace-font);
|
||||
font-size: 1em;
|
||||
line-height: 1.357em;
|
||||
}
|
||||
|
||||
#container > div > div.preview .code {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
#container > div > div.preview .latex-block {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#container > div > div.preview .latex {
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#container > div > div.preview .latex img,
|
||||
#container > div > div.preview .latex-block img {
|
||||
filter: brightness(0) invert(0)
|
||||
}
|
||||
|
||||
#container > div > div.preview.dragging {
|
||||
background-color: var(--vscode-editor-background);
|
||||
}
|
||||
|
||||
.monaco-workbench.vs-dark .notebookOverlay .cell.markdown .latex img,
|
||||
.monaco-workbench.vs-dark .notebookOverlay .cell.markdown .latex-block img {
|
||||
filter: brightness(0) invert(1)
|
||||
}
|
||||
|
||||
#container > div.nb-symbolHighlight > div {
|
||||
background-color: var(--vscode-notebook-symbolHighlightBackground);
|
||||
}
|
||||
|
@ -333,10 +564,29 @@ export class BackLayerWebView<T extends ICommonCellInfo> extends Disposable {
|
|||
${coreDependencies}
|
||||
<div id='container' class="widgetarea" style="position: absolute;width:100%;top: 0px"></div>
|
||||
<script>${preloadsScriptStr(this.options.outputNodePadding, this.options.outputNodeLeftPadding)}</script>
|
||||
${markdownRenderersSrc}
|
||||
</body>
|
||||
</html>`;
|
||||
}
|
||||
|
||||
private getMarkdownRendererScripts() {
|
||||
const markdownRenderers = this.notebookService.getMarkdownRendererInfo();
|
||||
|
||||
return markdownRenderers
|
||||
.sort((a, b) => {
|
||||
// prefer built-in extension
|
||||
if (a.extensionIsBuiltin) {
|
||||
return b.extensionIsBuiltin ? 0 : -1;
|
||||
}
|
||||
return b.extensionIsBuiltin ? 1 : -1;
|
||||
})
|
||||
.map(renderer => {
|
||||
return asWebviewUri(this.environmentService, this.id, renderer.entrypoint);
|
||||
})
|
||||
.map(src => `<script src="${src}"></script>`)
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
postRendererMessage(rendererId: string, message: any) {
|
||||
this._sendMessageToWebview({
|
||||
__vscode_notebook_message: true,
|
||||
|
@ -459,13 +709,18 @@ var requirejs = (function() {
|
|||
|
||||
if (data.__vscode_notebook_message) {
|
||||
if (data.type === 'dimension') {
|
||||
const height = data.data.height;
|
||||
const outputHeight = height;
|
||||
if (data.isOutput) {
|
||||
const height = data.data.height;
|
||||
const outputHeight = height;
|
||||
|
||||
const resolvedResult = this.resolveOutputId(data.id);
|
||||
if (resolvedResult) {
|
||||
const { cellInfo, output } = resolvedResult;
|
||||
this.notebookEditor.updateOutputHeight(cellInfo, output, outputHeight, !!data.init);
|
||||
const resolvedResult = this.resolveOutputId(data.id);
|
||||
if (resolvedResult) {
|
||||
const { cellInfo, output } = resolvedResult;
|
||||
this.notebookEditor.updateOutputHeight(cellInfo, output, outputHeight, !!data.init);
|
||||
}
|
||||
} else {
|
||||
const cellId = data.id.substr(0, data.id.length - '_preview'.length);
|
||||
this.notebookEditor.updateMarkdownCellHeight(cellId, data.data.height, !!data.init);
|
||||
}
|
||||
} else if (data.type === 'mouseenter') {
|
||||
const resolvedResult = this.resolveOutputId(data.id);
|
||||
|
@ -511,6 +766,18 @@ var requirejs = (function() {
|
|||
this._onDidClickDataLink(data);
|
||||
} else if (data.type === 'customRendererMessage') {
|
||||
this._onMessage.fire({ message: data.message, forRenderer: data.rendererId });
|
||||
} else if (data.type === 'toggleMarkdownPreview') {
|
||||
this.notebookEditor.setMarkdownCellEditState(data.cellId, CellEditState.Editing);
|
||||
} else if (data.type === 'cell-drag-start') {
|
||||
this.notebookEditor.markdownCellDragStart(data.cellId, data.position);
|
||||
} else if (data.type === 'cell-drag') {
|
||||
this.notebookEditor.markdownCellDrag(data.cellId, data.position);
|
||||
} else if (data.type === 'cell-drag-end') {
|
||||
this.notebookEditor.markdownCellDragEnd(data.cellId, {
|
||||
clientY: data.position.clientY,
|
||||
ctrlKey: data.ctrlKey,
|
||||
altKey: data.altKey,
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -559,7 +826,12 @@ var requirejs = (function() {
|
|||
|
||||
const workspaceFolders = this.contextService.getWorkspace().folders.map(x => x.uri);
|
||||
|
||||
this.localResourceRootsCache = [...this.notebookService.getNotebookProviderResourceRoots(), ...workspaceFolders, rootPath];
|
||||
this.localResourceRootsCache = [
|
||||
...this.notebookService.getNotebookProviderResourceRoots(),
|
||||
...this.notebookService.getMarkdownRendererInfo().map(x => dirname(x.entrypoint)),
|
||||
...workspaceFolders,
|
||||
rootPath,
|
||||
];
|
||||
|
||||
const webview = webviewService.createWebviewElement(this.id, {
|
||||
purpose: WebviewContentPurpose.NotebookRenderer,
|
||||
|
@ -611,6 +883,13 @@ var requirejs = (function() {
|
|||
return true;
|
||||
}
|
||||
|
||||
updateMarkdownScrollTop(items: { id: string, top: number }[]) {
|
||||
this._sendMessageToWebview({
|
||||
type: 'view-scroll-markdown',
|
||||
cells: items
|
||||
});
|
||||
}
|
||||
|
||||
updateViewScrollTop(top: number, forceDisplay: boolean, items: IDisplayOutputLayoutUpdateRequest[]) {
|
||||
if (this._disposed) {
|
||||
return;
|
||||
|
@ -639,6 +918,83 @@ var requirejs = (function() {
|
|||
});
|
||||
}
|
||||
|
||||
async createMarkdownPreview(cellId: string, content: string, cellTop: number) {
|
||||
if (this._disposed) {
|
||||
return;
|
||||
}
|
||||
|
||||
const initialTop = cellTop;
|
||||
this.markdownPreviewMapping.add(cellId);
|
||||
|
||||
this._sendMessageToWebview({
|
||||
type: 'createMarkdownPreview',
|
||||
id: cellId,
|
||||
content: content,
|
||||
top: initialTop,
|
||||
});
|
||||
}
|
||||
|
||||
async showMarkdownPreview(cellId: string, content: string, cellTop: number) {
|
||||
if (this._disposed) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._sendMessageToWebview({
|
||||
type: 'showMarkdownPreview',
|
||||
id: cellId,
|
||||
content: content,
|
||||
top: cellTop
|
||||
});
|
||||
}
|
||||
|
||||
async hideMarkdownPreview(cellId: string,) {
|
||||
if (this._disposed) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._sendMessageToWebview({
|
||||
type: 'hideMarkdownPreview',
|
||||
id: cellId
|
||||
});
|
||||
}
|
||||
|
||||
async removeMarkdownPreview(cellId: string,) {
|
||||
if (this._disposed) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.markdownPreviewMapping.delete(cellId);
|
||||
|
||||
this._sendMessageToWebview({
|
||||
type: 'removeMarkdownPreview',
|
||||
id: cellId
|
||||
});
|
||||
}
|
||||
|
||||
async initializeMarkdown(cells: Array<{ cellId: string, content: string }>) {
|
||||
await this._loaded;
|
||||
|
||||
// TODO: use proper handler
|
||||
const p = new Promise<void>(resolve => {
|
||||
this.webview?.onMessage(e => {
|
||||
if (e.type === 'initializedMarkdownPreview') {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
for (const cell of cells) {
|
||||
this.markdownPreviewMapping.add(cell.cellId);
|
||||
}
|
||||
|
||||
this._sendMessageToWebview({
|
||||
type: 'initializeMarkdownPreview',
|
||||
cells: cells,
|
||||
});
|
||||
|
||||
await p;
|
||||
}
|
||||
|
||||
async createInset(cellInfo: T, content: IInsetRenderOutput, cellTop: number, offset: number) {
|
||||
if (this._disposed) {
|
||||
return;
|
||||
|
|
|
@ -149,7 +149,7 @@ export class CellDragAndDropController extends Disposable {
|
|||
return;
|
||||
}
|
||||
|
||||
const dropDirection = this.getDropInsertDirection(event);
|
||||
const dropDirection = this.getDropInsertDirection(event.dragPosRatio);
|
||||
const insertionIndicatorAbsolutePos = dropDirection === 'above' ? event.cellTop : event.cellTop + event.cellHeight;
|
||||
const insertionIndicatorTop = insertionIndicatorAbsolutePos - this.list.scrollTop + BOTTOM_CELL_TOOLBAR_GAP / 2;
|
||||
if (insertionIndicatorTop >= 0) {
|
||||
|
@ -160,8 +160,8 @@ export class CellDragAndDropController extends Disposable {
|
|||
}
|
||||
}
|
||||
|
||||
private getDropInsertDirection(event: CellDragEvent): 'above' | 'below' {
|
||||
return event.dragPosRatio < 0.5 ? 'above' : 'below';
|
||||
private getDropInsertDirection(dragPosRatio: number): 'above' | 'below' {
|
||||
return dragPosRatio < 0.5 ? 'above' : 'below';
|
||||
}
|
||||
|
||||
private onCellDrop(event: CellDragEvent): void {
|
||||
|
@ -189,7 +189,7 @@ export class CellDragAndDropController extends Disposable {
|
|||
|
||||
const isCopy = (event.browserEvent.ctrlKey && !platform.isMacintosh) || (event.browserEvent.altKey && platform.isMacintosh);
|
||||
|
||||
const dropDirection = this.getDropInsertDirection(event);
|
||||
const dropDirection = this.getDropInsertDirection(event.dragPosRatio);
|
||||
const insertionIndicatorAbsolutePos = dropDirection === 'above' ? event.cellTop : event.cellTop + event.cellHeight;
|
||||
const insertionIndicatorTop = insertionIndicatorAbsolutePos - this.list.scrollTop + BOTTOM_CELL_TOOLBAR_GAP / 2;
|
||||
const editorHeight = this.notebookEditor.getDomNode().getBoundingClientRect().height;
|
||||
|
@ -275,4 +275,65 @@ export class CellDragAndDropController extends Disposable {
|
|||
|
||||
this.notebookEditor.textModel!.pushStackElement('Copy Cells', undefined, undefined);
|
||||
}
|
||||
|
||||
public startExplicitDrag(cell: ICellViewModel, position: { clientY: number }) {
|
||||
this.currentDraggedCell = cell;
|
||||
this.setInsertIndicatorVisibility(true);
|
||||
}
|
||||
|
||||
public explicitDrag(cell: ICellViewModel, position: { clientY: number }) {
|
||||
const target = this.list.elementAt(position.clientY);
|
||||
if (target && target !== cell) {
|
||||
const cellTop = this.list.getAbsoluteTopOfElement(target);
|
||||
const cellHeight = this.list.elementHeight(target);
|
||||
|
||||
const dragOffset = this.list.scrollTop + position.clientY - cellTop;
|
||||
|
||||
const dragPosInElement = dragOffset - cellTop;
|
||||
const dragPosRatio = dragPosInElement / cellHeight;
|
||||
|
||||
const dropDirection = this.getDropInsertDirection(dragPosRatio);
|
||||
const insertionIndicatorAbsolutePos = dropDirection === 'above' ? cellTop : cellTop + cellHeight;
|
||||
const insertionIndicatorTop = insertionIndicatorAbsolutePos - this.list.scrollTop + BOTTOM_CELL_TOOLBAR_GAP / 2;
|
||||
if (insertionIndicatorTop >= 0) {
|
||||
this.listInsertionIndicator.style.top = `${insertionIndicatorTop}px`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public endExplicitDrag(cell: ICellViewModel, ctx: { clientY: number, ctrlKey: boolean, altKey: boolean }) {
|
||||
this.currentDraggedCell = undefined;
|
||||
this.setInsertIndicatorVisibility(false);
|
||||
|
||||
const target = this.list.elementAt(ctx.clientY);
|
||||
if (!target || target === cell) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cellTop = this.list.getAbsoluteTopOfElement(target);
|
||||
const cellHeight = this.list.elementHeight(target);
|
||||
|
||||
const dragOffset = this.list.scrollTop + ctx.clientY - cellTop;
|
||||
|
||||
const dragPosInElement = dragOffset - cellTop;
|
||||
const dragPosRatio = dragPosInElement / cellHeight;
|
||||
|
||||
const dropDirection = this.getDropInsertDirection(dragPosRatio);
|
||||
|
||||
const isCopy = (ctx.ctrlKey && !platform.isMacintosh) || (ctx.altKey && platform.isMacintosh);
|
||||
if (isCopy) {
|
||||
this.copyCells([cell], target, dropDirection);
|
||||
} else {
|
||||
const viewModel = this.notebookEditor.viewModel!;
|
||||
let originalToIdx = viewModel.getCellIndex(target);
|
||||
if (dropDirection === 'below') {
|
||||
const relativeToIndex = viewModel.getCellIndex(target);
|
||||
const newIdx = viewModel.getNextVisibleCellIndex(relativeToIndex);
|
||||
originalToIdx = newIdx;
|
||||
}
|
||||
|
||||
const draggedCellRange = [this.notebookEditor.viewModel!.getCellIndex(cell), 1];
|
||||
this.notebookEditor.moveCellsToIdx(draggedCellRange[0], draggedCellRange[1], originalToIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -414,6 +414,7 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR
|
|||
|
||||
const bottomCellContainer = DOM.append(container, $('.cell-bottom-toolbar-container'));
|
||||
const betweenCellToolbar = disposables.add(this.createBetweenCellToolbar(bottomCellContainer, disposables, contextKeyService));
|
||||
const focusIndicatorBottom = DOM.append(container, $('.cell-focus-indicator.cell-focus-indicator-bottom'));
|
||||
|
||||
const statusBar = disposables.add(this.instantiationService.createInstance(CellEditorStatusBar, editorPart));
|
||||
DOM.hide(statusBar.durationContainer);
|
||||
|
@ -432,6 +433,7 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR
|
|||
editorPart,
|
||||
editorContainer,
|
||||
focusIndicatorLeft,
|
||||
focusIndicatorBottom,
|
||||
foldingIndicator,
|
||||
disposables,
|
||||
elementDisposables: new DisposableStore(),
|
||||
|
@ -524,6 +526,11 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR
|
|||
|
||||
elementDisposables.add(new CellContextKeyManager(templateData.contextKeyService, this.notebookEditor, this.notebookEditor.viewModel.notebookDocument!, element));
|
||||
|
||||
this.updateForLayout(element, templateData);
|
||||
elementDisposables.add(element.onDidChangeLayout(() => {
|
||||
this.updateForLayout(element, templateData);
|
||||
}));
|
||||
|
||||
// render toolbar first
|
||||
this.setupCellToolbarActions(templateData, elementDisposables);
|
||||
|
||||
|
@ -545,6 +552,11 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR
|
|||
templateData.statusBar.update(toolbarContext);
|
||||
}
|
||||
|
||||
private updateForLayout(element: MarkdownCellViewModel, templateData: MarkdownCellRenderTemplate): void {
|
||||
// templateData.focusIndicatorLeft.style.height = `${element.layoutInfo.indicatorHeight}px`;
|
||||
templateData.focusIndicatorBottom.style.top = `${element.layoutInfo.totalHeight - BOTTOM_CELL_TOOLBAR_GAP - CELL_BOTTOM_MARGIN}px`;
|
||||
}
|
||||
|
||||
disposeTemplate(templateData: MarkdownCellRenderTemplate): void {
|
||||
templateData.disposables.clear();
|
||||
}
|
||||
|
|
|
@ -23,16 +23,97 @@ import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/com
|
|||
import { NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { collapsedIcon, expandedIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons';
|
||||
import { renderIcon } from 'vs/base/browser/ui/iconLabel/iconLabels';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
|
||||
interface IMarkdownRenderStrategy extends IDisposable {
|
||||
update(): void;
|
||||
}
|
||||
|
||||
class WebviewMarkdownRenderer extends Disposable implements IMarkdownRenderStrategy {
|
||||
constructor(
|
||||
readonly notebookEditor: IActiveNotebookEditor,
|
||||
readonly viewCell: MarkdownCellViewModel
|
||||
) {
|
||||
super();
|
||||
}
|
||||
update(): void {
|
||||
this.notebookEditor.createMarkdownPreview(this.viewCell);
|
||||
}
|
||||
}
|
||||
|
||||
class BuiltinMarkdownRenderer extends Disposable implements IMarkdownRenderStrategy {
|
||||
private localDisposables = new DisposableStore();
|
||||
|
||||
constructor(
|
||||
readonly notebookEditor: IActiveNotebookEditor,
|
||||
readonly viewCell: MarkdownCellViewModel,
|
||||
readonly container: HTMLElement,
|
||||
readonly markdownContainer: HTMLElement,
|
||||
readonly dataSource: { codeEditor: CodeEditorWidget | null; }
|
||||
) {
|
||||
super();
|
||||
|
||||
this._register(getResizesObserver(this.markdownContainer, undefined, () => {
|
||||
if (viewCell.editState === CellEditState.Preview) {
|
||||
this.viewCell.renderedMarkdownHeight = container.clientHeight;
|
||||
}
|
||||
})).startObserving();
|
||||
}
|
||||
|
||||
update(): void {
|
||||
|
||||
const markdownRenderer = this.viewCell.getMarkdownRenderer();
|
||||
const renderedHTML = this.viewCell.getHTML();
|
||||
if (renderedHTML) {
|
||||
this.markdownContainer.appendChild(renderedHTML);
|
||||
}
|
||||
|
||||
if (this.dataSource.codeEditor) {
|
||||
// switch from editing mode
|
||||
this.viewCell.renderedMarkdownHeight = this.container.clientHeight;
|
||||
this.relayoutCell();
|
||||
} else {
|
||||
// first time, readonly mode
|
||||
this.localDisposables.add(markdownRenderer.onDidRenderAsync(() => {
|
||||
this.viewCell.renderedMarkdownHeight = this.container.clientHeight;
|
||||
this.relayoutCell();
|
||||
}));
|
||||
|
||||
this.localDisposables.add(this.viewCell.textBuffer.onDidChangeContent(() => {
|
||||
this.markdownContainer.innerText = '';
|
||||
this.viewCell.clearHTML();
|
||||
const renderedHTML = this.viewCell.getHTML();
|
||||
if (renderedHTML) {
|
||||
this.markdownContainer.appendChild(renderedHTML);
|
||||
}
|
||||
}));
|
||||
|
||||
this.viewCell.renderedMarkdownHeight = this.container.clientHeight;
|
||||
this.relayoutCell();
|
||||
}
|
||||
}
|
||||
|
||||
relayoutCell() {
|
||||
this.notebookEditor.layoutNotebookCell(this.viewCell, this.viewCell.layoutInfo.totalHeight);
|
||||
}
|
||||
}
|
||||
|
||||
export class StatefulMarkdownCell extends Disposable {
|
||||
|
||||
private editor: CodeEditorWidget | null = null;
|
||||
|
||||
get codeEditor() {
|
||||
return this.editor;
|
||||
}
|
||||
|
||||
private markdownContainer: HTMLElement;
|
||||
private editorPart: HTMLElement;
|
||||
|
||||
private localDisposables = new DisposableStore();
|
||||
private foldingState: CellFoldingState;
|
||||
private _activeCellRunPlaceholder: IDisposable | null = null;
|
||||
private _useRenderer: boolean = false;
|
||||
private _renderStrategy: IMarkdownRenderStrategy;
|
||||
|
||||
constructor(
|
||||
private readonly notebookEditor: IActiveNotebookEditor,
|
||||
|
@ -43,11 +124,21 @@ export class StatefulMarkdownCell extends Disposable {
|
|||
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
||||
@INotebookCellStatusBarService readonly notebookCellStatusBarService: INotebookCellStatusBarService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IConfigurationService private readonly configurationSerivce: IConfigurationService
|
||||
) {
|
||||
super();
|
||||
|
||||
this.markdownContainer = templateData.cellContainer;
|
||||
this.editorPart = templateData.editorPart;
|
||||
|
||||
this._useRenderer = !!(this.configurationSerivce.getValue<string>('notebook.experimental.useMarkdownRenderer'));
|
||||
if (this._useRenderer) {
|
||||
this._renderStrategy = new WebviewMarkdownRenderer(this.notebookEditor, this.viewCell);
|
||||
} else {
|
||||
this._renderStrategy = new BuiltinMarkdownRenderer(this.notebookEditor, this.viewCell, this.templateData.container, this.markdownContainer, this);
|
||||
}
|
||||
|
||||
this._register(this._renderStrategy);
|
||||
this._register(this.localDisposables);
|
||||
this._register(toDisposable(() => renderedEditors.delete(this.viewCell)));
|
||||
|
||||
|
@ -64,12 +155,6 @@ export class StatefulMarkdownCell extends Disposable {
|
|||
this.viewUpdate();
|
||||
}));
|
||||
|
||||
this._register(getResizesObserver(this.markdownContainer, undefined, () => {
|
||||
if (viewCell.editState === CellEditState.Preview) {
|
||||
this.viewCell.renderedMarkdownHeight = templateData.container.clientHeight;
|
||||
}
|
||||
})).startObserving();
|
||||
|
||||
const updateForFocusMode = () => {
|
||||
if (viewCell.focusMode === CellFocusMode.Editor) {
|
||||
this.focusEditorIfNeeded();
|
||||
|
@ -286,35 +371,8 @@ export class StatefulMarkdownCell extends Disposable {
|
|||
|
||||
this.markdownContainer.innerText = '';
|
||||
this.viewCell.clearHTML();
|
||||
const markdownRenderer = this.viewCell.getMarkdownRenderer();
|
||||
const renderedHTML = this.viewCell.getHTML();
|
||||
if (renderedHTML) {
|
||||
this.markdownContainer.appendChild(renderedHTML);
|
||||
}
|
||||
|
||||
if (this.editor) {
|
||||
// switch from editing mode
|
||||
this.viewCell.renderedMarkdownHeight = this.templateData.container.clientHeight;
|
||||
this.relayoutCell();
|
||||
} else {
|
||||
// first time, readonly mode
|
||||
this.localDisposables.add(markdownRenderer.onDidRenderAsync(() => {
|
||||
this.viewCell.renderedMarkdownHeight = this.templateData.container.clientHeight;
|
||||
this.relayoutCell();
|
||||
}));
|
||||
|
||||
this.localDisposables.add(this.viewCell.textBuffer.onDidChangeContent(() => {
|
||||
this.markdownContainer.innerText = '';
|
||||
this.viewCell.clearHTML();
|
||||
const renderedHTML = this.viewCell.getHTML();
|
||||
if (renderedHTML) {
|
||||
this.markdownContainer.appendChild(renderedHTML);
|
||||
}
|
||||
}));
|
||||
|
||||
this.viewCell.renderedMarkdownHeight = this.templateData.container.clientHeight;
|
||||
this.relayoutCell();
|
||||
}
|
||||
this._renderStrategy.update();
|
||||
}
|
||||
|
||||
private focusEditorIfNeeded() {
|
||||
|
@ -342,7 +400,7 @@ export class StatefulMarkdownCell extends Disposable {
|
|||
// this.relayoutCell();
|
||||
}
|
||||
|
||||
private relayoutCell(): void {
|
||||
relayoutCell(): void {
|
||||
this.notebookEditor.layoutNotebookCell(this.viewCell, this.viewCell.layoutInfo.totalHeight);
|
||||
}
|
||||
|
||||
|
@ -420,6 +478,7 @@ export class StatefulMarkdownCell extends Disposable {
|
|||
}
|
||||
|
||||
dispose() {
|
||||
this.notebookEditor.removeMarkdownPreview(this.viewCell);
|
||||
this.viewCell.detachTextEditor();
|
||||
super.dispose();
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import type { Event } from 'vs/base/common/event';
|
||||
import type { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ToWebviewMessage } from 'vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView';
|
||||
import { ICellDragEndMessage, ICellDragMessage, ICellDragStartMessage, ToWebviewMessage } from 'vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView';
|
||||
import { RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
|
||||
// !! IMPORTANT !! everything must be in-line within the webviewPreloads
|
||||
|
@ -136,7 +136,7 @@ function webviewPreloads() {
|
|||
|
||||
const outputObservers = new Map<string, ResizeObserver>();
|
||||
|
||||
const resizeObserve = (container: Element, id: string) => {
|
||||
const resizeObserve = (container: Element, id: string, output: boolean) => {
|
||||
const resizeObserver = new ResizeObserver(entries => {
|
||||
for (const entry of entries) {
|
||||
if (!document.body.contains(entry.target)) {
|
||||
|
@ -152,7 +152,8 @@ function webviewPreloads() {
|
|||
id: id,
|
||||
data: {
|
||||
height: entry.contentRect.height + __outputNodePadding__ * 2
|
||||
}
|
||||
},
|
||||
isOutput: output
|
||||
});
|
||||
} else {
|
||||
entry.target.style.padding = `0px`;
|
||||
|
@ -162,7 +163,8 @@ function webviewPreloads() {
|
|||
id: id,
|
||||
data: {
|
||||
height: entry.contentRect.height
|
||||
}
|
||||
},
|
||||
isOutput: output
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -320,6 +322,10 @@ function webviewPreloads() {
|
|||
mimeType?: string;
|
||||
element: HTMLElement;
|
||||
}
|
||||
interface ICreateMarkdownInfo {
|
||||
readonly content: string;
|
||||
readonly element: HTMLElement;
|
||||
}
|
||||
|
||||
interface IDestroyCellInfo {
|
||||
outputId: string;
|
||||
|
@ -327,6 +333,7 @@ function webviewPreloads() {
|
|||
|
||||
const onWillDestroyOutput = createEmitter<[string | undefined /* namespace */, IDestroyCellInfo | undefined /* cell uri */]>();
|
||||
const onDidCreateOutput = createEmitter<[string | undefined /* namespace */, ICreateCellInfo]>();
|
||||
const onDidCreateMarkdown = createEmitter<[string | undefined /* namespace */, ICreateMarkdownInfo]>();
|
||||
const onDidReceiveMessage = createEmitter<[string, unknown]>();
|
||||
|
||||
const matchesNs = (namespace: string, query: string | undefined) => namespace === '*' || query === namespace || query === 'undefined';
|
||||
|
@ -355,6 +362,7 @@ function webviewPreloads() {
|
|||
onDidReceiveMessage: mapEmitter(onDidReceiveMessage, ([ns, data]) => ns === namespace ? data : dontEmit),
|
||||
onWillDestroyOutput: mapEmitter(onWillDestroyOutput, ([ns, data]) => matchesNs(namespace, ns) ? data : dontEmit),
|
||||
onDidCreateOutput: mapEmitter(onDidCreateOutput, ([ns, data]) => matchesNs(namespace, ns) ? data : dontEmit),
|
||||
onDidCreateMarkdown: mapEmitter(onDidCreateMarkdown, ([ns, data]) => data),
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -405,6 +413,51 @@ function webviewPreloads() {
|
|||
const event = rawEvent as ({ data: ToWebviewMessage; });
|
||||
|
||||
switch (event.data.type) {
|
||||
case 'initializeMarkdownPreview':
|
||||
for (const cell of event.data.cells) {
|
||||
createMarkdownPreview(cell.cellId, cell.content, -10000);
|
||||
}
|
||||
|
||||
vscode.postMessage({
|
||||
__vscode_notebook_message: true,
|
||||
type: 'initializedMarkdownPreview',
|
||||
});
|
||||
break;
|
||||
case 'createMarkdownPreview':
|
||||
createMarkdownPreview(event.data.id, event.data.content, event.data.top);
|
||||
break;
|
||||
case 'showMarkdownPreview':
|
||||
{
|
||||
const data = event.data;
|
||||
let cellContainer = document.getElementById(data.id);
|
||||
if (cellContainer) {
|
||||
cellContainer.style.display = 'block';
|
||||
}
|
||||
const previewNode = document.getElementById(`${data.id}_container`);
|
||||
if (previewNode) {
|
||||
previewNode.style.top = `${data.top}px`;
|
||||
}
|
||||
updateMarkdownPreview(data.id, data.content);
|
||||
}
|
||||
break;
|
||||
case 'hideMarkdownPreview':
|
||||
{
|
||||
const data = event.data;
|
||||
let cellContainer = document.getElementById(data.id);
|
||||
if (cellContainer) {
|
||||
cellContainer.style.display = 'none';
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'removeMarkdownPreview':
|
||||
{
|
||||
const data = event.data;
|
||||
let cellContainer = document.getElementById(data.id);
|
||||
if (cellContainer) {
|
||||
cellContainer?.parentElement?.removeChild(cellContainer);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'html':
|
||||
enqueueOutputAction(event.data, async data => {
|
||||
const preloadResults = await Promise.all(data.requiredPreloads.map(p => preloadPromises.get(p.uri)));
|
||||
|
@ -431,6 +484,7 @@ function webviewPreloads() {
|
|||
}
|
||||
|
||||
const outputNode = document.createElement('div');
|
||||
outputNode.classList.add('output');
|
||||
outputNode.style.position = 'absolute';
|
||||
outputNode.style.top = data.top + 'px';
|
||||
outputNode.style.left = data.left + 'px';
|
||||
|
@ -468,7 +522,7 @@ function webviewPreloads() {
|
|||
cellOutputContainer.appendChild(outputNode);
|
||||
}
|
||||
|
||||
resizeObserve(outputNode, outputId);
|
||||
resizeObserve(outputNode, outputId, true);
|
||||
|
||||
vscode.postMessage({
|
||||
__vscode_notebook_message: true,
|
||||
|
@ -498,6 +552,21 @@ function webviewPreloads() {
|
|||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'view-scroll-markdown':
|
||||
{
|
||||
// const date = new Date();
|
||||
// console.log('----- will scroll ---- ', date.getMinutes() + ':' + date.getSeconds() + ':' + date.getMilliseconds());
|
||||
event.data.cells.map(cell => {
|
||||
const widget = document.getElementById(`${cell.id}_preview`)!;
|
||||
|
||||
if (widget) {
|
||||
widget.style.top = `${cell.top}px`;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
case 'clear':
|
||||
|
@ -589,6 +658,127 @@ function webviewPreloads() {
|
|||
__vscode_notebook_message: true,
|
||||
type: 'initialized'
|
||||
});
|
||||
|
||||
document.addEventListener('dragover', e => {
|
||||
// Allow dropping dragged markdown cells
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
const markdownCellDragDataType = 'x-vscode-markdown-cell-drag';
|
||||
|
||||
document.addEventListener('drop', e => {
|
||||
const data = e.dataTransfer?.getData(markdownCellDragDataType);
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
|
||||
const { cellId } = JSON.parse(data);
|
||||
const msg: ICellDragEndMessage = {
|
||||
__vscode_notebook_message: true,
|
||||
type: 'cell-drag-end',
|
||||
cellId: cellId,
|
||||
ctrlKey: e.ctrlKey,
|
||||
altKey: e.altKey,
|
||||
position: { clientX: e.clientX, clientY: e.clientY },
|
||||
};
|
||||
vscode.postMessage(msg);
|
||||
});
|
||||
|
||||
function createMarkdownPreview(cellId: string, content: string, top: number) {
|
||||
let cellContainer = document.getElementById(cellId);
|
||||
if (!cellContainer) {
|
||||
const container = document.getElementById('container')!;
|
||||
const newElement = document.createElement('div');
|
||||
|
||||
newElement.id = `${cellId}`;
|
||||
container.appendChild(newElement);
|
||||
cellContainer = newElement;
|
||||
|
||||
const previewContainerNode = document.createElement('div');
|
||||
previewContainerNode.style.position = 'absolute';
|
||||
previewContainerNode.style.top = top + 'px';
|
||||
previewContainerNode.id = `${cellId}_preview`;
|
||||
previewContainerNode.classList.add('preview');
|
||||
previewContainerNode.addEventListener('dblclick', () => {
|
||||
vscode.postMessage({
|
||||
__vscode_notebook_message: true,
|
||||
type: 'toggleMarkdownPreview',
|
||||
cellId,
|
||||
});
|
||||
});
|
||||
|
||||
previewContainerNode.setAttribute('draggable', 'true');
|
||||
|
||||
previewContainerNode.addEventListener('dragstart', e => {
|
||||
if (!e.dataTransfer) {
|
||||
return;
|
||||
}
|
||||
e.dataTransfer.setData(markdownCellDragDataType, JSON.stringify({ cellId }));
|
||||
|
||||
(e.target as HTMLElement).classList.add('dragging');
|
||||
|
||||
const msg: ICellDragStartMessage = {
|
||||
__vscode_notebook_message: true,
|
||||
type: 'cell-drag-start',
|
||||
cellId: cellId,
|
||||
position: { clientX: e.clientX, clientY: e.clientY },
|
||||
};
|
||||
vscode.postMessage(msg);
|
||||
});
|
||||
|
||||
previewContainerNode.addEventListener('drag', e => {
|
||||
const msg: ICellDragMessage = {
|
||||
__vscode_notebook_message: true,
|
||||
type: 'cell-drag',
|
||||
cellId: cellId,
|
||||
position: { clientX: e.clientX, clientY: e.clientY },
|
||||
};
|
||||
vscode.postMessage(msg);
|
||||
});
|
||||
|
||||
previewContainerNode.addEventListener('dragend', e => {
|
||||
(e.target as HTMLElement).classList.remove('dragging');
|
||||
});
|
||||
|
||||
cellContainer.appendChild(previewContainerNode);
|
||||
|
||||
const previewNode = document.createElement('div');
|
||||
previewContainerNode.appendChild(previewNode);
|
||||
|
||||
// TODO: handle namespace
|
||||
onDidCreateMarkdown.fire([undefined /* data.apiNamespace */, {
|
||||
element: previewNode,
|
||||
content: content
|
||||
}]);
|
||||
|
||||
resizeObserve(previewContainerNode, `${cellId}_preview`, false);
|
||||
|
||||
vscode.postMessage({
|
||||
__vscode_notebook_message: true,
|
||||
type: 'dimension',
|
||||
id: `${cellId}_preview`,
|
||||
init: true,
|
||||
data: {
|
||||
height: previewContainerNode.clientHeight
|
||||
},
|
||||
isOutput: false
|
||||
});
|
||||
} else {
|
||||
updateMarkdownPreview(cellId, content);
|
||||
}
|
||||
}
|
||||
|
||||
function updateMarkdownPreview(cellId: string, content: string) {
|
||||
const previewNode = document.getElementById(`${cellId}_preview`);
|
||||
if (previewNode) {
|
||||
// TODO: handle namespace
|
||||
onDidCreateMarkdown.fire([undefined /* data.apiNamespace */, {
|
||||
element: previewNode,
|
||||
content: content
|
||||
}]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const preloadsScriptStr = (outputNodePadding: number, outputNodeLeftPadding: number) => `(${webviewPreloads})()`.replace(/__outputNodePadding__/g, `${outputNodePadding}`).replace(/__outputNodeLeftPadding__/g, `${outputNodeLeftPadding}`);
|
||||
|
|
|
@ -140,6 +140,13 @@ export interface INotebookRendererInfo {
|
|||
matches(mimeType: string): boolean;
|
||||
}
|
||||
|
||||
export interface INotebookMarkdownRendererInfo {
|
||||
readonly entrypoint: URI;
|
||||
readonly extensionLocation: URI;
|
||||
readonly extensionId: ExtensionIdentifier;
|
||||
readonly extensionIsBuiltin: boolean;
|
||||
}
|
||||
|
||||
export interface IStreamOutput {
|
||||
outputKind: CellOutputKind.Text;
|
||||
text: string;
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { joinPath } from 'vs/base/common/resources';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { INotebookMarkdownRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
|
||||
export class NotebookMarkdownRendererInfo implements INotebookMarkdownRendererInfo {
|
||||
|
||||
readonly id: string;
|
||||
readonly entrypoint: URI;
|
||||
readonly displayName: string;
|
||||
readonly extensionLocation: URI;
|
||||
readonly extensionId: ExtensionIdentifier;
|
||||
readonly extensionIsBuiltin: boolean;
|
||||
|
||||
constructor(descriptor: {
|
||||
readonly id: string;
|
||||
readonly displayName: string;
|
||||
readonly entrypoint: string;
|
||||
readonly extension: IExtensionDescription;
|
||||
}) {
|
||||
this.id = descriptor.id;
|
||||
this.extensionId = descriptor.extension.identifier;
|
||||
this.extensionLocation = descriptor.extension.extensionLocation;
|
||||
this.entrypoint = joinPath(this.extensionLocation, descriptor.entrypoint);
|
||||
this.displayName = descriptor.displayName;
|
||||
this.extensionIsBuiltin = descriptor.extension.isBuiltin;
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@ import { NotebookExtensionDescription } from 'vs/workbench/api/common/extHost.pr
|
|||
import { Event } from 'vs/base/common/event';
|
||||
import {
|
||||
INotebookTextModel, INotebookRendererInfo,
|
||||
IEditor, INotebookKernelProvider, INotebookKernelInfo2, TransientMetadata, NotebookDataDto, TransientOptions, INotebookDecorationRenderOptions, INotebookExclusiveDocumentFilter, IOrderedMimeType, ITransformedDisplayOutputDto
|
||||
IEditor, INotebookKernelProvider, INotebookKernelInfo2, TransientMetadata, NotebookDataDto, TransientOptions, INotebookDecorationRenderOptions, INotebookExclusiveDocumentFilter, IOrderedMimeType, ITransformedDisplayOutputDto, INotebookMarkdownRendererInfo
|
||||
} from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
|
@ -56,6 +56,7 @@ export interface INotebookService {
|
|||
getContributedNotebookKernelProviders(): Promise<INotebookKernelProvider[]>;
|
||||
getContributedNotebookOutputRenderers(id: string): NotebookOutputRendererInfo | undefined;
|
||||
getRendererInfo(id: string): INotebookRendererInfo | undefined;
|
||||
getMarkdownRendererInfo(): INotebookMarkdownRendererInfo[];
|
||||
|
||||
resolveNotebook(viewType: string, uri: URI, forceReload: boolean, backupId?: string): Promise<NotebookTextModel>;
|
||||
getNotebookTextModel(uri: URI): NotebookTextModel | undefined;
|
||||
|
|
|
@ -12,7 +12,7 @@ import { BareFontInfo } from 'vs/editor/common/config/fontInfo';
|
|||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo';
|
||||
import { EditorModel } from 'vs/workbench/common/editor';
|
||||
import { ICellViewModel, INotebookEditor, INotebookEditorContribution, INotebookEditorMouseEvent, NotebookLayoutInfo, INotebookDeltaDecoration, INotebookEditorCreationOptions, NotebookEditorOptions, ICellOutputViewModel, IInsetRenderOutput, IDisplayOutputViewModel, ICommonCellInfo, IGenericCellViewModel, INotebookCellOutputLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { ICellViewModel, INotebookEditor, INotebookEditorContribution, INotebookEditorMouseEvent, NotebookLayoutInfo, INotebookDeltaDecoration, INotebookEditorCreationOptions, NotebookEditorOptions, ICellOutputViewModel, IInsetRenderOutput, IDisplayOutputViewModel, ICommonCellInfo, IGenericCellViewModel, INotebookCellOutputLayoutInfo, CellEditState } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer';
|
||||
import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher';
|
||||
import { CellViewModel, IModelDecorationsChangeAccessor, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
|
||||
|
@ -87,6 +87,18 @@ export class TestNotebookEditor implements INotebookEditor {
|
|||
updateOutputHeight(cellInfo: ICommonCellInfo, output: IDisplayOutputViewModel, height: number, isInit: boolean): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
setMarkdownCellEditState(cellId: string, editState: CellEditState): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
markdownCellDragStart(cellId: string, position: { clientY: number }): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
markdownCellDrag(cellId: string, position: { clientY: number }): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
markdownCellDragEnd(cellId: string, position: { clientY: number }): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
async beginComputeContributedKernels(): Promise<INotebookKernelInfo2[]> {
|
||||
return [];
|
||||
}
|
||||
|
@ -303,6 +315,18 @@ export class TestNotebookEditor implements INotebookEditor {
|
|||
createInset(cell: CellViewModel, output: IInsetRenderOutput, offset: number): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
createMarkdownPreview(cell: ICellViewModel): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
hideMarkdownPreview(cell: ICellViewModel): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
removeMarkdownPreview(cell: ICellViewModel): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
updateMarkdownCellHeight(cellId: string, height: number, isInit: boolean): void {
|
||||
// noop
|
||||
}
|
||||
removeInset(output: ICellOutputViewModel): void {
|
||||
// throw new Error('Method not implemented.');
|
||||
}
|
||||
|
|
|
@ -254,11 +254,11 @@ export class PreferencesEditor extends EditorPane {
|
|||
const promise = this.input && this.input.isDirty() ? this.editorService.save({ editor: this.input, groupId: this.group!.id }) : Promise.resolve(true);
|
||||
promise.then(() => {
|
||||
if (target === ConfigurationTarget.USER_LOCAL) {
|
||||
this.preferencesService.switchSettings(ConfigurationTarget.USER_LOCAL, this.preferencesService.userSettingsResource, true);
|
||||
this.preferencesService.switchSettings(ConfigurationTarget.USER_LOCAL, this.preferencesService.userSettingsResource);
|
||||
} else if (target === ConfigurationTarget.WORKSPACE) {
|
||||
this.preferencesService.switchSettings(ConfigurationTarget.WORKSPACE, this.preferencesService.workspaceSettingsResource!, true);
|
||||
this.preferencesService.switchSettings(ConfigurationTarget.WORKSPACE, this.preferencesService.workspaceSettingsResource!);
|
||||
} else if (target instanceof URI) {
|
||||
this.preferencesService.switchSettings(ConfigurationTarget.WORKSPACE_FOLDER, target, true);
|
||||
this.preferencesService.switchSettings(ConfigurationTarget.WORKSPACE_FOLDER, target);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -303,6 +303,10 @@ export class SettingsEditor2 extends EditorPane {
|
|||
}
|
||||
|
||||
private _setOptions(options: SettingsEditorOptions): void {
|
||||
if (options.focusSearch) {
|
||||
this.focusSearch();
|
||||
}
|
||||
|
||||
if (options.query) {
|
||||
this.searchWidget.setValue(options.query);
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import { assertIsDefined, withNullAsUndefined } from 'vs/base/common/types';
|
|||
import { URI } from 'vs/base/common/uri';
|
||||
import 'vs/css!./media/searchEditor';
|
||||
import { ICodeEditor, isDiffEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
|
@ -109,6 +110,7 @@ export const openNewSearchEditor =
|
|||
const activeWorkspaceRootUri = historyService.getLastActiveWorkspaceRoot(Schemas.file);
|
||||
const lastActiveWorkspaceRoot = activeWorkspaceRootUri ? withNullAsUndefined(workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri)) : undefined;
|
||||
|
||||
|
||||
const activeEditorControl = editorService.activeTextEditorControl;
|
||||
let activeModel: ICodeEditor | undefined;
|
||||
let selected = '';
|
||||
|
@ -133,7 +135,8 @@ export const openNewSearchEditor =
|
|||
|
||||
telemetryService.publicLog2('searchEditor/openNewSearchEditor');
|
||||
|
||||
const args: OpenSearchEditorArgs = { query: selected || undefined };
|
||||
const seedSearchStringFromSelection = _args.location === 'new' || configurationService.getValue<IEditorOptions>('editor').find!.seedSearchStringFromSelection;
|
||||
const args: OpenSearchEditorArgs = { query: seedSearchStringFromSelection ? selected : undefined };
|
||||
Object.entries(_args).forEach(([name, value]) => {
|
||||
if (value !== undefined) {
|
||||
(args as any)[name as any] = (typeof value === 'string') ? configurationResolverService.resolve(lastActiveWorkspaceRoot, value) : value;
|
||||
|
|
|
@ -80,11 +80,14 @@ export class HierarchicalByLocationProjection extends Disposable implements ITes
|
|||
}));
|
||||
|
||||
// when test states change, reflect in the tree
|
||||
this._register(results.onTestChanged(([, { item, state, computedState }]) => {
|
||||
// todo: optimize this to avoid needing to iterate
|
||||
this._register(results.onTestChanged(([, { item, state, retired, computedState }]) => {
|
||||
for (const i of this.items.values()) {
|
||||
if (i.test.item.extId === item.extId) {
|
||||
i.ownState = state.state;
|
||||
i.retired = retired;
|
||||
refreshComputedState(computedStateAccessor, i, this.addUpdated, computedState);
|
||||
this.addUpdated(i);
|
||||
this.updateEmitter.fire();
|
||||
return;
|
||||
}
|
||||
|
@ -120,6 +123,13 @@ export class HierarchicalByLocationProjection extends Disposable implements ITes
|
|||
return this.locations.getTestAtPosition(uri, position);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public hasTestInDocument(uri: URI) {
|
||||
return this.locations.hasTestInDocument(uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
|
@ -222,6 +232,7 @@ export class HierarchicalByLocationProjection extends Disposable implements ITes
|
|||
const prevState = this.results.getStateByExtId(item.test.item.extId)?.[1];
|
||||
if (prevState) {
|
||||
item.ownState = prevState.state.state;
|
||||
item.retired = prevState.retired;
|
||||
refreshComputedState(computedStateAccessor, item, this.addUpdated, prevState.computedState);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ export class HierarchicalElement implements ITestTreeElement {
|
|||
}
|
||||
|
||||
public state = TestRunState.Unset;
|
||||
public retired = false;
|
||||
public ownState = TestRunState.Unset;
|
||||
|
||||
constructor(public readonly test: InternalTestItem, public readonly parentItem: HierarchicalFolder | HierarchicalElement) {
|
||||
|
@ -73,6 +74,7 @@ export class HierarchicalFolder implements ITestTreeElement {
|
|||
return Iterable.concatNested(Iterable.map(this.children, c => c.debuggable));
|
||||
}
|
||||
|
||||
public retired = false;
|
||||
public state = TestRunState.Unset;
|
||||
public ownState = TestRunState.Unset;
|
||||
|
||||
|
|
|
@ -32,6 +32,11 @@ export interface ITestTreeProjection extends IDisposable {
|
|||
*/
|
||||
getTestAtPosition(uri: URI, position: Position): ITestTreeElement | undefined;
|
||||
|
||||
/**
|
||||
* Gets whether any test is defined in the given URI.
|
||||
*/
|
||||
hasTestInDocument(uri: URI): boolean;
|
||||
|
||||
/**
|
||||
* Applies pending update to the tree.
|
||||
*/
|
||||
|
@ -82,6 +87,11 @@ export interface ITestTreeElement {
|
|||
*/
|
||||
state: TestRunState;
|
||||
|
||||
/**
|
||||
* Whether the node's test result is 'retired' -- from an outdated test run.
|
||||
*/
|
||||
readonly retired: boolean;
|
||||
|
||||
readonly ownState: TestRunState;
|
||||
readonly label: string;
|
||||
readonly parentItem: ITestTreeElement | null;
|
||||
|
|
|
@ -27,6 +27,10 @@ export const locationsEqual = (a: ModeLocation | undefined, b: ModeLocation | un
|
|||
export class TestLocationStore<T extends { location?: ModeLocation, depth: number }> {
|
||||
private readonly itemsByUri = new Map<string, T[]>();
|
||||
|
||||
public hasTestInDocument(uri: URI) {
|
||||
return !!this.itemsByUri.get(uri.toString())?.length;
|
||||
}
|
||||
|
||||
public getTestAtPosition(uri: URI, position: Position) {
|
||||
const tests = this.itemsByUri.get(uri.toString());
|
||||
if (!tests) {
|
||||
|
|
|
@ -15,6 +15,7 @@ export const testingRunIcon = registerIcon('testing-run-icon', Codicon.run, loca
|
|||
export const testingRunAllIcon = registerIcon('testing-run-all-icon', Codicon.runAll, localize('testingRunAllIcon', 'Icon of the "run all tests" action.'));
|
||||
export const testingDebugIcon = registerIcon('testing-debug-icon', Codicon.debugAlt, localize('testingDebugIcon', 'Icon of the "debug test" action.'));
|
||||
export const testingCancelIcon = registerIcon('testing-cancel-icon', Codicon.close, localize('testingCancelIcon', 'Icon to cancel ongoing test runs.'));
|
||||
export const testingFilterIcon = registerIcon('testing-filter', Codicon.filter, localize('filterIcon', 'Icon for the \'Filter\' action in the testing view.'));
|
||||
|
||||
export const testingShowAsList = registerIcon('testing-show-as-list-icon', Codicon.listTree, localize('testingShowAsList', 'Icon shown when the test explorer is disabled as a tree.'));
|
||||
export const testingShowAsTree = registerIcon('testing-show-as-list-icon', Codicon.listFlat, localize('testingShowAsTree', 'Icon shown when the test explorer is disabled as a list.'));
|
||||
|
|
|
@ -41,6 +41,10 @@
|
|||
margin-right: 0.25em;
|
||||
}
|
||||
|
||||
.test-explorer .computed-state.retired {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.test-explorer .test-explorer-messages {
|
||||
padding: 0 12px 8px;
|
||||
}
|
||||
|
@ -92,6 +96,27 @@
|
|||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.testing-filter-action-item .testing-filter-wrapper input {
|
||||
padding-right: 30px !important;
|
||||
}
|
||||
|
||||
.testing-filter-action-item .monaco-action-bar {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.testing-filter-action-item > .monaco-action-bar .action-item .action-label.codicon.testing-filter-button {
|
||||
line-height: 20px;
|
||||
height: 20px;
|
||||
min-width: 22px;
|
||||
margin-left: 4px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/** -- decorations */
|
||||
|
||||
.monaco-editor .testing-run-glyph {
|
||||
|
|
|
@ -116,8 +116,8 @@ CommandsRegistry.registerCommand({
|
|||
|
||||
CommandsRegistry.registerCommand({
|
||||
id: 'vscode.revealTestInExplorer',
|
||||
handler: async (accessor: ServicesAccessor, path: string[]) => {
|
||||
accessor.get(ITestExplorerFilterState).reveal = path;
|
||||
handler: async (accessor: ServicesAccessor, extId: string) => {
|
||||
accessor.get(ITestExplorerFilterState).reveal.value = extId;
|
||||
accessor.get(IViewsService).openView(Testing.ExplorerViewId);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -4,9 +4,12 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems';
|
||||
import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview';
|
||||
import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem';
|
||||
import { HistoryInputBox } from 'vs/base/browser/ui/inputbox/inputBox';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { Action, IAction, IActionRunner } from 'vs/base/common/actions';
|
||||
import { Delayer } from 'vs/base/common/async';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
|
@ -14,22 +17,25 @@ import { localize } from 'vs/nls';
|
|||
import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions';
|
||||
import { ContextScopedHistoryInputBox } from 'vs/platform/browser/contextScopedHistoryWidget';
|
||||
import { ContextKeyEqualsExpr, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { inputActiveOptionBorder, inputActiveOptionForeground, inputActiveOptionBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { attachInputBoxStyler } from 'vs/platform/theme/common/styler';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IColorTheme, ICssStyleCollector, IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
import { ViewContainerLocation } from 'vs/workbench/common/views';
|
||||
import { testingFilterIcon } from 'vs/workbench/contrib/testing/browser/icons';
|
||||
import { Testing } from 'vs/workbench/contrib/testing/common/constants';
|
||||
import { ObservableValue } from 'vs/workbench/contrib/testing/common/observableValue';
|
||||
import { StoredValue } from 'vs/workbench/contrib/testing/common/storedValue';
|
||||
import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys';
|
||||
|
||||
export interface ITestExplorerFilterState {
|
||||
_serviceBrand: undefined;
|
||||
readonly onDidChange: Event<string>;
|
||||
readonly onDidRequestReveal: Event<string[]>;
|
||||
value: string;
|
||||
reveal: string[] | undefined;
|
||||
readonly text: ObservableValue<string>;
|
||||
/** Reveal request, the extId of the test to reveal */
|
||||
readonly reveal: ObservableValue<string | undefined>;
|
||||
readonly currentDocumentOnly: ObservableValue<boolean>;
|
||||
|
||||
readonly onDidRequestInputFocus: Event<void>;
|
||||
focusInput(): void;
|
||||
|
@ -39,37 +45,19 @@ export const ITestExplorerFilterState = createDecorator<ITestExplorerFilterState
|
|||
|
||||
export class TestExplorerFilterState implements ITestExplorerFilterState {
|
||||
declare _serviceBrand: undefined;
|
||||
private readonly revealRequest = new Emitter<string[]>();
|
||||
private readonly changeEmitter = new Emitter<string>();
|
||||
private readonly focusEmitter = new Emitter<void>();
|
||||
private _value = '';
|
||||
private _reveal?: string[];
|
||||
public readonly text = new ObservableValue('');
|
||||
public readonly currentDocumentOnly = ObservableValue.stored(new StoredValue<boolean>({
|
||||
key: 'testsByCurrentDocumentOnly',
|
||||
scope: StorageScope.WORKSPACE,
|
||||
target: StorageTarget.USER
|
||||
}, this.storage), false);
|
||||
|
||||
public readonly reveal = new ObservableValue<string | undefined>(undefined);
|
||||
|
||||
public readonly onDidRequestInputFocus = this.focusEmitter.event;
|
||||
public readonly onDidRequestReveal = this.revealRequest.event;
|
||||
public readonly onDidChange = this.changeEmitter.event;
|
||||
|
||||
public get reveal() {
|
||||
return this._reveal;
|
||||
}
|
||||
|
||||
public set reveal(v: string[] | undefined) {
|
||||
this._reveal = v;
|
||||
if (v !== undefined) {
|
||||
this.revealRequest.fire(v);
|
||||
}
|
||||
}
|
||||
|
||||
public get value() {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
public set value(v: string) {
|
||||
if (v !== this._value) {
|
||||
this._value = v;
|
||||
this.changeEmitter.fire(v);
|
||||
}
|
||||
}
|
||||
constructor(@IStorageService private readonly storage: IStorageService) { }
|
||||
|
||||
public focusInput() {
|
||||
this.focusEmitter.fire();
|
||||
|
@ -84,6 +72,8 @@ export class TestingExplorerFilter extends BaseActionViewItem {
|
|||
target: StorageTarget.USER
|
||||
});
|
||||
|
||||
private readonly filtersAction = new Action('markersFiltersAction', localize('testing.filters.menu', "More Filters..."), 'testing-filter-button ' + ThemeIcon.asClassName(testingFilterIcon));
|
||||
|
||||
constructor(
|
||||
action: IAction,
|
||||
@ITestExplorerFilterState private readonly state: ITestExplorerFilterState,
|
||||
|
@ -92,6 +82,8 @@ export class TestingExplorerFilter extends BaseActionViewItem {
|
|||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
) {
|
||||
super(null, action);
|
||||
this.updateFilterActiveState();
|
||||
this._register(state.currentDocumentOnly.onDidChange(this.updateFilterActiveState, this));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -108,10 +100,10 @@ export class TestingExplorerFilter extends BaseActionViewItem {
|
|||
placeholder: localize('testExplorerFilter', "Filter (e.g. text, !exclude)"),
|
||||
history: this.history.get([]),
|
||||
}));
|
||||
input.value = this.state.value;
|
||||
input.value = this.state.text.value;
|
||||
this._register(attachInputBoxStyler(input, this.themeService));
|
||||
|
||||
this._register(this.state.onDidChange(newValue => {
|
||||
this._register(this.state.text.onDidChange(newValue => {
|
||||
input.value = newValue;
|
||||
}));
|
||||
|
||||
|
@ -121,7 +113,7 @@ export class TestingExplorerFilter extends BaseActionViewItem {
|
|||
|
||||
this._register(input.onDidChange(() => updateDelayer.trigger(() => {
|
||||
input.addToHistory();
|
||||
this.state.value = input.value;
|
||||
this.state.text.value = input.value;
|
||||
})));
|
||||
|
||||
this._register(dom.addStandardDisposableListener(input.inputElement, dom.EventType.KEY_DOWN, e => {
|
||||
|
@ -131,6 +123,16 @@ export class TestingExplorerFilter extends BaseActionViewItem {
|
|||
e.preventDefault();
|
||||
}
|
||||
}));
|
||||
|
||||
const actionbar = this._register(new ActionBar(container, {
|
||||
actionViewItemProvider: action => {
|
||||
if (action.id === this.filtersAction.id) {
|
||||
return this.instantiationService.createInstance(FiltersDropdownMenuActionViewItem, action, this.state, this.actionRunner);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}));
|
||||
actionbar.push(this.filtersAction, { icon: true, label: false });
|
||||
}
|
||||
|
||||
|
||||
|
@ -160,6 +162,59 @@ export class TestingExplorerFilter extends BaseActionViewItem {
|
|||
this.saveState();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the 'checked' state of the filter submenu.
|
||||
*/
|
||||
private updateFilterActiveState() {
|
||||
this.filtersAction.checked = this.state.currentDocumentOnly.value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class FiltersDropdownMenuActionViewItem extends DropdownMenuActionViewItem {
|
||||
|
||||
constructor(
|
||||
action: IAction,
|
||||
private readonly filters: ITestExplorerFilterState,
|
||||
actionRunner: IActionRunner,
|
||||
@IContextMenuService contextMenuService: IContextMenuService
|
||||
) {
|
||||
super(action,
|
||||
{ getActions: () => this.getActions() },
|
||||
contextMenuService,
|
||||
{
|
||||
actionRunner,
|
||||
classNames: action.class,
|
||||
anchorAlignmentProvider: () => AnchorAlignment.RIGHT,
|
||||
menuAsChild: true
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
render(container: HTMLElement): void {
|
||||
super.render(container);
|
||||
this.updateChecked();
|
||||
}
|
||||
|
||||
private getActions(): IAction[] {
|
||||
return [
|
||||
{
|
||||
checked: this.filters.currentDocumentOnly.value,
|
||||
class: undefined,
|
||||
enabled: true,
|
||||
id: 'currentDocument',
|
||||
label: localize('testing.filters.currentFile', "Show in Active File Only"),
|
||||
run: async () => this.filters.currentDocumentOnly.value = !this.filters.currentDocumentOnly.value,
|
||||
tooltip: '',
|
||||
dispose: () => null
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
updateChecked(): void {
|
||||
this.element!.classList.toggle('checked', this._action.checked);
|
||||
}
|
||||
}
|
||||
|
||||
registerAction2(class extends Action2 {
|
||||
|
@ -177,3 +232,18 @@ registerAction2(class extends Action2 {
|
|||
}
|
||||
async run(): Promise<void> { }
|
||||
});
|
||||
|
||||
registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => {
|
||||
const inputActiveOptionBorderColor = theme.getColor(inputActiveOptionBorder);
|
||||
if (inputActiveOptionBorderColor) {
|
||||
collector.addRule(`.testing-filter-button.checked { border-color: ${inputActiveOptionBorderColor}; }`);
|
||||
}
|
||||
const inputActiveOptionForegroundColor = theme.getColor(inputActiveOptionForeground);
|
||||
if (inputActiveOptionForegroundColor) {
|
||||
collector.addRule(`.testing-filter-button.checked { color: ${inputActiveOptionForegroundColor}; }`);
|
||||
}
|
||||
const inputActiveOptionBackgroundColor = theme.getColor(inputActiveOptionBackground);
|
||||
if (inputActiveOptionBackgroundColor) {
|
||||
collector.addRule(`.testing-filter-button.checked { background-color: ${inputActiveOptionBackgroundColor}; }`);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -20,6 +20,7 @@ import { FuzzyScore } from 'vs/base/common/filters';
|
|||
import { splitGlobAware } from 'vs/base/common/glob';
|
||||
import { Iterable } from 'vs/base/common/iterator';
|
||||
import { Disposable, DisposableStore, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import 'vs/css!./media/testing';
|
||||
import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
|
@ -64,7 +65,7 @@ import { DebugAction, RunAction } from './testExplorerActions';
|
|||
export class TestingExplorerView extends ViewPane {
|
||||
public viewModel!: TestingExplorerViewModel;
|
||||
private filterActionBar = this._register(new MutableDisposable());
|
||||
private currentSubscription?: TestSubscriptionListener;
|
||||
private readonly currentSubscription = new MutableDisposable<TestSubscriptionListener>();
|
||||
private container!: HTMLElement;
|
||||
private finishDiscovery?: () => void;
|
||||
private readonly location = TestingContextKeys.explorerLocation.bindTo(this.contextKeyService);;
|
||||
|
@ -112,17 +113,16 @@ export class TestingExplorerView extends ViewPane {
|
|||
this._register(this.instantiationService.createInstance(TestRunProgress, messagesContainer, this.getProgressLocation()));
|
||||
|
||||
const listContainer = dom.append(this.container, dom.$('.test-explorer-tree'));
|
||||
this.viewModel = this.instantiationService.createInstance(TestingExplorerViewModel, listContainer, this.onDidChangeBodyVisibility, this.currentSubscription);
|
||||
this.viewModel = this.instantiationService.createInstance(TestingExplorerViewModel, listContainer, this.onDidChangeBodyVisibility, this.currentSubscription.value);
|
||||
this._register(this.viewModel);
|
||||
|
||||
this._register(this.onDidChangeBodyVisibility(visible => {
|
||||
if (!visible && this.currentSubscription) {
|
||||
this.currentSubscription.dispose();
|
||||
this.currentSubscription = undefined;
|
||||
this.currentSubscription.value = undefined;
|
||||
this.viewModel.replaceSubscription(undefined);
|
||||
} else if (visible && !this.currentSubscription) {
|
||||
this.currentSubscription = this.createSubscription();
|
||||
this.viewModel.replaceSubscription(this.currentSubscription);
|
||||
} else if (visible && !this.currentSubscription.value) {
|
||||
this.currentSubscription.value = this.createSubscription();
|
||||
this.viewModel.replaceSubscription(this.currentSubscription.value);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
@ -227,7 +227,6 @@ export class TestingExplorerViewModel extends Disposable {
|
|||
@ITestExplorerFilterState filterState: TestExplorerFilterState,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
@ICodeEditorService codeEditorService: ICodeEditorService,
|
||||
@IStorageService private readonly storageService: IStorageService,
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
||||
@ITestResultService private readonly testResults: ITestResultService,
|
||||
|
@ -239,13 +238,7 @@ export class TestingExplorerViewModel extends Disposable {
|
|||
|
||||
const labels = this._register(instantiationService.createInstance(ResourceLabels, { onDidChangeVisibility: onDidChangeVisibility }));
|
||||
|
||||
this.filter = new TestsFilter(filterState.value);
|
||||
|
||||
this._register(filterState.onDidChange(text => {
|
||||
this.filter.setFilter(text);
|
||||
this.tree.refilter();
|
||||
}));
|
||||
|
||||
this.filter = this.instantiationService.createInstance(TestsFilter);
|
||||
this.tree = instantiationService.createInstance(
|
||||
WorkbenchObjectTree,
|
||||
'Test Explorer List',
|
||||
|
@ -263,11 +256,22 @@ export class TestingExplorerViewModel extends Disposable {
|
|||
accessibilityProvider: instantiationService.createInstance(ListAccessibilityProvider),
|
||||
filter: this.filter,
|
||||
}) as WorkbenchObjectTree<ITestTreeElement, FuzzyScore>;
|
||||
|
||||
this._register(Event.any(filterState.currentDocumentOnly.onDidChange, filterState.text.onDidChange)(this.tree.refilter, this.tree));
|
||||
this._register(editorService.onDidActiveEditorChange(() => {
|
||||
if (filterState.currentDocumentOnly.value && editorService.activeEditor?.resource) {
|
||||
if (this.projection.hasTestInDocument(editorService.activeEditor.resource)) {
|
||||
this.filter.filterToUri(editorService.activeEditor.resource);
|
||||
this.tree.refilter();
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
this._register(this.tree);
|
||||
|
||||
this._register(dom.addStandardDisposableListener(this.tree.getHTMLElement(), 'keydown', evt => {
|
||||
if (DefaultKeyboardNavigationDelegate.mightProducePrintableCharacter(evt)) {
|
||||
filterState.value = evt.browserEvent.key;
|
||||
filterState.text.value = evt.browserEvent.key;
|
||||
filterState.focusInput();
|
||||
}
|
||||
}));
|
||||
|
@ -282,7 +286,7 @@ export class TestingExplorerViewModel extends Disposable {
|
|||
}
|
||||
}));
|
||||
|
||||
const tracker = this._register(new CodeEditorTracker(codeEditorService, this));
|
||||
const tracker = this._register(this.instantiationService.createInstance(CodeEditorTracker, this));
|
||||
this._register(onDidChangeVisibility(visible => {
|
||||
if (visible) {
|
||||
tracker.activate();
|
||||
|
@ -447,7 +451,10 @@ class CodeEditorTracker {
|
|||
private store = new DisposableStore();
|
||||
private lastRevealed?: ITestTreeElement;
|
||||
|
||||
constructor(@ICodeEditorService private readonly codeEditorService: ICodeEditorService, private readonly model: TestingExplorerViewModel) {
|
||||
constructor(
|
||||
private readonly model: TestingExplorerViewModel,
|
||||
@ICodeEditorService private readonly codeEditorService: ICodeEditorService,
|
||||
) {
|
||||
}
|
||||
|
||||
public activate() {
|
||||
|
@ -496,22 +503,23 @@ class CodeEditorTracker {
|
|||
}
|
||||
|
||||
const enum FilterResult {
|
||||
Include,
|
||||
Exclude,
|
||||
Inherit,
|
||||
Include,
|
||||
}
|
||||
|
||||
class TestsFilter implements ITreeFilter<ITestTreeElement> {
|
||||
private lastText?: string;
|
||||
private filters: [include: boolean, value: string][] | undefined;
|
||||
private _filterToUri: string | undefined;
|
||||
|
||||
constructor(initialFilter: string) {
|
||||
this.setFilter(initialFilter);
|
||||
}
|
||||
constructor(@ITestExplorerFilterState private readonly state: ITestExplorerFilterState) { }
|
||||
|
||||
/**
|
||||
* Parses and updates the tree filter. Supports lists of patterns that can be !negated.
|
||||
*/
|
||||
public setFilter(text: string) {
|
||||
private setFilter(text: string) {
|
||||
this.lastText = text;
|
||||
text = text.trim();
|
||||
|
||||
if (!text) {
|
||||
|
@ -529,37 +537,68 @@ class TestsFilter implements ITreeFilter<ITestTreeElement> {
|
|||
}
|
||||
}
|
||||
|
||||
public filter(element: ITestTreeElement): TreeFilterResult<void> {
|
||||
for (let e: ITestTreeElement | null = element; e; e = e.parentItem) {
|
||||
switch (this.testFilterText(e.label)) {
|
||||
case FilterResult.Exclude:
|
||||
return TreeVisibility.Hidden;
|
||||
case FilterResult.Include:
|
||||
return TreeVisibility.Visible;
|
||||
case FilterResult.Inherit:
|
||||
// continue to parent
|
||||
}
|
||||
}
|
||||
|
||||
return TreeVisibility.Recurse;
|
||||
public filterToUri(uri: URI) {
|
||||
this._filterToUri = uri.toString();
|
||||
}
|
||||
|
||||
private testFilterText(data: string) {
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public filter(element: ITestTreeElement): TreeFilterResult<void> {
|
||||
if (this.state.text.value !== this.lastText) {
|
||||
this.setFilter(this.state.text.value);
|
||||
}
|
||||
|
||||
switch (Math.min(this.testFilterText(element), this.testLocation(element))) {
|
||||
case FilterResult.Exclude:
|
||||
return TreeVisibility.Hidden;
|
||||
case FilterResult.Include:
|
||||
return TreeVisibility.Visible;
|
||||
default:
|
||||
return TreeVisibility.Recurse;
|
||||
}
|
||||
}
|
||||
|
||||
private testLocation(element: ITestTreeElement): FilterResult {
|
||||
if (!this._filterToUri || !this.state.currentDocumentOnly.value) {
|
||||
return FilterResult.Include;
|
||||
}
|
||||
|
||||
for (let e: ITestTreeElement | null = element; e; e = e!.parentItem) {
|
||||
if (!e.location) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return e.location.uri.toString() === this._filterToUri
|
||||
? FilterResult.Include
|
||||
: FilterResult.Exclude;
|
||||
}
|
||||
|
||||
return FilterResult.Inherit;
|
||||
}
|
||||
|
||||
private testFilterText(element: ITestTreeElement) {
|
||||
if (!this.filters) {
|
||||
return FilterResult.Include;
|
||||
}
|
||||
|
||||
// start as included if the first glob is a negation
|
||||
let included = this.filters[0][0] === false ? FilterResult.Exclude : FilterResult.Inherit;
|
||||
data = data.toLowerCase();
|
||||
for (let e: ITestTreeElement | null = element; e; e = e.parentItem) {
|
||||
// start as included if the first glob is a negation
|
||||
let included = this.filters[0][0] === false ? FilterResult.Exclude : FilterResult.Inherit;
|
||||
const data = e.label.toLowerCase();
|
||||
|
||||
for (const [include, filter] of this.filters) {
|
||||
if (data.includes(filter)) {
|
||||
included = include ? FilterResult.Include : FilterResult.Exclude;
|
||||
for (const [include, filter] of this.filters) {
|
||||
if (data.includes(filter)) {
|
||||
included = include ? FilterResult.Include : FilterResult.Exclude;
|
||||
}
|
||||
}
|
||||
|
||||
if (included !== FilterResult.Inherit) {
|
||||
return included;
|
||||
}
|
||||
}
|
||||
|
||||
return included;
|
||||
return FilterResult.Inherit;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -589,10 +628,19 @@ class ListAccessibilityProvider implements IListAccessibilityProvider<ITestTreeE
|
|||
}
|
||||
|
||||
getAriaLabel(element: ITestTreeElement): string {
|
||||
return localize({
|
||||
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]);
|
||||
|
||||
if (element.retired) {
|
||||
label = localize({
|
||||
key: 'testing.treeElementLabelOutdated',
|
||||
comment: ['{0} is the original label in testing.treeElementLabel'],
|
||||
}, '{0}, outdated result', label, testStateNames[element.state]);
|
||||
}
|
||||
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -669,6 +717,10 @@ class TestsRenderer implements ITreeRenderer<ITestTreeElement, FuzzyScore, TestT
|
|||
|
||||
const icon = testingStatesToIcons.get(element.state);
|
||||
data.icon.className = 'computed-state ' + (icon ? ThemeIcon.asClassName(icon) : '');
|
||||
if (element.retired) {
|
||||
data.icon.className += ' retired';
|
||||
}
|
||||
|
||||
const test = element.test;
|
||||
if (test) {
|
||||
if (test.item.location) {
|
||||
|
|
32
src/vs/workbench/contrib/testing/common/observableValue.ts
Normal file
32
src/vs/workbench/contrib/testing/common/observableValue.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { StoredValue } from 'vs/workbench/contrib/testing/common/storedValue';
|
||||
|
||||
export class ObservableValue<T> {
|
||||
private readonly changeEmitter = new Emitter<T>();
|
||||
|
||||
public readonly onDidChange = this.changeEmitter.event;
|
||||
|
||||
public get value() {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
public set value(v: T) {
|
||||
if (v !== this._value) {
|
||||
this._value = v;
|
||||
this.changeEmitter.fire(v);
|
||||
}
|
||||
}
|
||||
|
||||
public static stored<T>(stored: StoredValue<T>, defaultValue: T) {
|
||||
const o = new ObservableValue(stored.get(defaultValue));
|
||||
o.onDidChange(value => stored.store(value));
|
||||
return o;
|
||||
}
|
||||
|
||||
constructor(private _value: T) { }
|
||||
}
|
|
@ -93,6 +93,7 @@ const itemToNode = (
|
|||
item: { ...item.item },
|
||||
state: unsetState,
|
||||
computedState: TestRunState.Unset,
|
||||
retired: false,
|
||||
};
|
||||
|
||||
byExtId.set(n.item.extId, n);
|
||||
|
@ -154,6 +155,7 @@ interface ISerializedResults {
|
|||
interface TestResultItem extends IncrementalTestCollectionItem {
|
||||
state: ITestState;
|
||||
computedState: TestRunState;
|
||||
retired: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -300,6 +302,28 @@ export class LiveTestResult implements ITestResult {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks a test as retired. This can trigger it to be re-run in live mode.
|
||||
*/
|
||||
public retire(extId: string) {
|
||||
const root = this.testByExtId.get(extId);
|
||||
if (!root || root.retired) {
|
||||
return;
|
||||
}
|
||||
|
||||
const queue: Iterable<string>[] = [[root.id]];
|
||||
while (queue.length) {
|
||||
for (const id of queue.pop()!) {
|
||||
const entry = this.testByInternalId.get(id);
|
||||
if (entry && !entry.retired) {
|
||||
entry.retired = true;
|
||||
queue.push(entry.children);
|
||||
this.changeEmitter.fire(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a test, by its ID, to the test run. This can end up being called
|
||||
* if tests were started while discovery was still happening, so initially
|
||||
|
@ -363,6 +387,7 @@ class HydratedTestResult implements ITestResult {
|
|||
for (const [key, value] of serialized.items) {
|
||||
this.map.set(key, value);
|
||||
|
||||
value.retired = true;
|
||||
for (const message of value.state.messages) {
|
||||
if (message.location) {
|
||||
message.location.uri = URI.revive(message.location.uri);
|
||||
|
|
|
@ -4,16 +4,17 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { GettingStartedInputFactory, GettingStartedInput, getGettingStartedInput } from 'vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted';
|
||||
import { GettingStartedInputFactory, GettingStartedInput, GettingStartedPage } from 'vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Extensions as EditorInputExtensions, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor';
|
||||
import { MenuId, registerAction2, Action2 } from 'vs/platform/actions/common/actions';
|
||||
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { Extensions as ConfigurationExtensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration';
|
||||
import product from 'vs/platform/product/common/product';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ContextKeyEqualsExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { EditorDescriptor, IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/browser/editor';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
|
||||
export * as icons from 'vs/workbench/contrib/welcome/gettingStarted/browser/gettingStartedIcons';
|
||||
|
||||
|
@ -24,10 +25,8 @@ registerAction2(class extends Action2 {
|
|||
title: localize('Getting Started', "Getting Started"),
|
||||
category: localize('help', "Help"),
|
||||
f1: true,
|
||||
precondition: ContextKeyExpr.has('config.workbench.experimental.gettingStarted'),
|
||||
menu: {
|
||||
id: MenuId.MenubarHelpMenu,
|
||||
when: ContextKeyExpr.has('config.workbench.experimental.gettingStarted'),
|
||||
group: '1_welcome',
|
||||
order: 2,
|
||||
}
|
||||
|
@ -35,21 +34,94 @@ registerAction2(class extends Action2 {
|
|||
}
|
||||
|
||||
public run(accessor: ServicesAccessor) {
|
||||
accessor.get(IEditorService).openEditor(accessor.get(IInstantiationService).invokeFunction(getGettingStartedInput, {}), {});
|
||||
accessor.get(IEditorService).openEditor(new GettingStartedInput({}), {});
|
||||
}
|
||||
});
|
||||
|
||||
Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories).registerEditorInputFactory(GettingStartedInput.ID, GettingStartedInputFactory);
|
||||
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
|
||||
EditorDescriptor.create(
|
||||
GettingStartedPage,
|
||||
GettingStartedPage.ID,
|
||||
localize('gettingStarted', "Getting Started")
|
||||
),
|
||||
[
|
||||
new SyncDescriptor(GettingStartedInput)
|
||||
]
|
||||
);
|
||||
|
||||
if (product.quality !== 'stable') {
|
||||
Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).registerConfiguration({
|
||||
...workbenchConfigurationNodeBase,
|
||||
properties: {
|
||||
'workbench.experimental.gettingStarted': {
|
||||
type: 'boolean',
|
||||
description: localize('gettingStartedDescription', "Enables an experimental Getting Started page, available via the Help menu."),
|
||||
default: false,
|
||||
}
|
||||
const category = localize('gettingStarted', "Getting Started");
|
||||
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'gettingStarted.goBack',
|
||||
title: localize('gettingStarted.goBack', "Go Back"),
|
||||
category,
|
||||
keybinding: {
|
||||
weight: KeybindingWeight.EditorContrib,
|
||||
primary: KeyCode.Escape,
|
||||
},
|
||||
precondition: ContextKeyEqualsExpr.create('activeEditor', 'gettingStartedPage'),
|
||||
f1: true
|
||||
});
|
||||
}
|
||||
|
||||
run(accessor: ServicesAccessor) {
|
||||
const editorService = accessor.get(IEditorService);
|
||||
const editorPane = editorService.activeEditorPane;
|
||||
if (editorPane instanceof GettingStartedPage) {
|
||||
editorPane.escape();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'gettingStarted.next',
|
||||
title: localize('gettingStarted.goNext', "Next"),
|
||||
category,
|
||||
keybinding: {
|
||||
weight: KeybindingWeight.EditorContrib,
|
||||
primary: KeyCode.DownArrow,
|
||||
secondary: [KeyCode.RightArrow],
|
||||
},
|
||||
precondition: ContextKeyEqualsExpr.create('activeEditor', 'gettingStartedPage'),
|
||||
f1: true
|
||||
});
|
||||
}
|
||||
|
||||
run(accessor: ServicesAccessor) {
|
||||
const editorService = accessor.get(IEditorService);
|
||||
const editorPane = editorService.activeEditorPane;
|
||||
if (editorPane instanceof GettingStartedPage) {
|
||||
editorPane.focusNext();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'gettingStarted.prev',
|
||||
title: localize('gettingStarted.goPrev', "Previous"),
|
||||
category,
|
||||
keybinding: {
|
||||
weight: KeybindingWeight.EditorContrib,
|
||||
primary: KeyCode.UpArrow,
|
||||
secondary: [KeyCode.LeftArrow],
|
||||
},
|
||||
precondition: ContextKeyEqualsExpr.create('activeEditor', 'gettingStartedPage'),
|
||||
f1: true
|
||||
});
|
||||
}
|
||||
|
||||
run(accessor: ServicesAccessor) {
|
||||
const editorService = accessor.get(IEditorService);
|
||||
const editorPane = editorService.activeEditorPane;
|
||||
if (editorPane instanceof GettingStartedPage) {
|
||||
editorPane.focusPrevious();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -3,12 +3,29 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.file-icons-enabled .show-file-icons .vs_code_editor_getting_started\.md-name-file-icon.md-ext-file-icon.ext-file-icon.markdown-lang-file-icon.file-icon::before {
|
||||
.file-icons-enabled .show-file-icons .vscode_getting_started_page-name-file-icon.file-icon::before {
|
||||
content: ' ';
|
||||
background-image: url('../../../../browser/media/code-icon.svg');
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer {
|
||||
box-sizing: border-box;
|
||||
padding: 10px 20px;
|
||||
line-height: 22px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
height: inherit;
|
||||
width: 100%;
|
||||
user-select: initial;
|
||||
-webkit-user-select: initial;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
|
@ -16,53 +33,54 @@
|
|||
top: 0;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer.animationReady .gettingStartedSlide {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer.animationReady .gettingStartedSlide {
|
||||
/* keep consistant with SLIDE_TRANSITION_TIME_MS in gettingStarted.ts */
|
||||
transition: left 0.25s, opacity 0.25s;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.categories {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.categories {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.categories .gap {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.categories .gap {
|
||||
flex: 150px 0 1000
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.categories .header {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.categories .header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.categories .category-title {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.categories .category-title {
|
||||
margin-bottom: 4px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.categories .category-description-container {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.categories .category-description-container {
|
||||
width: 100%
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.categories .category-progress {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.categories .category-progress {
|
||||
margin-top: 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.categories .progress-bar-outer {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.categories .progress-bar-outer {
|
||||
height: 8px;
|
||||
border-radius: 4px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.categories .progress-bar-inner {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.categories .progress-bar-inner {
|
||||
height: 100%;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.categories .getting-started-categories-container {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.categories .getting-started-categories-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
|
@ -70,12 +88,12 @@
|
|||
margin: 32px auto;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.categories .getting-started-categories-scrolling-container {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.categories .getting-started-categories-scrolling-container {
|
||||
overflow: scroll;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide .getting-started-category {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide .getting-started-category {
|
||||
width: 330px;
|
||||
min-height: 80px;
|
||||
font-size: 13px;
|
||||
|
@ -85,26 +103,26 @@
|
|||
display: flex;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.categories .getting-started-category {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.categories .getting-started-category {
|
||||
padding-right: 46px;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide .getting-started-category .codicon {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide .getting-started-category .codicon {
|
||||
margin-right: 10px;
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.detail {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .gap {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.detail .gap {
|
||||
flex: 150px 0 1000
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-category {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.detail .getting-started-category {
|
||||
width: 330px;
|
||||
display: flex;
|
||||
padding: 10px 0 20px 12px;
|
||||
|
@ -112,23 +130,23 @@
|
|||
min-height: auto;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-detail-columns .gap {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.detail .getting-started-detail-columns .gap {
|
||||
flex: 150px 1 1000
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-category .codicon {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.detail .getting-started-category .codicon {
|
||||
margin-left:0;
|
||||
font-size: 22pt;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-detail-columns {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.detail .getting-started-detail-columns {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
padding: 40px 40px 0;
|
||||
max-height: calc(100% - 40px);
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
|
@ -136,56 +154,57 @@
|
|||
max-height: 180px;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task:not(.expanded) {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task:not(.expanded) {
|
||||
max-height: 54px;
|
||||
background: none;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-detail-columns .getting-started-detail-left > div {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.detail .getting-started-detail-columns .getting-started-detail-left > div {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task:not(.expanded) .task-description,
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task:not(.expanded) .actions,
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task:not(.expanded) button,
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task:not(.expanded) a {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task:not(.expanded) .task-description,
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task:not(.expanded) .image-description,
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task:not(.expanded) .actions,
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task:not(.expanded) button,
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task:not(.expanded) a {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task .task-description {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task .task-description {
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task .actions {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task .actions {
|
||||
margin-top: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task .task-next {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task .task-next {
|
||||
margin-left: auto;
|
||||
margin-right: 10px;
|
||||
padding: 6px 12px;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task .codicon.hidden {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task .codicon.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task .codicon {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task .codicon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task-action {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task-action {
|
||||
padding: 6px 12px;
|
||||
font-size: 13px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-detail-left {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.detail .getting-started-detail-left {
|
||||
min-width: 330px;
|
||||
width: 40%;
|
||||
max-width: 400px;
|
||||
|
@ -193,21 +212,21 @@
|
|||
flex-direction: column;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .full-height-scrollable {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.detail .full-height-scrollable {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-detail-container {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.detail .getting-started-detail-container {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .gettingStartedDetailsContent {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.detail .gettingStartedDetailsContent {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-detail-right {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.detail .getting-started-detail-right {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
|
@ -218,11 +237,11 @@
|
|||
max-width: 800px;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-detail-right img {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.detail .getting-started-detail-right img {
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer button {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer button {
|
||||
border: none;
|
||||
color: inherit;
|
||||
text-align: left;
|
||||
|
@ -230,15 +249,15 @@
|
|||
margin: 1px 0; /* makes room for focus border */
|
||||
font-family: inherit;
|
||||
}
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer button:hover {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer button:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer button:focus {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer button:focus {
|
||||
outline-style: solid;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .prev-button.button-link {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .prev-button.button-link {
|
||||
position: absolute;
|
||||
left: 40px;
|
||||
top: 5px;
|
||||
|
@ -247,24 +266,24 @@
|
|||
z-index: 1;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .prev-button:hover {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .prev-button:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .prev-button .codicon {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .prev-button .codicon {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
left: -4px;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide .skip {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide .skip {
|
||||
display: block;
|
||||
margin: 2px auto;
|
||||
width: fit-content;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide h1 {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide h1 {
|
||||
font-size: 32px;
|
||||
font-weight: normal;
|
||||
border-bottom: none;
|
||||
|
@ -272,42 +291,42 @@
|
|||
padding: 0;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide h2 {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide h2 {
|
||||
font-weight: normal;
|
||||
line-height: 26px;
|
||||
margin: 0 0 4px 0;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide h3 {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide h3 {
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide .subtitle {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide .subtitle {
|
||||
font-size: 16px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.next {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStarted.showCategories .detail {
|
||||
left: 100%;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.prev {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStarted.showDetails .categories {
|
||||
left: -100%;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .button-link {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .button-link {
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
margin: 2px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .button-link:hover {
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer .button-link:hover {
|
||||
text-decoration: underline;
|
||||
background: transparent;
|
||||
}
|
||||
|
|
|
@ -4,15 +4,12 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./gettingStarted';
|
||||
import 'vs/workbench/contrib/welcome/gettingStarted/browser/vs_code_editor_getting_started';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { WalkThroughInput, WalkThroughInputOptions } from 'vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput';
|
||||
import { FileAccess, Schemas } from 'vs/base/common/network';
|
||||
import { IEditorInputFactory } from 'vs/workbench/common/editor';
|
||||
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { EditorInput, EditorOptions, IEditorInputFactory, IEditorOpenContext } from 'vs/workbench/common/editor';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
import { $, addDisposableListener } from 'vs/base/browser/dom';
|
||||
import { $, addDisposableListener, reset } from 'vs/base/browser/dom';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { IGettingStartedCategoryWithProgress, IGettingStartedService } from 'vs/workbench/services/gettingStarted/common/gettingStartedService';
|
||||
|
@ -23,62 +20,56 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
|||
import { 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 { ServicesAccessor } from 'vs/editor/browser/editorExtensions';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
|
||||
const SLIDE_TRANSITION_TIME_MS = 250;
|
||||
|
||||
export const gettingStartedInputTypeId = 'workbench.editors.gettingStartedInput';
|
||||
const telemetryFrom = 'gettingStartedPage';
|
||||
|
||||
export class GettingStartedInput extends WalkThroughInput {
|
||||
export class GettingStartedInput extends EditorInput {
|
||||
|
||||
get resource(): URI | undefined {
|
||||
return URI.from({ scheme: Schemas.walkThrough, authority: 'vscode_getting_started_page' });
|
||||
}
|
||||
getTypeId(): string {
|
||||
return GettingStartedInput.ID;
|
||||
}
|
||||
|
||||
matches(other: unknown) {
|
||||
if (other instanceof GettingStartedInput) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static readonly ID = gettingStartedInputTypeId;
|
||||
|
||||
constructor(
|
||||
options: WalkThroughInputOptions & { selectedCategory?: string, selectedTask?: string },
|
||||
@ITextModelService textModelResolverService: ITextModelService
|
||||
options: { selectedCategory?: string, selectedTask?: string }
|
||||
) {
|
||||
super(options, textModelResolverService);
|
||||
super();
|
||||
this.selectedCategory = options.selectedCategory;
|
||||
this.selectedTask = options.selectedTask;
|
||||
}
|
||||
|
||||
getName() {
|
||||
return localize('gettingStarted', "Getting Started");
|
||||
}
|
||||
|
||||
selectedCategory: string | undefined;
|
||||
selectedTask: string | undefined;
|
||||
}
|
||||
|
||||
export function getGettingStartedInput(accessor: ServicesAccessor, options: { selectedCategory?: string, selectedTask?: string }) {
|
||||
const resource = FileAccess.asBrowserUri('./vs_code_editor_getting_started.md', require)
|
||||
.with({
|
||||
scheme: Schemas.walkThrough,
|
||||
query: JSON.stringify({ moduleId: 'vs/workbench/contrib/welcome/gettingStarted/browser/vs_code_editor_getting_started' })
|
||||
});
|
||||
export class GettingStartedPage extends EditorPane {
|
||||
|
||||
const instantiationService = accessor.get(IInstantiationService);
|
||||
public static ID = 'gettingStartedPage';
|
||||
|
||||
const pages: GettingStartedPage[] = [];
|
||||
|
||||
const editorInput = instantiationService.createInstance(GettingStartedInput, {
|
||||
typeId: gettingStartedInputTypeId,
|
||||
name: localize('editorGettingStarted.title', "Getting Started"),
|
||||
resource,
|
||||
telemetryFrom,
|
||||
selectedCategory: options.selectedCategory,
|
||||
selectedTask: options.selectedTask,
|
||||
onReady: (container: HTMLElement, disposableStore: DisposableStore) => {
|
||||
const page = instantiationService.createInstance(GettingStartedPage, editorInput);
|
||||
page.onReady(container);
|
||||
pages.push(page);
|
||||
disposableStore.add(page);
|
||||
},
|
||||
layout: () => pages.forEach(page => page.layout()),
|
||||
});
|
||||
|
||||
return editorInput;
|
||||
}
|
||||
|
||||
export class GettingStartedPage extends Disposable {
|
||||
readonly editorInput: GettingStartedInput;
|
||||
private editorInput!: GettingStartedInput;
|
||||
private inProgressScroll = Promise.resolve();
|
||||
|
||||
private dispatchListeners: DisposableStore = new DisposableStore();
|
||||
|
@ -91,19 +82,22 @@ export class GettingStartedPage extends Disposable {
|
|||
private detailsScrollbar: DomScrollableElement | undefined;
|
||||
private detailImageScrollbar: DomScrollableElement | undefined;
|
||||
|
||||
private container: HTMLElement;
|
||||
|
||||
constructor(
|
||||
editorInput: GettingStartedInput,
|
||||
@ICommandService private readonly commandService: ICommandService,
|
||||
@IProductService private readonly productService: IProductService,
|
||||
@IKeybindingService private readonly keybindingService: IKeybindingService,
|
||||
@IGettingStartedService private readonly gettingStartedService: IGettingStartedService,
|
||||
@ITelemetryService private readonly telemetryService: ITelemetryService,
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IOpenerService private readonly openerService: IOpenerService,
|
||||
@IThemeService private readonly themeService: IThemeService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
) {
|
||||
super();
|
||||
|
||||
this.editorInput = editorInput;
|
||||
super(GettingStartedPage.ID, telemetryService, themeService, storageService);
|
||||
|
||||
this.container = $('.gettingStartedContainer');
|
||||
|
||||
this.gettingStartedCategories = this.gettingStartedService.getCategories();
|
||||
this._register(this.dispatchListeners);
|
||||
|
@ -135,10 +129,18 @@ export class GettingStartedPage extends Disposable {
|
|||
}));
|
||||
}
|
||||
|
||||
private registerDispatchListeners(container: HTMLElement) {
|
||||
async setInput(newInput: GettingStartedInput, options: EditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken) {
|
||||
this.container.classList.remove('animationReady');
|
||||
this.editorInput = newInput;
|
||||
await super.setInput(newInput, options, context, token);
|
||||
this.buildCategoriesSlide();
|
||||
setTimeout(() => this.container.classList.add('animationReady'), 0);
|
||||
}
|
||||
|
||||
private registerDispatchListeners() {
|
||||
this.dispatchListeners.clear();
|
||||
|
||||
container.querySelectorAll('[x-dispatch]').forEach(element => {
|
||||
this.container.querySelectorAll('[x-dispatch]').forEach(element => {
|
||||
const [command, argument] = (element.getAttribute('x-dispatch') ?? '').split(':');
|
||||
if (command) {
|
||||
this.dispatchListeners.add(addDisposableListener(element, 'click', (e) => {
|
||||
|
@ -157,11 +159,11 @@ export class GettingStartedPage extends Disposable {
|
|||
|
||||
switch (command) {
|
||||
case 'scrollPrev': {
|
||||
this.scrollPrev(container);
|
||||
this.scrollPrev();
|
||||
break;
|
||||
}
|
||||
case 'skip': {
|
||||
this.commandService.executeCommand('workbench.action.closeActiveEditor');
|
||||
this.runSkip();
|
||||
break;
|
||||
}
|
||||
case 'selectCategory': {
|
||||
|
@ -170,12 +172,12 @@ export class GettingStartedPage extends Disposable {
|
|||
if (selectedCategory.content.type === 'command') {
|
||||
this.commandService.executeCommand(selectedCategory.content.command);
|
||||
} else {
|
||||
this.scrollToCategory(container, argument);
|
||||
this.scrollToCategory(argument);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'selectTask': {
|
||||
this.selectTask(container, argument);
|
||||
this.selectTask(argument);
|
||||
e.stopPropagation();
|
||||
break;
|
||||
}
|
||||
|
@ -205,13 +207,16 @@ export class GettingStartedPage extends Disposable {
|
|||
});
|
||||
}
|
||||
|
||||
private selectTask(container: HTMLElement, id: string | undefined, contractIfAlreadySelected = true) {
|
||||
const mediaElement = assertIsDefined(container.querySelector('.getting-started-media') as HTMLImageElement);
|
||||
private selectTask(id: string | undefined, contractIfAlreadySelected = true, delayFocus = true) {
|
||||
const mediaElement = assertIsDefined(this.container.querySelector('.getting-started-media') as HTMLImageElement);
|
||||
this.taskDisposables.clear();
|
||||
if (id) {
|
||||
const taskElement = assertIsDefined(container.querySelector(`[data-task-id="${id}"]`));
|
||||
taskElement.parentElement?.querySelectorAll('.expanded').forEach(node => node.classList.remove('expanded'));
|
||||
(taskElement as HTMLDivElement).focus();
|
||||
const taskElement = assertIsDefined(this.container.querySelector(`[data-task-id="${id}"]`));
|
||||
taskElement.parentElement?.querySelectorAll('.expanded').forEach(node => {
|
||||
node.classList.remove('expanded');
|
||||
node.setAttribute('aria-expanded', 'false');
|
||||
});
|
||||
setTimeout(() => (taskElement as HTMLDivElement).focus(), delayFocus ? SLIDE_TRANSITION_TIME_MS : 0);
|
||||
if (this.editorInput.selectedTask === id && contractIfAlreadySelected) {
|
||||
this.editorInput.selectedTask = undefined;
|
||||
return;
|
||||
|
@ -228,6 +233,7 @@ export class GettingStartedPage extends Disposable {
|
|||
this.taskDisposables.add(addDisposableListener(mediaElement, 'click', () => taskElement.querySelector('button')?.click()));
|
||||
this.taskDisposables.add(this.themeService.onDidColorThemeChange(() => this.updateMediaSourceForColorMode(mediaElement, taskToExpand.media.path)));
|
||||
taskElement.classList.add('expanded');
|
||||
taskElement.setAttribute('aria-expanded', 'true');
|
||||
} else {
|
||||
this.editorInput.selectedTask = undefined;
|
||||
mediaElement.setAttribute('src', '');
|
||||
|
@ -242,14 +248,51 @@ export class GettingStartedPage extends Disposable {
|
|||
element.src = sources[themeType].toString();
|
||||
}
|
||||
|
||||
onReady(container: HTMLElement) {
|
||||
createEditor(parent: HTMLElement) {
|
||||
const tasksContent =
|
||||
$('.gettingStartedDetailsContent', {},
|
||||
$('.gap'),
|
||||
$('.getting-started-detail-columns', {},
|
||||
$('.gap'),
|
||||
$('.getting-started-detail-left', {},
|
||||
$('.getting-started-detail-title')),
|
||||
$('.getting-started-detail-right', {},
|
||||
$('img.getting-started-media')),
|
||||
$('.gap'),
|
||||
),
|
||||
$('.gap')
|
||||
);
|
||||
|
||||
const tasksSlide =
|
||||
$('.gettingStartedSlideDetails.gettingStartedSlide.detail', {},
|
||||
$('button.prev-button.button-link', { 'x-dispatch': 'scrollPrev' }, $('span.scroll-button.codicon.codicon-chevron-left'), localize('back', "Back")),
|
||||
tasksContent
|
||||
);
|
||||
|
||||
const gettingStartedPage =
|
||||
$('.gettingStarted', { role: 'document' },
|
||||
$('.gettingStartedSlideCategory.gettingStartedSlide.categories'),
|
||||
tasksSlide
|
||||
);
|
||||
|
||||
|
||||
if (this.detailImageScrollbar) { this.detailImageScrollbar.dispose(); }
|
||||
this.detailImageScrollbar = this._register(new DomScrollableElement(tasksContent, { className: 'full-height-scrollable' }));
|
||||
tasksSlide.appendChild(this.detailImageScrollbar.getDomNode());
|
||||
this.detailImageScrollbar.scanDomNode();
|
||||
|
||||
this.container.appendChild(gettingStartedPage);
|
||||
parent.appendChild(this.container);
|
||||
}
|
||||
|
||||
buildCategoriesSlide() {
|
||||
const categoryElements = this.gettingStartedCategories.map(
|
||||
category => {
|
||||
const categoryDescriptionElement =
|
||||
category.content.type === 'items' ?
|
||||
$('.category-description-container', {},
|
||||
$('h3.category-title', {}, category.title),
|
||||
$('.category-description.description', {}, category.description),
|
||||
$('.category-description.description', { 'aria-label': category.description + ' ' + localize('pressEnterToSelect', "Press Enter to Select") }, category.description),
|
||||
$('.category-progress', { 'x-data-category-id': category.id, },
|
||||
$('.message'),
|
||||
$('.progress-bar-outer', {},
|
||||
|
@ -257,26 +300,13 @@ export class GettingStartedPage extends Disposable {
|
|||
:
|
||||
$('.category-description-container', {},
|
||||
$('h3.category-title', {}, category.title),
|
||||
$('.category-description.description', {}, category.description));
|
||||
$('.category-description.description', { 'aria-label': category.description + ' ' + localize('pressEnterToSelect', "Press Enter to Select") }, category.description));
|
||||
|
||||
return $('button.getting-started-category',
|
||||
{ 'x-dispatch': 'selectCategory:' + category.id },
|
||||
{ 'x-dispatch': 'selectCategory:' + category.id, },
|
||||
$(ThemeIcon.asCSSSelector(category.icon), {}), categoryDescriptionElement);
|
||||
});
|
||||
|
||||
const categoriesSlide = assertIsDefined(container.querySelector('.gettingStartedSlideCategory'));
|
||||
const tasksSlide = assertIsDefined(container.querySelector('.gettingStartedSlideDetails'));
|
||||
|
||||
const tasksContent = assertIsDefined(container.querySelector('.gettingStartedDetailsContent') as HTMLElement);
|
||||
tasksContent.remove();
|
||||
if (this.detailImageScrollbar) { this.detailImageScrollbar.dispose(); }
|
||||
this.detailImageScrollbar = this._register(new DomScrollableElement(tasksContent, { className: 'full-height-scrollable' }));
|
||||
tasksSlide.appendChild(this.detailImageScrollbar.getDomNode());
|
||||
this.detailImageScrollbar.scanDomNode();
|
||||
|
||||
const rightColumn = assertIsDefined(container.querySelector('.getting-started-detail-right'));
|
||||
rightColumn.appendChild($('img.getting-started-media'));
|
||||
|
||||
const categoryScrollContainer = $('.getting-started-categories-scrolling-container');
|
||||
const categoriesContainer = $('.getting-started-categories-container');
|
||||
categoryElements.forEach(element => {
|
||||
|
@ -288,14 +318,22 @@ export class GettingStartedPage extends Disposable {
|
|||
|
||||
if (this.categoriesScrollbar) { this.categoriesScrollbar.dispose(); }
|
||||
this.categoriesScrollbar = this._register(new DomScrollableElement(categoryScrollContainer, {}));
|
||||
categoriesSlide.appendChild(this.categoriesScrollbar.getDomNode());
|
||||
categoriesSlide.appendChild($('.gap'));
|
||||
const categoriesSlide = assertIsDefined(this.container.querySelector('.gettingStartedSlideCategory') as HTMLElement);
|
||||
reset(categoriesSlide,
|
||||
$('.gap'),
|
||||
$('.header', {},
|
||||
$('h1.product-name.caption', {}, localize('gettingStarted.vscode', "Visual Studio Code")),
|
||||
$('p.subtitle.description', {}, localize({ key: 'gettingStarted.editingRedefined', comment: ['Shown as subtitle on the Welcome page.'] }, "Code editing. Redefined")),
|
||||
),
|
||||
this.categoriesScrollbar.getDomNode(),
|
||||
$('.gap')
|
||||
);
|
||||
this.categoriesScrollbar.scanDomNode();
|
||||
|
||||
this.updateCategoryProgress();
|
||||
|
||||
assertIsDefined(container.querySelector('.product-name')).textContent = this.productService.nameLong;
|
||||
this.registerDispatchListeners(container);
|
||||
assertIsDefined(this.container.querySelector('.product-name')).textContent = this.productService.nameLong;
|
||||
this.registerDispatchListeners();
|
||||
|
||||
|
||||
if (this.editorInput.selectedCategory) {
|
||||
|
@ -303,15 +341,12 @@ export class GettingStartedPage extends Disposable {
|
|||
if (!this.currentCategory) {
|
||||
throw Error('Could not restore to category ' + this.editorInput.selectedCategory + ' as it was not found');
|
||||
}
|
||||
this.buildCategorySlide(container, this.editorInput.selectedCategory, this.editorInput.selectedTask);
|
||||
categoriesSlide.classList.add('prev');
|
||||
this.setButtonEnablement(container, 'details');
|
||||
this.setSlide('details');
|
||||
this.buildCategorySlide(this.editorInput.selectedCategory, this.editorInput.selectedTask);
|
||||
} else {
|
||||
tasksSlide.classList.add('next');
|
||||
this.focusFirstUncompletedCategory(container);
|
||||
this.setButtonEnablement(container, 'categories');
|
||||
this.focusFirstUncompletedCategory();
|
||||
this.setSlide('categories');
|
||||
}
|
||||
setTimeout(() => assertIsDefined(container.querySelector('.gettingStartedContainer')).classList.add('animationReady'), 0);
|
||||
}
|
||||
|
||||
layout() {
|
||||
|
@ -342,29 +377,26 @@ export class GettingStartedPage extends Disposable {
|
|||
});
|
||||
}
|
||||
|
||||
private async scrollToCategory(container: HTMLElement, categoryID: string) {
|
||||
private async scrollToCategory(categoryID: string) {
|
||||
this.inProgressScroll = this.inProgressScroll.then(async () => {
|
||||
this.clearDetialView(container);
|
||||
this.clearDetialView();
|
||||
this.editorInput.selectedCategory = categoryID;
|
||||
this.currentCategory = this.gettingStartedCategories.find(category => category.id === categoryID);
|
||||
const slides = [...container.querySelectorAll('.gettingStartedSlide').values()];
|
||||
const currentSlide = slides.findIndex(element => !element.classList.contains('prev') && !element.classList.contains('next'));
|
||||
if (currentSlide < slides.length - 1) {
|
||||
this.buildCategorySlide(container, categoryID);
|
||||
slides[currentSlide].classList.add('prev');
|
||||
slides[currentSlide + 1].classList.remove('next');
|
||||
this.setButtonEnablement(container, 'details');
|
||||
}
|
||||
this.buildCategorySlide(categoryID);
|
||||
this.setSlide('details');
|
||||
});
|
||||
}
|
||||
|
||||
private buildCategorySlide(container: HTMLElement, categoryID: string, selectedItem?: string) {
|
||||
private buildCategorySlide(categoryID: string, selectedItem?: string) {
|
||||
const category = this.gettingStartedCategories.find(category => category.id === categoryID);
|
||||
if (!category) { throw Error('could not find category with ID ' + categoryID); }
|
||||
if (category.content.type !== 'items') { throw Error('category with ID ' + categoryID + ' is not of items type'); }
|
||||
|
||||
const leftColumn = assertIsDefined(container.querySelector('.getting-started-detail-left'));
|
||||
const detailTitle = assertIsDefined(container.querySelector('.getting-started-detail-title'));
|
||||
const leftColumn = assertIsDefined(this.container.querySelector('.getting-started-detail-left'));
|
||||
const detailTitle = assertIsDefined(this.container.querySelector('.getting-started-detail-title'));
|
||||
const oldTitle = detailTitle.querySelector('.getting-started-category');
|
||||
if (oldTitle) { detailTitle.removeChild(oldTitle); }
|
||||
|
||||
detailTitle.appendChild(
|
||||
$('.getting-started-category',
|
||||
{},
|
||||
|
@ -375,11 +407,16 @@ export class GettingStartedPage extends Disposable {
|
|||
|
||||
const categoryElements = category.content.items.map(
|
||||
(task, i, arr) => $('button.getting-started-task',
|
||||
{ 'x-dispatch': 'selectTask:' + task.id, 'data-task-id': task.id },
|
||||
{
|
||||
'x-dispatch': 'selectTask:' + task.id,
|
||||
'data-task-id': task.id,
|
||||
'aria-expanded': 'false',
|
||||
},
|
||||
$('.codicon' + (task.done ? '.complete.codicon-pass-filled' : '.codicon-circle-large-outline'), { 'data-done-task-id': task.id }),
|
||||
$('.task-description-container', {},
|
||||
$('h3.task-title', {}, task.title),
|
||||
$('.task-description.description', {}, task.description),
|
||||
$('.image-description', { 'aria-label': localize('imageShowing', "Image showing {0}", task.media.altText) }),
|
||||
$('.actions', {},
|
||||
...(
|
||||
task.button
|
||||
|
@ -403,15 +440,15 @@ export class GettingStartedPage extends Disposable {
|
|||
leftColumn.appendChild(this.detailsScrollbar.getDomNode());
|
||||
|
||||
const toExpand = category.content.items.find(item => !item.done) ?? category.content.items[0];
|
||||
this.selectTask(container, selectedItem ?? toExpand.id, false);
|
||||
this.selectTask(selectedItem ?? toExpand.id, false);
|
||||
this.detailsScrollbar.scanDomNode();
|
||||
this.registerDispatchListeners(container);
|
||||
this.registerDispatchListeners();
|
||||
}
|
||||
|
||||
private clearDetialView(container: HTMLElement) {
|
||||
const detailContainer = (container.querySelector('.getting-started-detail-container'));
|
||||
private clearDetialView() {
|
||||
const detailContainer = (this.container.querySelector('.getting-started-detail-container'));
|
||||
detailContainer?.remove();
|
||||
const detailTitle = assertIsDefined(container.querySelector('.getting-started-detail-title'));
|
||||
const detailTitle = assertIsDefined(this.container.querySelector('.getting-started-detail-title'));
|
||||
while (detailTitle.firstChild) { detailTitle.removeChild(detailTitle.firstChild); }
|
||||
}
|
||||
|
||||
|
@ -421,40 +458,74 @@ export class GettingStartedPage extends Disposable {
|
|||
else { return ` (${binding.getLabel()})`; }
|
||||
}
|
||||
|
||||
private async scrollPrev(container: HTMLElement) {
|
||||
private async scrollPrev() {
|
||||
this.inProgressScroll = this.inProgressScroll.then(async () => {
|
||||
this.currentCategory = undefined;
|
||||
this.editorInput.selectedCategory = undefined;
|
||||
this.editorInput.selectedTask = undefined;
|
||||
this.selectTask(container, undefined);
|
||||
const slides = [...container.querySelectorAll('.gettingStartedSlide').values()];
|
||||
const currentSlide = slides.findIndex(element =>
|
||||
!element.classList.contains('prev') && !element.classList.contains('next'));
|
||||
if (currentSlide > 0) {
|
||||
slides[currentSlide].classList.add('next');
|
||||
assertIsDefined(slides[currentSlide - 1]).classList.remove('prev');
|
||||
this.setButtonEnablement(container, 'categories');
|
||||
}
|
||||
this.focusFirstUncompletedCategory(container);
|
||||
this.selectTask(undefined);
|
||||
this.setSlide('categories');
|
||||
this.focusFirstUncompletedCategory();
|
||||
});
|
||||
}
|
||||
|
||||
private focusFirstUncompletedCategory(container: HTMLElement) {
|
||||
private runSkip() {
|
||||
this.commandService.executeCommand('workbench.action.closeActiveEditor');
|
||||
}
|
||||
|
||||
escape() {
|
||||
if (this.editorInput.selectedCategory) {
|
||||
this.scrollPrev();
|
||||
} else {
|
||||
this.runSkip();
|
||||
}
|
||||
}
|
||||
|
||||
focusNext() {
|
||||
if (this.editorInput.selectedCategory) {
|
||||
const allTasks = this.currentCategory?.content.type === 'items' && this.currentCategory.content.items;
|
||||
if (allTasks) {
|
||||
const selectedIndex = allTasks.findIndex(task => task.id === this.editorInput.selectedTask);
|
||||
if (allTasks[selectedIndex + 1]?.id) { this.selectTask(allTasks[selectedIndex + 1]?.id, true, false); }
|
||||
}
|
||||
} else {
|
||||
(document.activeElement?.nextElementSibling as HTMLElement)?.focus?.();
|
||||
}
|
||||
}
|
||||
|
||||
focusPrevious() {
|
||||
if (this.editorInput.selectedCategory) {
|
||||
const allTasks = this.currentCategory?.content.type === 'items' && this.currentCategory.content.items;
|
||||
if (allTasks) {
|
||||
const selectedIndex = allTasks.findIndex(task => task.id === this.editorInput.selectedTask);
|
||||
if (allTasks[selectedIndex - 1]?.id) { this.selectTask(allTasks[selectedIndex - 1]?.id, true, false); }
|
||||
}
|
||||
} else {
|
||||
(document.activeElement?.previousElementSibling as HTMLElement)?.focus?.();
|
||||
}
|
||||
}
|
||||
|
||||
private focusFirstUncompletedCategory() {
|
||||
let toFocus!: HTMLElement;
|
||||
container.querySelectorAll('.category-progress').forEach(progress => {
|
||||
this.container.querySelectorAll('.category-progress').forEach(progress => {
|
||||
const progressAmount = assertIsDefined(progress.querySelector('.progress-bar-inner') as HTMLDivElement).style.width;
|
||||
if (!toFocus && progressAmount !== '100%') { toFocus = assertIsDefined(progress.parentElement?.parentElement); }
|
||||
});
|
||||
(toFocus ?? assertIsDefined(container.querySelector('button.skip')) as HTMLButtonElement).focus();
|
||||
(toFocus ?? assertIsDefined(this.container.querySelector('button.skip')) as HTMLButtonElement).focus();
|
||||
}
|
||||
|
||||
private setButtonEnablement(container: HTMLElement, toEnable: 'details' | 'categories') {
|
||||
private setSlide(toEnable: 'details' | 'categories') {
|
||||
const slideManager = assertIsDefined(this.container.querySelector('.gettingStarted'));
|
||||
if (toEnable === 'categories') {
|
||||
container.querySelector('.gettingStartedSlideDetails')!.querySelectorAll('button').forEach(button => button.disabled = true);
|
||||
container.querySelector('.gettingStartedSlideCategory')!.querySelectorAll('button').forEach(button => button.disabled = false);
|
||||
slideManager.classList.remove('showDetails');
|
||||
slideManager.classList.add('showCategories');
|
||||
this.container.querySelector('.gettingStartedSlideDetails')!.querySelectorAll('button').forEach(button => button.disabled = true);
|
||||
this.container.querySelector('.gettingStartedSlideCategory')!.querySelectorAll('button').forEach(button => button.disabled = false);
|
||||
} else {
|
||||
container.querySelector('.gettingStartedSlideDetails')!.querySelectorAll('button').forEach(button => button.disabled = false);
|
||||
container.querySelector('.gettingStartedSlideCategory')!.querySelectorAll('button').forEach(button => button.disabled = true);
|
||||
slideManager.classList.add('showDetails');
|
||||
slideManager.classList.remove('showCategories');
|
||||
this.container.querySelector('.gettingStartedSlideDetails')!.querySelectorAll('button').forEach(button => button.disabled = false);
|
||||
this.container.querySelector('.gettingStartedSlideCategory')!.querySelectorAll('button').forEach(button => button.disabled = true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -471,9 +542,9 @@ export class GettingStartedInputFactory implements IEditorInputFactory {
|
|||
public deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): GettingStartedInput {
|
||||
try {
|
||||
const { selectedCategory, selectedTask } = JSON.parse(serializedEditorInput);
|
||||
return instantiationService.invokeFunction(getGettingStartedInput, { selectedCategory, selectedTask });
|
||||
return new GettingStartedInput({ selectedCategory, selectedTask });
|
||||
} catch { }
|
||||
return instantiationService.invokeFunction(getGettingStartedInput, {});
|
||||
return new GettingStartedInput({});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -486,85 +557,85 @@ registerThemingParticipant((theme, collector) => {
|
|||
|
||||
const foregroundColor = theme.getColor(foreground);
|
||||
if (foregroundColor) {
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer { color: ${foregroundColor}; }`);
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .gettingStartedContainer { color: ${foregroundColor}; }`);
|
||||
}
|
||||
|
||||
const descriptionColor = theme.getColor(descriptionForeground);
|
||||
if (descriptionColor) {
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .description { color: ${descriptionColor}; }`);
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .category-progress .message { color: ${descriptionColor}; }`);
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .gettingStartedContainer .description { color: ${descriptionColor}; }`);
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .gettingStartedContainer .category-progress .message { color: ${descriptionColor}; }`);
|
||||
}
|
||||
|
||||
const iconColor = theme.getColor(textLinkForeground);
|
||||
if (iconColor) {
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .getting-started-category .codicon { color: ${iconColor} }`);
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task .codicon.complete { color: ${iconColor} } `);
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task.expanded .codicon { color: ${iconColor} } `);
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .gettingStartedContainer .getting-started-category .codicon { color: ${iconColor} }`);
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task .codicon.complete { color: ${iconColor} } `);
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task.expanded .codicon { color: ${iconColor} } `);
|
||||
}
|
||||
|
||||
const buttonColor = theme.getColor(welcomePageTileBackground);
|
||||
if (buttonColor) {
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer button { background: ${buttonColor}; }`);
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .gettingStartedContainer button { background: ${buttonColor}; }`);
|
||||
}
|
||||
|
||||
const buttonHoverColor = theme.getColor(welcomePageTileHoverBackground);
|
||||
if (buttonHoverColor) {
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer button:hover { background: ${buttonHoverColor}; }`);
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .gettingStartedContainer button:hover { background: ${buttonHoverColor}; }`);
|
||||
}
|
||||
if (buttonColor && buttonHoverColor) {
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer button.expanded:hover { background: ${buttonColor}; }`);
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .gettingStartedContainer button.expanded:hover { background: ${buttonColor}; }`);
|
||||
}
|
||||
|
||||
const emphasisButtonForeground = theme.getColor(buttonForeground);
|
||||
if (emphasisButtonForeground) {
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer button.emphasis { color: ${emphasisButtonForeground}; }`);
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .gettingStartedContainer button.emphasis { color: ${emphasisButtonForeground}; }`);
|
||||
}
|
||||
|
||||
const emphasisButtonBackground = theme.getColor(buttonBackground);
|
||||
if (emphasisButtonBackground) {
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer button.emphasis { background: ${emphasisButtonBackground}; }`);
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .gettingStartedContainer button.emphasis { background: ${emphasisButtonBackground}; }`);
|
||||
}
|
||||
|
||||
const pendingItemColor = theme.getColor(buttonSecondaryBackground);
|
||||
if (pendingItemColor) {
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task .codicon { color: ${pendingItemColor} } `);
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task .codicon { color: ${pendingItemColor} } `);
|
||||
}
|
||||
|
||||
const emphasisButtonHoverBackground = theme.getColor(buttonHoverBackground);
|
||||
if (emphasisButtonHoverBackground) {
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer button.emphasis:hover { background: ${emphasisButtonHoverBackground}; }`);
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .gettingStartedContainer button.emphasis:hover { background: ${emphasisButtonHoverBackground}; }`);
|
||||
}
|
||||
|
||||
const link = theme.getColor(textLinkForeground);
|
||||
if (link) {
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer a { color: ${link}; }`);
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .button-link { color: ${link}; }`);
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .button-link * { color: ${link}; }`);
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .gettingStartedContainer a { color: ${link}; }`);
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .gettingStartedContainer .button-link { color: ${link}; }`);
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .gettingStartedContainer .button-link * { color: ${link}; }`);
|
||||
}
|
||||
const activeLink = theme.getColor(textLinkActiveForeground);
|
||||
if (activeLink) {
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer a:hover,
|
||||
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer a:active { color: ${activeLink}; }`);
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .gettingStartedContainer a:hover,
|
||||
.monaco-workbench .part.editor > .content .gettingStartedContainer a:active { color: ${activeLink}; }`);
|
||||
}
|
||||
const focusColor = theme.getColor(focusBorder);
|
||||
if (focusColor) {
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer a:focus { outline-color: ${focusColor}; }`);
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .gettingStartedContainer a:focus { outline-color: ${focusColor}; }`);
|
||||
}
|
||||
const border = theme.getColor(contrastBorder);
|
||||
if (border) {
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer button { border-color: ${border}; border: 1px solid; }`);
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .gettingStartedContainer button { border-color: ${border}; border: 1px solid; }`);
|
||||
}
|
||||
const activeBorder = theme.getColor(activeContrastBorder);
|
||||
if (activeBorder) {
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer button:hover { outline-color: ${activeBorder}; }`);
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .gettingStartedContainer button:hover { outline-color: ${activeBorder}; }`);
|
||||
}
|
||||
|
||||
const progressBackground = theme.getColor(welcomePageProgressBackground);
|
||||
if (progressBackground) {
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.categories .progress-bar-outer { background-color: ${progressBackground}; }`);
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.categories .progress-bar-outer { background-color: ${progressBackground}; }`);
|
||||
}
|
||||
const progressForeground = theme.getColor(welcomePageProgressForeground);
|
||||
if (progressForeground) {
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.categories .progress-bar-inner { background-color: ${progressForeground}; }`);
|
||||
collector.addRule(`.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.categories .progress-bar-inner { background-color: ${progressForeground}; }`);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { escape } from 'vs/base/common/strings';
|
||||
import { localize } from 'vs/nls';
|
||||
|
||||
export default () => `
|
||||
<div class="gettingStartedContainer">
|
||||
<div class="gettingStarted" role="document">
|
||||
<div class="gettingStartedSlideCategory gettingStartedSlide categories">
|
||||
<div class="gap"></div>
|
||||
<div class="header">
|
||||
<h1 class="product-name caption">${escape(localize('gettingStarted.vscode', "Visual Studio Code"))}</h1>
|
||||
<p class="subtitle description">${escape(localize({ key: 'gettingStarted.editingRedefined', comment: ['Shown as subtitle on the Welcome page.'] }, "Code editing. Redefined"))}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gettingStartedSlideDetails gettingStartedSlide detail">
|
||||
<button class="prev-button button-link" x-dispatch="scrollPrev"><span class="scroll-button codicon codicon-chevron-left"></span>Back</a>
|
||||
<div class="gettingStartedDetailsContent">
|
||||
<div class="gap"></div>
|
||||
<div class="getting-started-detail-columns">
|
||||
<div class="gap"></div>
|
||||
<div class="getting-started-detail-left">
|
||||
<div class="getting-started-detail-title"></div>
|
||||
</div>
|
||||
<div class="getting-started-detail-right"></div>
|
||||
<div class="gap"></div>
|
||||
</div>
|
||||
<div class="gap"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`.replace(/\|/g, '`');
|
|
@ -13,7 +13,6 @@ import { IConfigurationRegistry, Extensions as ConfigurationExtensions, Configur
|
|||
import { IEditorInputFactoryRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor';
|
||||
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
|
||||
import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration';
|
||||
import product from 'vs/platform/product/common/product';
|
||||
|
||||
Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
|
||||
.registerConfiguration({
|
||||
|
@ -22,21 +21,14 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
|
|||
'workbench.startupEditor': {
|
||||
'scope': ConfigurationScope.RESOURCE,
|
||||
'type': 'string',
|
||||
'enum': [
|
||||
...['none', 'welcomePage', 'readme', 'newUntitledFile', 'welcomePageInEmptyWorkbench'],
|
||||
...(product.quality !== 'stable'
|
||||
? ['gettingStarted']
|
||||
: [])
|
||||
],
|
||||
'enum': ['none', 'welcomePage', 'readme', 'newUntitledFile', 'welcomePageInEmptyWorkbench', 'gettingStarted'],
|
||||
'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 Welcome page (default)."),
|
||||
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 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.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 Welcome page when opening an empty workbench."),],
|
||||
...(product.quality !== 'stable'
|
||||
? [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 Getting Started page (experimental).")]
|
||||
: [])
|
||||
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 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 Getting Started page.")]
|
||||
],
|
||||
'default': 'welcomePage',
|
||||
'description': localize('workbench.startupEditor', "Controls which editor is shown at startup, if none are restored from the previous session.")
|
||||
|
|
|
@ -46,7 +46,7 @@ import { IProductService } from 'vs/platform/product/common/productService';
|
|||
import { IEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { getGettingStartedInput, gettingStartedInputTypeId } from 'vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted';
|
||||
import { GettingStartedInput, gettingStartedInputTypeId } from 'vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted';
|
||||
import { welcomeButtonBackground, welcomeButtonHoverBackground, welcomePageBackground } from 'vs/workbench/contrib/welcome/page/browser/welcomePageColors';
|
||||
|
||||
const configurationKey = 'workbench.startupEditor';
|
||||
|
@ -123,7 +123,8 @@ export class WelcomePageContribution implements IWorkbenchContribution {
|
|||
}
|
||||
|
||||
if (startupEditorTypeID === gettingStartedInputTypeId) {
|
||||
editorService.openEditor(instantiationService.invokeFunction(getGettingStartedInput, {}), options);
|
||||
const launchEditor = instantiationService.createInstance(GettingStartedInput, {});
|
||||
return editorService.openEditor(launchEditor, options);
|
||||
} else {
|
||||
const launchEditor = instantiationService.createInstance(WelcomePage);
|
||||
return launchEditor.openEditor(options);
|
||||
|
|
|
@ -54,7 +54,7 @@ export class WorkspaceTrustRequestHandler extends Disposable implements IWorkben
|
|||
if (visible) {
|
||||
this.badgeDisposable.value = this.activityService.showGlobalActivity({
|
||||
badge: new IconBadge(workspaceTrustIcon, () => localize('requestTrustIconText', "Some features require workspace trust.")),
|
||||
priority: 0
|
||||
priority: 10
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@ import { IModeService } from 'vs/editor/common/services/modeService';
|
|||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import * as nls from 'vs/nls';
|
||||
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
@ -217,6 +216,10 @@ export class PreferencesService extends Disposable implements IPreferencesServic
|
|||
|
||||
private openSettings2(options?: ISettingsEditorOptions): Promise<IEditorPane> {
|
||||
const input = this.settingsEditor2Input;
|
||||
options = {
|
||||
...options,
|
||||
focusSearch: true
|
||||
};
|
||||
return this.editorService.openEditor(input, options ? SettingsEditorOptions.create(options) : undefined)
|
||||
.then(() => this.editorGroupService.activeGroup.activeEditorPane!);
|
||||
}
|
||||
|
@ -269,11 +272,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic
|
|||
return this.openOrSwitchSettings2(ConfigurationTarget.WORKSPACE_FOLDER, folder, options, group);
|
||||
}
|
||||
|
||||
switchSettings(target: ConfigurationTarget, resource: URI, jsonEditor?: boolean): Promise<void> {
|
||||
if (!jsonEditor) {
|
||||
return this.doOpenSettings2(target, resource).then(() => undefined);
|
||||
}
|
||||
|
||||
switchSettings(target: ConfigurationTarget, resource: URI): Promise<void> {
|
||||
const activeEditorPane = this.editorService.activeEditorPane;
|
||||
if (activeEditorPane?.input instanceof PreferencesEditorInput) {
|
||||
return this.doSwitchSettings(target, resource, activeEditorPane.input, activeEditorPane.group).then(() => undefined);
|
||||
|
@ -336,7 +335,12 @@ export class PreferencesService extends Disposable implements IPreferencesServic
|
|||
}
|
||||
|
||||
private openOrSwitchSettings2(configurationTarget: ConfigurationTarget, folderUri?: URI, options?: ISettingsEditorOptions, group: IEditorGroup = this.editorGroupService.activeGroup): Promise<IEditorPane | undefined> {
|
||||
return this.doOpenSettings2(configurationTarget, folderUri, options, group);
|
||||
const settingsOptions: ISettingsEditorOptions = {
|
||||
...options,
|
||||
target: configurationTarget,
|
||||
folderUri
|
||||
};
|
||||
return this.openSettings2(settingsOptions);
|
||||
}
|
||||
|
||||
private doOpenSettings(configurationTarget: ConfigurationTarget, resource: URI, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise<IEditorPane | undefined> {
|
||||
|
@ -388,17 +392,6 @@ export class PreferencesService extends Disposable implements IPreferencesServic
|
|||
return this.instantiationService.createInstance(Settings2EditorModel, this.getDefaultSettings(ConfigurationTarget.USER_LOCAL));
|
||||
}
|
||||
|
||||
private doOpenSettings2(target: ConfigurationTarget, folderUri: URI | undefined, options?: IEditorOptions, group?: IEditorGroup): Promise<IEditorPane | undefined> {
|
||||
const input = this.settingsEditor2Input;
|
||||
const settingsOptions: ISettingsEditorOptions = {
|
||||
...options,
|
||||
target,
|
||||
folderUri
|
||||
};
|
||||
|
||||
return this.editorService.openEditor(input, SettingsEditorOptions.create(settingsOptions), group);
|
||||
}
|
||||
|
||||
private async doSwitchSettings(target: ConfigurationTarget, resource: URI, input: PreferencesEditorInput, group: IEditorGroup, options?: ISettingsEditorOptions): Promise<IEditorPane> {
|
||||
const settingsURI = await this.getEditableSettingsURI(target, resource);
|
||||
if (!settingsURI) {
|
||||
|
|
|
@ -169,6 +169,7 @@ export interface ISettingsEditorOptions extends IEditorOptions {
|
|||
key: string;
|
||||
edit?: boolean;
|
||||
};
|
||||
focusSearch?: boolean;
|
||||
}
|
||||
|
||||
export class SettingsEditorOptions extends EditorOptions implements ISettingsEditorOptions {
|
||||
|
@ -180,6 +181,7 @@ export class SettingsEditorOptions extends EditorOptions implements ISettingsEdi
|
|||
key: string;
|
||||
edit?: boolean;
|
||||
};
|
||||
focusSearch?: boolean;
|
||||
|
||||
static create(settings: ISettingsEditorOptions): SettingsEditorOptions {
|
||||
const options = new SettingsEditorOptions();
|
||||
|
@ -190,6 +192,7 @@ export class SettingsEditorOptions extends EditorOptions implements ISettingsEdi
|
|||
options.query = settings.query;
|
||||
options.revealSetting = settings.revealSetting;
|
||||
options.pinned = true;
|
||||
options.focusSearch = settings.focusSearch;
|
||||
|
||||
return options;
|
||||
}
|
||||
|
@ -221,7 +224,7 @@ export interface IPreferencesService {
|
|||
openRemoteSettings(): Promise<IEditorPane | undefined>;
|
||||
openWorkspaceSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise<IEditorPane | undefined>;
|
||||
openFolderSettings(folder: URI, jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise<IEditorPane | undefined>;
|
||||
switchSettings(target: ConfigurationTarget, resource: URI, jsonEditor?: boolean): Promise<void>;
|
||||
switchSettings(target: ConfigurationTarget, resource: URI): Promise<void>;
|
||||
openGlobalKeybindingSettings(textual: boolean, options?: IKeybindingsEditorOptions): Promise<void>;
|
||||
openDefaultKeybindingsFile(): Promise<IEditorPane | undefined>;
|
||||
getEditableSettingsURI(configurationTarget: ConfigurationTarget, resource?: URI): Promise<URI | null>;
|
||||
|
|
|
@ -475,12 +475,14 @@ export abstract class AbstractTextFileService extends Disposable implements ITex
|
|||
if (!languageName) {
|
||||
return untitledName;
|
||||
}
|
||||
|
||||
const extension = this.modeService.getExtensions(languageName)[0];
|
||||
if (extension) {
|
||||
if (!untitledName.endsWith(extension)) {
|
||||
return untitledName + extension;
|
||||
}
|
||||
}
|
||||
|
||||
const filename = this.modeService.getFilenames(languageName)[0];
|
||||
return filename || untitledName;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import { URI } from 'vs/base/common/uri';
|
|||
import * as types from 'vs/workbench/api/common/extHostTypes';
|
||||
import { isWindows } from 'vs/base/common/platform';
|
||||
import { assertType } from 'vs/base/common/types';
|
||||
import { notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
|
||||
function assertToJSON(a: any, expected: any) {
|
||||
const raw = JSON.stringify(a);
|
||||
|
@ -648,4 +649,40 @@ suite('ExtHostTypes', function () {
|
|||
assert.deepStrictEqual(md.value, '\n```html\n<img src=0 onerror="alert(1)">\n```\n');
|
||||
});
|
||||
|
||||
test('NotebookMetadata - defaults', function () {
|
||||
const obj = new types.NotebookDocumentMetadata();
|
||||
assert.strictEqual(obj.cellEditable, notebookDocumentMetadataDefaults.cellEditable);
|
||||
assert.strictEqual(obj.cellHasExecutionOrder, notebookDocumentMetadataDefaults.cellHasExecutionOrder);
|
||||
assert.strictEqual(obj.cellRunnable, notebookDocumentMetadataDefaults.cellRunnable);
|
||||
assert.deepStrictEqual(obj.custom, notebookDocumentMetadataDefaults.custom);
|
||||
assert.deepStrictEqual(obj.displayOrder, notebookDocumentMetadataDefaults.displayOrder);
|
||||
assert.strictEqual(obj.editable, notebookDocumentMetadataDefaults.editable);
|
||||
assert.deepStrictEqual(obj.languages, notebookDocumentMetadataDefaults.languages);
|
||||
assert.strictEqual(obj.runState, notebookDocumentMetadataDefaults.runState);
|
||||
assert.strictEqual(obj.runnable, notebookDocumentMetadataDefaults.runnable);
|
||||
assert.strictEqual(obj.trusted, notebookDocumentMetadataDefaults.trusted);
|
||||
});
|
||||
|
||||
test('NotebookMetadata - with', function () {
|
||||
const obj = new types.NotebookDocumentMetadata();
|
||||
const newObj = obj.with({ trusted: false });
|
||||
assert.ok(obj !== newObj);
|
||||
assert.strictEqual(obj.trusted, true);
|
||||
assert.strictEqual(newObj.trusted, false);
|
||||
});
|
||||
|
||||
test('NotebookCellMetadata - with', function () {
|
||||
const obj = new types.NotebookCellMetadata(true, false, true);
|
||||
|
||||
const newObj = obj.with({ statusMessage: 'hello' });
|
||||
assert.ok(obj !== newObj);
|
||||
assert.strictEqual(obj.statusMessage, undefined);
|
||||
assert.strictEqual(obj.editable, true);
|
||||
assert.strictEqual(obj.custom, undefined);
|
||||
|
||||
assert.strictEqual(newObj.statusMessage, 'hello');
|
||||
assert.strictEqual(newObj.editable, true);
|
||||
assert.strictEqual(newObj.custom, undefined);
|
||||
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue