Merge remote-tracking branch 'origin/main' into tyriar/megan

This commit is contained in:
Daniel Imms 2021-02-16 02:44:34 -08:00
commit 982b28f42d
240 changed files with 4321 additions and 2536 deletions

View file

@ -8,7 +8,7 @@ set -e
SCRIPT_PATH="$(cd "$(dirname $0)" && pwd)"
CONTAINER_IMAGE_REPOSITORY="$1"
BRANCH="${2:-"master"}"
BRANCH="${2:-"main"}"
if [ "${CONTAINER_IMAGE_REPOSITORY}" = "" ]; then
echo "Container repository not specified!"

View file

@ -2,7 +2,7 @@
"name": "Code - OSS",
// Image contents: https://github.com/microsoft/vscode-dev-containers/blob/master/repository-containers/images/github.com/microsoft/vscode/.devcontainer/base.Dockerfile
"image": "mcr.microsoft.com/vscode/devcontainers/repos/microsoft/vscode:branch-master",
"image": "mcr.microsoft.com/vscode/devcontainers/repos/microsoft/vscode:branch-main",
"workspaceMount": "source=${localWorkspaceFolder},target=/home/node/workspace/vscode,type=bind,consistency=cached",
"workspaceFolder": "/home/node/workspace/vscode",

View file

@ -278,6 +278,7 @@
"sinon",
"vs/nls",
"**/vs/base/{common,browser}/**",
"**/vs/base/parts/*/{common,browser}/**",
"**/vs/platform/*/{common,browser}/**",
"**/vs/platform/*/test/{common,browser}/**"
]

View file

@ -2,7 +2,7 @@
* Read our Pull Request guidelines:
https://github.com/microsoft/vscode/wiki/How-to-Contribute#pull-requests
* Associate an issue with the Pull Request.
* Ensure that the code is up-to-date with the `master` branch.
* Ensure that the code is up-to-date with the `main` branch.
* Include a description of the proposed changes and how to test them.
-->

View file

@ -7,7 +7,7 @@ on:
types:
- completed
branches:
- master
- main
- release/*
jobs:

View file

@ -3,11 +3,11 @@ name: CI
on:
push:
branches:
- master
- main
- release/*
pull_request:
branches:
- master
- main
- release/*
jobs:

View file

@ -2,9 +2,9 @@ name: VS Code Repo Dev Container Cache Image Generation
on:
push:
# Currently doing this for master, but could be done for PRs as well
# Currently doing this for main, but could be done for PRs as well
branches:
- "master"
- "main"
# Only updates to these files result in changes to installed packages, so skip otherwise
paths:
@ -35,6 +35,6 @@ jobs:
az acr login --name $ACR_REGISTRY_NAME
GIT_BRANCH=$(echo "${{ github.ref }}" | grep -oP 'refs/(heads|tags)/\K(.+)')
if [ "$GIT_BRANCH" == "" ]; then GIT_BRANCH=master; fi
if [ "$GIT_BRANCH" == "" ]; then GIT_BRANCH=main; fi
.devcontainer/cache/build-cache-image.sh "${{ secrets.CONTAINER_IMAGE_REGISTRY }}/public/vscode/devcontainers/repos/microsoft/vscode" "${GIT_BRANCH}"

View file

@ -3,7 +3,7 @@ on:
pull_request:
push:
branches:
- master
- main
jobs:
richnav:

3
.vscode/launch.json vendored
View file

@ -110,7 +110,8 @@
// "${workspaceFolder}", // Uncomment for running out of sources.
"${workspaceFolder}/extensions/vscode-api-tests/testWorkspace",
"--extensionDevelopmentPath=${workspaceFolder}/extensions/vscode-api-tests",
"--extensionTestsPath=${workspaceFolder}/extensions/vscode-api-tests/out/singlefolder-tests"
"--extensionTestsPath=${workspaceFolder}/extensions/vscode-api-tests/out/singlefolder-tests",
"--disable-extensions"
],
"outFiles": [
"${workspaceFolder}/out/**/*.js"

View file

@ -30,7 +30,7 @@
{
"kind": 1,
"language": "markdown",
"value": "New issues or pull requests submitted by the community are initially triaged by an [automatic classification bot](https://github.com/microsoft/vscode-github-triage-actions/tree/master/classifier-deep). Issues that the bot does not correctly triage are then triaged by a team member. The team rotates the inbox tracker on a weekly basis.\n\nA [mirror](https://github.com/JacksonKearl/testissues/issues) of the VS Code issue stream is available with details about how the bot classifies issues, including feature-area classifications and confidence ratings. Per-category confidence thresholds and feature-area ownership data is maintained in [.github/classifier.json](https://github.com/microsoft/vscode/blob/master/.github/classifier.json). \n\n💡 The bot is being run through a GitHub action that runs every 30 minutes. Give the bot the opportunity to classify an issue before doing it manually.\n\n### Inbox Tracking\n\nThe inbox tracker is responsible for the [global inbox](https://github.com/microsoft/vscode/issues?utf8=%E2%9C%93&q=is%3Aopen+no%3Aassignee+-label%3Afeature-request+-label%3Atestplan-item+-label%3Aplan-item) containing all **open issues and pull requests** that\n- are neither **feature requests** nor **test plan items** nor **plan items** and\n- have **no owner assignment**.\n\nThe **inbox tracker** may perform any step described in our [issue triaging documentation](https://github.com/microsoft/vscode/wiki/Issues-Triaging) but its main responsibility is to route issues to the actual feature area owner.\n\nFeature area owners track the **feature area inbox** containing all **open issues and pull requests** that\n- are personally assigned to them and are not assigned to any milestone\n- are labeled with their feature area label and are not assigned to any milestone.\nThis secondary triage may involve any of the steps described in our [issue triaging documentation](https://github.com/microsoft/vscode/wiki/Issues-Triaging) and results in a fully triaged or closed issue.\n\nThe [github triage extension](https://github.com/microsoft/vscode-github-triage-extension) can be used to assist with triaging — it provides a \"Command Palette\"-style list of triaging actions like assignment, labeling, and triggers for various bot actions.",
"value": "New issues or pull requests submitted by the community are initially triaged by an [automatic classification bot](https://github.com/microsoft/vscode-github-triage-actions/tree/master/classifier-deep). Issues that the bot does not correctly triage are then triaged by a team member. The team rotates the inbox tracker on a weekly basis.\n\nA [mirror](https://github.com/JacksonKearl/testissues/issues) of the VS Code issue stream is available with details about how the bot classifies issues, including feature-area classifications and confidence ratings. Per-category confidence thresholds and feature-area ownership data is maintained in [.github/classifier.json](https://github.com/microsoft/vscode/blob/main/.github/classifier.json). \n\n💡 The bot is being run through a GitHub action that runs every 30 minutes. Give the bot the opportunity to classify an issue before doing it manually.\n\n### Inbox Tracking\n\nThe inbox tracker is responsible for the [global inbox](https://github.com/microsoft/vscode/issues?utf8=%E2%9C%93&q=is%3Aopen+no%3Aassignee+-label%3Afeature-request+-label%3Atestplan-item+-label%3Aplan-item) containing all **open issues and pull requests** that\n- are neither **feature requests** nor **test plan items** nor **plan items** and\n- have **no owner assignment**.\n\nThe **inbox tracker** may perform any step described in our [issue triaging documentation](https://github.com/microsoft/vscode/wiki/Issues-Triaging) but its main responsibility is to route issues to the actual feature area owner.\n\nFeature area owners track the **feature area inbox** containing all **open issues and pull requests** that\n- are personally assigned to them and are not assigned to any milestone\n- are labeled with their feature area label and are not assigned to any milestone.\nThis secondary triage may involve any of the steps described in our [issue triaging documentation](https://github.com/microsoft/vscode/wiki/Issues-Triaging) and results in a fully triaged or closed issue.\n\nThe [github triage extension](https://github.com/microsoft/vscode-github-triage-extension) can be used to assist with triaging — it provides a \"Command Palette\"-style list of triaging actions like assignment, labeling, and triggers for various bot actions.",
"editable": true
},
{

View file

@ -8,7 +8,8 @@
{
"kind": 2,
"language": "github-issues",
"value": "// list of repos we work in\n$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog\n\n// current milestone name\n$milestone=milestone:\"February 2021\""
"value": "// list of repos we work in\n$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog\n\n// current milestone name\n$milestone=milestone:\"February 2021\"",
"editable": true
},
{
"kind": 1,
@ -19,7 +20,8 @@
{
"kind": 2,
"language": "github-issues",
"value": "$repos $milestone assignee:@me is:open"
"value": "$repos $milestone assignee:@me is:open",
"editable": true
},
{
"kind": 1,
@ -36,7 +38,8 @@
{
"kind": 2,
"language": "github-issues",
"value": "$repos assignee:@me is:open label:bug"
"value": "$repos assignee:@me is:open label:bug",
"editable": true
},
{
"kind": 1,
@ -47,7 +50,8 @@
{
"kind": 2,
"language": "github-issues",
"value": "$repos assignee:@me is:open label:debt OR $repos assignee:@me is:open label:engineering"
"value": "$repos assignee:@me is:open label:debt OR $repos assignee:@me is:open label:engineering",
"editable": true
},
{
"kind": 1,
@ -58,7 +62,8 @@
{
"kind": 2,
"language": "github-issues",
"value": "$repos assignee:@me is:open label:perf OR $repos assignee:@me is:open label:perf-startup OR $repos assignee:@me is:open label:perf-bloat OR $repos assignee:@me is:open label:freeze-slow-crash-leak"
"value": "$repos assignee:@me is:open label:perf OR $repos assignee:@me is:open label:perf-startup OR $repos assignee:@me is:open label:perf-bloat OR $repos assignee:@me is:open label:freeze-slow-crash-leak",
"editable": true
},
{
"kind": 1,
@ -69,12 +74,14 @@
{
"kind": 2,
"language": "github-issues",
"value": "$repos assignee:@me is:open label:feature-request milestone:Backlog sort:reactions-+1-desc"
"value": "$repos assignee:@me is:open label:feature-request milestone:Backlog sort:reactions-+1-desc",
"editable": true
},
{
"kind": 2,
"language": "github-issues",
"value": "$repos assignee:@me is:open milestone:\"Backlog Candidates\""
"value": "$repos assignee:@me is:open milestone:\"Backlog Candidates\"",
"editable": true
},
{
"kind": 1,
@ -91,7 +98,8 @@
{
"kind": 2,
"language": "github-issues",
"value": "$repos assignee:@me is:open type:issue -label:bug -label:\"needs more info\" -label:feature-request -label:under-discussion -label:debt -label:plan-item -label:upstream"
"value": "$repos assignee:@me is:open type:issue -label:bug -label:\"needs more info\" -label:feature-request -label:under-discussion -label:debt -label:plan-item -label:upstream",
"editable": true
},
{
"kind": 1,
@ -102,6 +110,7 @@
{
"kind": 2,
"language": "github-issues",
"value": "$repos assignee:@me is:open label:\"needs more info\""
"value": "$repos assignee:@me is:open label:\"needs more info\"",
"editable": true
}
]

View file

@ -5,7 +5,7 @@
## The Repository
This repository ("`Code - OSS`") is where we (Microsoft) develop the [Visual Studio Code](https://code.visualstudio.com) product. Not only do we work on code and issues here, we also publish our [roadmap](https://github.com/microsoft/vscode/wiki/Roadmap), [monthly iteration plans](https://github.com/microsoft/vscode/wiki/Iteration-Plans), and our [endgame plans](https://github.com/microsoft/vscode/wiki/Running-the-Endgame). This source code is available to everyone under the standard [MIT license](https://github.com/microsoft/vscode/blob/master/LICENSE.txt).
This repository ("`Code - OSS`") is where we (Microsoft) develop the [Visual Studio Code](https://code.visualstudio.com) product. Not only do we work on code and issues here, we also publish our [roadmap](https://github.com/microsoft/vscode/wiki/Roadmap), [monthly iteration plans](https://github.com/microsoft/vscode/wiki/Iteration-Plans), and our [endgame plans](https://github.com/microsoft/vscode/wiki/Running-the-Endgame). This source code is available to everyone under the standard [MIT license](https://github.com/microsoft/vscode/blob/main/LICENSE.txt).
## Visual Studio Code

View file

@ -71,6 +71,7 @@ steps:
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
- script: |
set -e
npx https://aka.ms/enablesecurefeed standAlone
timeoutInMinutes: 5
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true'))
@ -387,7 +388,3 @@ steps:
displayName: Upload configuration (for Bing settings search)
condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), ne(variables['VSCODE_PUBLISH'], 'false'))
continueOnError: true
- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0
displayName: "Component Detection"
continueOnError: true

View file

@ -1,9 +1,9 @@
trigger:
branches:
include: ["master", "release/*"]
include: ["main", "release/*"]
pr:
branches:
include: ["master", "release/*"]
include: ["main", "release/*"]
steps:
- task: NodeTool@0
@ -31,8 +31,8 @@ steps:
git remote add distro "https://github.com/$VSCODE_MIXIN_REPO.git"
git fetch distro
# Push master branch into oss/master
git push distro origin/master:refs/heads/oss/master
# Push main branch into oss/main
git push distro origin/main:refs/heads/oss/main
# Push every release branch into oss/release
git for-each-ref --format="%(refname:short)" refs/remotes/origin/release/* | sed 's/^origin\/\(.*\)$/\0:refs\/heads\/oss\/\1/' | xargs git push distro

View file

@ -28,9 +28,9 @@ steps:
git config user.name "VSCode"
git checkout origin/electron-11.x.y
git merge origin/master
git merge origin/main
# Push master branch into exploration branch
# Push main branch into exploration branch
git push origin HEAD:electron-11.x.y
displayName: Sync & Merge Exploration

View file

@ -69,6 +69,7 @@ steps:
displayName: Extract node_modules cache
- script: |
set -e
npx https://aka.ms/enablesecurefeed standAlone
timeoutInMinutes: 5
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true'))
@ -132,7 +133,3 @@ steps:
artifact: vscode-server-linux-alpine-web
displayName: Publish web server archive
condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false'))
- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0
displayName: "Component Detection"
continueOnError: true

View file

@ -67,6 +67,7 @@ steps:
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['VSCODE_ARCH'], 'x64'))
- script: |
set -e
npx https://aka.ms/enablesecurefeed standAlone
timeoutInMinutes: 5
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true'))
@ -286,7 +287,3 @@ steps:
artifactName: "snap-$(VSCODE_ARCH)"
targetPath: .build/linux/snap-tarball
condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false'))
- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0
displayName: "Component Detection"
continueOnError: true

View file

@ -5,7 +5,7 @@ schedules:
displayName: Mon-Fri at 7:00
branches:
include:
- master
- main
parameters:
- name: VSCODE_QUALITY

View file

@ -50,6 +50,7 @@ steps:
displayName: Extract node_modules cache
- script: |
set -e
npx https://aka.ms/enablesecurefeed standAlone
timeoutInMinutes: 5
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true'))
@ -136,3 +137,7 @@ steps:
targetPath: $(Build.ArtifactStagingDirectory)/compilation.tar.gz
artifactName: Compilation
displayName: Publish compilation artifact
- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0
displayName: "Component Detection"
continueOnError: true

View file

@ -61,7 +61,7 @@ steps:
TAG_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`)
CHANNEL="G1C14HJ2F"
MESSAGE="DefinitelyTyped/DefinitelyTyped#vscode-types-$TAG_VERSION created. Endgame master, please open this link, examine changes and create a PR:"
MESSAGE="DefinitelyTyped/DefinitelyTyped#vscode-types-$TAG_VERSION created. Endgame champion, please open this link, examine changes and create a PR:"
LINK="https://github.com/DefinitelyTyped/DefinitelyTyped/compare/vscode-types-$TAG_VERSION?quick_pull=1&body=Updating%20VS%20Code%20Extension%20API.%20See%20https%3A%2F%2Fgithub.com%2Fmicrosoft%2Fvscode%2Fissues%2F70175%20for%20details."
MESSAGE2="[@eamodio, @jrieken, @kmaetzel, @egamma]. Please review and merge PR to publish @types/vscode."

View file

@ -72,7 +72,7 @@ function getNewFileHeader(tag: string) {
`/*---------------------------------------------------------------------------------------------`,
` * Copyright (c) Microsoft Corporation. All rights reserved.`,
` * Licensed under the MIT License.`,
` * See https://github.com/microsoft/vscode/blob/master/LICENSE.txt for license information.`,
` * See https://github.com/microsoft/vscode/blob/main/LICENSE.txt for license information.`,
` *--------------------------------------------------------------------------------------------*/`,
``,
`/**`,

View file

@ -60,6 +60,7 @@ steps:
displayName: Extract node_modules cache
- script: |
set -e
npx https://aka.ms/enablesecurefeed standAlone
timeoutInMinutes: 5
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true'))

View file

@ -65,8 +65,10 @@ steps:
condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true'))
displayName: Extract node_modules cache
- script: |
npx https://aka.ms/enablesecurefeed standAlone
- powershell: |
. build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop"
exec { npx https://aka.ms/enablesecurefeed standAlone }
timeoutInMinutes: 5
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true'))
displayName: Switch to Terrapin packages
@ -320,7 +322,3 @@ steps:
artifact: vscode-server-win32-$(VSCODE_ARCH)-web
displayName: Publish web server archive
condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64'))
- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0
displayName: "Component Detection"
continueOnError: true

View file

@ -49,7 +49,7 @@ let BUNDLED_FILE_HEADER = [
' * Copyright (c) Microsoft Corporation. All rights reserved.',
' * Version: ' + headerVersion,
' * Released under the MIT license',
' * https://github.com/microsoft/vscode/blob/master/LICENSE.txt',
' * https://github.com/microsoft/vscode/blob/main/LICENSE.txt',
' *-----------------------------------------------------------*/',
''
].join('\n');

View file

@ -52,13 +52,13 @@ const vscodeResources = [
'out-build/bootstrap-amd.js',
'out-build/bootstrap-node.js',
'out-build/bootstrap-window.js',
'out-build/paths.js',
'out-build/vs/**/*.{svg,png,html,jpg}',
'!out-build/vs/code/browser/**/*.html',
'!out-build/vs/editor/standalone/**/*.svg',
'out-build/vs/base/common/performance.js',
'out-build/vs/base/node/languagePacks.js',
'out-build/vs/base/node/{stdForkStart.js,terminateProcess.sh,cpuUsage.sh,ps.sh}',
'out-build/vs/base/node/userDataPath.js',
'out-build/vs/base/browser/ui/codicons/codicon/**',
'out-build/vs/base/parts/sandbox/electron-browser/preload.js',
'out-build/vs/workbench/browser/media/*-theme.css',
@ -284,6 +284,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op
let result = all
.pipe(util.skipDirectories())
.pipe(util.fixWin32DirectoryPermissions())
.pipe(filter(['**', '!**/.github/**'], { dot: true })) // https://github.com/microsoft/vscode/issues/116523
.pipe(electron(_.extend({}, config, { platform, arch: arch === 'armhf' ? 'arm' : arch, ffmpegChromium: true })))
.pipe(filter(['**', '!LICENSE', '!LICENSES.chromium.html', '!version'], { dot: true }));
@ -526,7 +527,7 @@ gulp.task(task.define(
if (!shouldSetupSettingsSearch()) {
const branch = process.env.BUILD_SOURCEBRANCH;
console.log(`Only runs on master and release branches, not ${branch}`);
console.log(`Only runs on main and release branches, not ${branch}`);
return;
}
@ -552,21 +553,21 @@ gulp.task(task.define(
function shouldSetupSettingsSearch() {
const branch = process.env.BUILD_SOURCEBRANCH;
return branch && (/\/master$/.test(branch) || branch.indexOf('/release/') >= 0);
return branch && (/\/main$/.test(branch) || branch.indexOf('/release/') >= 0);
}
function getSettingsSearchBuildId(packageJson) {
try {
const branch = process.env.BUILD_SOURCEBRANCH;
const branchId = branch.indexOf('/release/') >= 0 ? 0 :
/\/master$/.test(branch) ? 1 :
/\/main$/.test(branch) ? 1 :
2; // Some unexpected branch
const out = cp.execSync(`git rev-list HEAD --count`);
const count = parseInt(out.toString());
// <version number><commit count><branchId (avoid unlikely conflicts)>
// 1.25.1, 1,234,567 commits, master = 1250112345671
// 1.25.1, 1,234,567 commits, main = 1250112345671
return util.versionStringToNumber(packageJson.version) * 1e8 + count * 10 + branchId;
} catch (e) {
throw new Error('Could not determine build number: ' + e.toString());

View file

@ -11,4 +11,4 @@ a good page describing the code editor's features is [here](https://code.visuals
This npm module contains the core editor functionality, as it comes from the [vscode repository](https://github.com/microsoft/vscode).
## License
[MIT](https://github.com/microsoft/vscode/blob/master/LICENSE.txt)
[MIT](https://github.com/microsoft/vscode/blob/main/LICENSE.txt)

View file

@ -212,6 +212,11 @@
"type": "number",
"default": 0.3,
"description": "%emmetPreferencesCssFuzzySearchMinScore%"
},
"output.reverseAttributes": {
"type": "boolean",
"default": false,
"description": "%emmetPreferencesOutputReverseAttributes%"
}
}
},
@ -430,8 +435,7 @@
"deps": "yarn add vscode-emmet-helper"
},
"devDependencies": {
"@types/node": "^12.19.9",
"emmet": "https://github.com/rzhao271/emmet.git#1b2df677d8925ef5ea6da9df8845968403979a0a"
"@types/node": "^12.19.9"
},
"dependencies": {
"@emmetio/abbreviation": "^2.2.0",
@ -439,7 +443,7 @@
"@emmetio/html-matcher": "^0.3.3",
"@emmetio/math-expression": "^1.0.4",
"image-size": "^0.5.2",
"vscode-emmet-helper": "2.2.4",
"vscode-emmet-helper": "^2.3.0",
"vscode-languageserver-textdocument": "^1.0.1"
}
}

View file

@ -55,5 +55,6 @@
"emmetPreferencesCssOProperties": "Comma separated CSS properties that get the 'o' vendor prefix when used in Emmet abbreviation that starts with `-`. Set to empty string to always avoid the 'o' prefix.",
"emmetPreferencesCssMsProperties": "Comma separated CSS properties that get the 'ms' vendor prefix when used in Emmet abbreviation that starts with `-`. Set to empty string to always avoid the 'ms' prefix.",
"emmetPreferencesCssFuzzySearchMinScore": "The minimum score (from 0 to 1) that fuzzy-matched abbreviation should achieve. Lower values may produce many false-positive matches, higher values may reduce possible matches.",
"emmetOptimizeStylesheetParsing": "When set to `false`, the whole file is parsed to determine if current position is valid for expanding Emmet abbreviations. When set to `true`, only the content around the current position in css/scss/less files is parsed."
"emmetOptimizeStylesheetParsing": "When set to `false`, the whole file is parsed to determine if current position is valid for expanding Emmet abbreviations. When set to `true`, only the content around the current position in css/scss/less files is parsed.",
"emmetPreferencesOutputReverseAttributes": "If `true`, reverses attribute merging directions when resolving snippets."
}

View file

@ -2,10 +2,10 @@
# yarn lockfile v1
"@emmetio/abbreviation@^2.2.0":
version "2.2.0"
resolved "https://registry.yarnpkg.com/@emmetio/abbreviation/-/abbreviation-2.2.0.tgz#9f8dedbdb00e3136d6d37c6415375c82c0bb477f"
integrity sha512-NPGVUmnr7cLj4i6MKS4c8NjuoIIJROrruJl/8nXsp2MdbDRHvtfq25foySvv/NbfqTQm+P9JzVLDD9JxGIpvkQ==
"@emmetio/abbreviation@^2.2.0", "@emmetio/abbreviation@^2.2.1":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@emmetio/abbreviation/-/abbreviation-2.2.1.tgz#d9458fe1f09fe042f019c48aa681165ba613a48d"
integrity sha512-uUNwNgbH0JPlrdXhy8VQbNPLLG7abMvOaLVMblx22i68Rl9r+2N235ALgIYFUty1yXC9DkVw6xMbz/D4QVARcQ==
dependencies:
"@emmetio/scanner" "^1.0.0"
@ -54,15 +54,16 @@
integrity sha1-Rs/+oRmgoAMxKiHC2bVijLX81EI=
"@types/node@^12.19.9":
version "12.19.15"
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.19.15.tgz#0de7e978fb43db62da369db18ea088a63673c182"
integrity sha512-lowukE3GUI+VSYSu6VcBXl14d61Rp5hA1D+61r16qnwC0lYNSqdxcvRh0pswejorHfS+HgwBasM8jLXz0/aOsw==
version "12.20.0"
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.0.tgz#692dfdecd6c97f5380c42dd50f19261f9f604deb"
integrity sha512-0/41wHcurotvSOTHQUFkgL702c3pyWR1mToSrrX3pGPvGfpHTv3Ksx0M4UVuU5VJfjVb62Eyr1eKO1tWNUCg2Q==
"emmet@https://github.com/rzhao271/emmet.git#1b2df677d8925ef5ea6da9df8845968403979a0a":
version "2.3.0"
resolved "https://github.com/rzhao271/emmet.git#1b2df677d8925ef5ea6da9df8845968403979a0a"
emmet@^2.3.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/emmet/-/emmet-2.3.1.tgz#77614d949d1d01e5c248d08043a13a7f4d539e47"
integrity sha512-u8h++9u3y9QWhn0imUXfQO+s80To5MGD97zd/00wGC39CfNGBPe//ZKepJz9I1LQ2FDRXHrn+e3JaN/53Y5z6A==
dependencies:
"@emmetio/abbreviation" "^2.2.0"
"@emmetio/abbreviation" "^2.2.1"
"@emmetio/css-abbreviation" "^2.1.2"
image-size@^0.5.2:
@ -75,12 +76,12 @@ jsonc-parser@^2.3.0:
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.3.1.tgz#59549150b133f2efacca48fe9ce1ec0659af2342"
integrity sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==
vscode-emmet-helper@2.2.4:
version "2.2.4"
resolved "https://registry.yarnpkg.com/vscode-emmet-helper/-/vscode-emmet-helper-2.2.4.tgz#8ab86d2b7fe9e6270b4c77c9fd8d1eb8f3f4c401"
integrity sha512-1N6bMzP1ZzkDGzamvsKxQ/lOmBc4+OQdj0dA2C9A5PSeYV9gh5xbJ061sm+VyFHOGZE+VyUQq5m/WFmFsLbKnA==
vscode-emmet-helper@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/vscode-emmet-helper/-/vscode-emmet-helper-2.3.0.tgz#a98357ad5ac9c71d7c00396f22b7963b1a74cc5c"
integrity sha512-QhU8+HlynMuUkqBYgA3wIDTSsUNkw8GWxLR214Hjvwr0lkFZa0CRqW/PzAI1CFREjSrTxJYQvkVnbfatZzKAuA==
dependencies:
emmet "https://github.com/rzhao271/emmet.git#1b2df677d8925ef5ea6da9df8845968403979a0a"
emmet "^2.3.0"
jsonc-parser "^2.3.0"
vscode-languageserver-textdocument "^1.0.1"
vscode-languageserver-types "^3.15.1"

View file

@ -53,7 +53,7 @@
"vscode:prepublish": "npm run compile"
},
"dependencies": {
"node-fetch": "2.6.0",
"node-fetch": "2.6.1",
"uuid": "8.1.0",
"vscode-extension-telemetry": "0.1.1",
"vscode-nls": "^4.1.2"

View file

@ -14,7 +14,7 @@ export async function activate(context: vscode.ExtensionContext) {
const telemetryReporter = new TelemetryReporter(name, version, aiKey);
context.subscriptions.push(vscode.window.registerUriHandler(uriHandler));
const loginService = new GitHubAuthenticationProvider(context);
const loginService = new GitHubAuthenticationProvider(context, telemetryReporter);
await loginService.initialize(context);
@ -24,8 +24,7 @@ export async function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(vscode.authentication.registerAuthenticationProvider('github', 'GitHub', {
onDidChangeSessions: onDidChangeSessions.event,
getAllSessions: () => Promise.resolve(loginService.sessions),
getSessions: (scopes: string[]) => loginService.getSessions(scopes),
getSessions: (scopes?: string[]) => loginService.getSessions(scopes),
createSession: async (scopeList: string[]) => {
try {
/* __GDPR__

View file

@ -9,6 +9,7 @@ import { Keychain } from './common/keychain';
import { GitHubServer, NETWORK_ERROR } from './githubServer';
import Logger from './common/logger';
import { arrayEquals } from './common/utils';
import TelemetryReporter from 'vscode-extension-telemetry';
export const onDidChangeSessions = new vscode.EventEmitter<vscode.AuthenticationProviderAuthenticationSessionsChangeEvent>();
@ -25,12 +26,13 @@ interface SessionData {
export class GitHubAuthenticationProvider {
private _sessions: vscode.AuthenticationSession[] = [];
private _githubServer = new GitHubServer();
private _githubServer: GitHubServer;
private _keychain: Keychain;
constructor(context: vscode.ExtensionContext) {
constructor(context: vscode.ExtensionContext, telemetryReporter: TelemetryReporter) {
this._keychain = new Keychain(context);
this._githubServer = new GitHubServer(telemetryReporter);
}
public async initialize(context: vscode.ExtensionContext): Promise<void> {
@ -44,8 +46,10 @@ export class GitHubAuthenticationProvider {
context.subscriptions.push(context.secrets.onDidChange(() => this.checkForUpdates()));
}
async getSessions(scopes: string[]): Promise<vscode.AuthenticationSession[]> {
return this._sessions.filter(session => arrayEquals(session.scopes, scopes));
async getSessions(scopes?: string[]): Promise<vscode.AuthenticationSession[]> {
return scopes
? this._sessions.filter(session => arrayEquals(session.scopes, scopes))
: this._sessions;
}
private async verifySessions(): Promise<void> {
@ -53,6 +57,7 @@ export class GitHubAuthenticationProvider {
const verificationPromises = this._sessions.map(async session => {
try {
await this._githubServer.getUserInfo(session.accessToken);
this._githubServer.checkIsEdu(session.accessToken);
verifiedSessions.push(session);
} catch (e) {
// Remove sessions that return unauthorized response
@ -161,6 +166,7 @@ export class GitHubAuthenticationProvider {
public async createSession(scopes: string): Promise<vscode.AuthenticationSession> {
const token = await this._githubServer.login(scopes);
const session = await this.tokenToSession(token, scopes.split(' '));
this._githubServer.checkIsEdu(token);
await this.setToken(session);
return session;
}

View file

@ -9,6 +9,7 @@ import fetch, { Response } from 'node-fetch';
import { v4 as uuid } from 'uuid';
import { PromiseAdapter, promiseFromEvent } from './common/utils';
import Logger from './common/logger';
import TelemetryReporter from 'vscode-extension-telemetry';
const localize = nls.loadMessageBundle();
@ -41,6 +42,8 @@ export class GitHubServer {
private _pendingStates = new Map<string, string[]>();
private _codeExchangePromises = new Map<string, Promise<string>>();
constructor(private readonly telemetryReporter: TelemetryReporter) { }
private isTestEnvironment(url: vscode.Uri): boolean {
return url.authority === 'vscode-web-test-playground.azurewebsites.net' || url.authority.startsWith('localhost:');
}
@ -210,4 +213,36 @@ export class GitHubServer {
throw new Error(result.statusText);
}
}
public async checkIsEdu(token: string): Promise<void> {
try {
const result = await fetch('https://education.github.com/api/user', {
headers: {
Authorization: `token ${token}`,
'faculty-check-preview': 'true',
'User-Agent': 'Visual-Studio-Code'
}
});
if (result.ok) {
const json: { student: boolean, faculty: boolean } = await result.json();
/* __GDPR__
"session" : {
"isEdu": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }
}
*/
this.telemetryReporter.sendTelemetryEvent('session', {
isEdu: json.student
? 'student'
: json.faculty
? 'faculty'
: 'none'
});
}
} catch (e) {
// No-op
}
}
}

View file

@ -84,10 +84,10 @@ mime-types@^2.1.12:
dependencies:
mime-db "1.44.0"
node-fetch@2.6.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd"
integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==
node-fetch@2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
semver@^5.3.0:
version "5.7.1"

View file

@ -125,9 +125,9 @@ is-plain-object@^3.0.0:
integrity sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==
node-fetch@^2.3.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd"
integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==
version "2.6.1"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
once@^1.4.0:
version "1.4.0"

View file

@ -47,7 +47,7 @@
},
"dependencies": {
"buffer": "^5.6.0",
"node-fetch": "^2.6.0",
"node-fetch": "2.6.1",
"randombytes": "github:rmacfarlane/randombytes#b28d4ecee46262801ea09f15fa1f1513a05c5971",
"sha.js": "2.4.11",
"stream": "0.0.2",

View file

@ -300,7 +300,11 @@ export class AzureActiveDirectoryService {
return Promise.all(this._tokens.map(token => this.convertToSession(token)));
}
async getSessions(scopes: string[]): Promise<vscode.AuthenticationSession[]> {
async getSessions(scopes?: string[]): Promise<vscode.AuthenticationSession[]> {
if (!scopes) {
return this.sessions;
}
const orderedScopes = scopes.sort().join(' ');
const matchingTokens = this._tokens.filter(token => token.scope === orderedScopes);
return Promise.all(matchingTokens.map(token => this.convertToSession(token)));

View file

@ -20,7 +20,6 @@ export async function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(vscode.authentication.registerAuthenticationProvider('microsoft', 'Microsoft', {
onDidChangeSessions: onDidChangeSessions.event,
getAllSessions: () => Promise.resolve(loginService.sessions),
getSessions: (scopes: string[]) => loginService.getSessions(scopes),
createSession: async (scopes: string[]) => {
try {

View file

@ -126,10 +126,10 @@ mime-types@^2.1.12:
dependencies:
mime-db "1.44.0"
node-fetch@^2.6.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd"
integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==
node-fetch@2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
"randombytes@github:rmacfarlane/randombytes#b28d4ecee46262801ea09f15fa1f1513a05c5971":
version "2.1.0"

View file

@ -303,7 +303,7 @@
"list.activeSelectionBackground": "#08286b",
// "list.activeSelectionForeground": "",
"list.focusBackground": "#08286b",
"quickInput.list.focusBackground": "#08286b",
"list.hoverBackground": "#061940",
"list.inactiveSelectionBackground": "#152037",
"list.dropBackground": "#041D52",

View file

@ -10,7 +10,7 @@
"list.highlightForeground": "#e3b583",
"list.activeSelectionBackground": "#7c5021",
"list.hoverBackground": "#7c502166",
"list.focusBackground": "#7c5021AA",
"quickInput.list.focusBackground": "#7c5021AA",
"list.inactiveSelectionBackground": "#645342",
"pickerGroup.foreground": "#e3b583",
"pickerGroup.border": "#e3b583",

View file

@ -3,7 +3,7 @@
"colors": {
"dropdown.background": "#525252",
"list.activeSelectionBackground": "#707070",
"list.focusBackground": "#707070",
"quickInput.list.focusBackground": "#707070",
"list.inactiveSelectionBackground": "#4e4e4e",
"list.hoverBackground": "#444444",
"list.highlightForeground": "#e58520",

View file

@ -9,7 +9,7 @@
"colors": {
"dropdown.background": "#414339",
"list.activeSelectionBackground": "#75715E",
"list.focusBackground": "#414339",
"quickInput.list.focusBackground": "#414339",
"dropdown.listBackground": "#1e1f1c",
"list.inactiveSelectionBackground": "#414339",
"list.hoverBackground": "#3e3d32",

View file

@ -473,7 +473,7 @@
"pickerGroup.foreground": "#A6B39B",
"pickerGroup.border": "#749351",
"list.activeSelectionForeground": "#6c6c6c",
"list.focusBackground": "#CADEB9",
"quickInput.list.focusBackground": "#CADEB9",
"list.hoverBackground": "#e0e0e0",
"list.activeSelectionBackground": "#c4d9b1",
"list.inactiveSelectionBackground": "#d3dbcd",

View file

@ -49,7 +49,7 @@
"list.activeSelectionBackground": "#880000",
"list.inactiveSelectionBackground": "#770000",
"list.dropBackground": "#662222",
"list.focusBackground": "#660000",
"quickInput.list.focusBackground": "#660000",
"list.highlightForeground": "#ff4444",
"pickerGroup.foreground": "#cc9999",
"pickerGroup.border": "#ff000033",

View file

@ -350,7 +350,7 @@
"list.activeSelectionBackground": "#005A6F",
// "list.activeSelectionForeground": "",
"list.focusBackground": "#005A6F",
"quickInput.list.focusBackground": "#005A6F",
"list.hoverBackground": "#004454AA",
"list.inactiveSelectionBackground": "#00445488",
"list.dropBackground": "#00445488",

View file

@ -350,7 +350,7 @@
"list.activeSelectionBackground": "#DFCA88",
"list.activeSelectionForeground": "#6C6C6C",
"list.focusBackground": "#DFCA8866",
"quickInput.list.focusBackground": "#DFCA8866",
"list.hoverBackground": "#DFCA8844",
"list.inactiveSelectionBackground": "#D1CBB8",
"list.highlightForeground": "#B58900",

View file

@ -5,7 +5,7 @@
"errorForeground": "#a92049",
"input.background": "#001733",
"dropdown.background": "#001733",
"list.focusBackground": "#ffffff60",
"quickInput.list.focusBackground": "#ffffff60",
"list.activeSelectionBackground": "#ffffff60",
"list.inactiveSelectionBackground": "#ffffff40",
"list.hoverBackground": "#ffffff30",

View file

@ -116,6 +116,27 @@
}
]
}
],
"notebookProvider": [
{
"viewType": "notebookCoreTest",
"displayName": "Notebook Core Test",
"selector": [
{
"filenamePattern": "*.vsctestnb",
"excludeFileNamePattern": ""
}
]
},
{
"viewType": "notebook.nbdtest",
"displayName": "notebook.nbdtest",
"selector": [
{
"filenamePattern": "**/*.nbdtest"
}
]
}
]
},
"scripts": {

View file

@ -0,0 +1,235 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import * as vscode from 'vscode';
import * as utils from '../utils';
suite('Notebook Document', function () {
const contentProvider = new class implements vscode.NotebookContentProvider {
async openNotebook(uri: vscode.Uri, _openContext: vscode.NotebookDocumentOpenContext): Promise<vscode.NotebookData> {
return {
cells: [{ cellKind: vscode.NotebookCellKind.Code, source: uri.toString(), language: 'javascript', metadata: {}, outputs: [] }],
metadata: {}
};
}
async resolveNotebook(_document: vscode.NotebookDocument, _webview: vscode.NotebookCommunication) {
//
}
async saveNotebook(_document: vscode.NotebookDocument, _cancellation: vscode.CancellationToken) {
//
}
async saveNotebookAs(_targetResource: vscode.Uri, _document: vscode.NotebookDocument, _cancellation: vscode.CancellationToken) {
//
}
async backupNotebook(_document: vscode.NotebookDocument, _context: vscode.NotebookDocumentBackupContext, _cancellation: vscode.CancellationToken) {
return { id: '', delete() { } };
}
};
const disposables: vscode.Disposable[] = [];
suiteTeardown(async function () {
// utils.assertNoRpc();
await utils.revertAllDirty();
await utils.closeAllEditors();
utils.disposeAll(disposables);
disposables.length = 0;
for (let doc of vscode.notebook.notebookDocuments) {
assert.strictEqual(doc.isDirty, false, doc.uri.toString());
}
});
suiteSetup(function () {
disposables.push(vscode.notebook.registerNotebookContentProvider('notebook.nbdtest', contentProvider));
});
test('cannot register sample provider multiple times', function () {
assert.throws(() => {
vscode.notebook.registerNotebookContentProvider('notebook.nbdtest', contentProvider);
});
});
test('cannot open unknown types', async function () {
try {
await vscode.notebook.openNotebookDocument(vscode.Uri.parse('some:///thing.notTypeKnown'));
assert.ok(false);
} catch {
assert.ok(true);
}
});
test('document basics', async function () {
const uri = await utils.createRandomFile(undefined, undefined, '.nbdtest');
const notebook = await vscode.notebook.openNotebookDocument(uri);
assert.strictEqual(notebook.uri.toString(), uri.toString());
assert.strictEqual(notebook.isDirty, false);
assert.strictEqual(notebook.isUntitled, false);
assert.strictEqual(notebook.cells.length, 1);
assert.strictEqual(notebook.viewType, 'notebook.nbdtest');
});
test('notebook open/close, notebook ready when cell-document open event is fired', async function () {
const uri = await utils.createRandomFile(undefined, undefined, '.nbdtest');
let didHappen = false;
const p = utils.asPromise(vscode.workspace.onDidOpenTextDocument).then(doc => {
if (doc.uri.scheme !== 'vscode-notebook-cell') {
return;
}
const notebook = vscode.notebook.notebookDocuments.find(notebook => {
const cell = notebook.cells.find(cell => cell.document === doc);
return Boolean(cell);
});
assert.ok(notebook, `notebook for cell ${doc.uri} NOT found`);
didHappen = true;
});
await vscode.notebook.openNotebookDocument(uri);
await p;
assert.strictEqual(didHappen, true);
});
test('notebook open/close, all cell-documents are ready', async function () {
const uri = await utils.createRandomFile(undefined, undefined, '.nbdtest');
const p = utils.asPromise(vscode.notebook.onDidOpenNotebookDocument).then(notebook => {
for (let cell of notebook.cells) {
const doc = vscode.workspace.textDocuments.find(doc => doc.uri.toString() === cell.uri.toString());
assert.ok(doc);
assert.strictEqual(doc.notebook === notebook, true);
assert.strictEqual(doc === cell.document, true);
assert.strictEqual(doc?.languageId, cell.language);
assert.strictEqual(doc?.isDirty, false);
assert.strictEqual(doc?.isClosed, false);
}
});
await vscode.notebook.openNotebookDocument(uri);
await p;
});
test('workspace edit API (replaceCells)', async function () {
const uri = await utils.createRandomFile(undefined, undefined, '.nbdtest');
const document = await vscode.notebook.openNotebookDocument(uri);
assert.strictEqual(document.cells.length, 1);
// inserting two new cells
{
const edit = new vscode.WorkspaceEdit();
edit.replaceNotebookCells(document.uri, 0, 0, [{
cellKind: vscode.NotebookCellKind.Markdown,
language: 'markdown',
metadata: undefined,
outputs: [],
source: 'new_markdown'
}, {
cellKind: vscode.NotebookCellKind.Code,
language: 'fooLang',
metadata: undefined,
outputs: [],
source: 'new_code'
}]);
const success = await vscode.workspace.applyEdit(edit);
assert.strictEqual(success, true);
}
assert.strictEqual(document.cells.length, 3);
assert.strictEqual(document.cells[0].document.getText(), 'new_markdown');
assert.strictEqual(document.cells[1].document.getText(), 'new_code');
// deleting cell 1 and 3
{
const edit = new vscode.WorkspaceEdit();
edit.replaceNotebookCells(document.uri, 0, 1, []);
edit.replaceNotebookCells(document.uri, 2, 3, []);
const success = await vscode.workspace.applyEdit(edit);
assert.strictEqual(success, true);
}
assert.strictEqual(document.cells.length, 1);
assert.strictEqual(document.cells[0].document.getText(), 'new_code');
// replacing all cells
{
const edit = new vscode.WorkspaceEdit();
edit.replaceNotebookCells(document.uri, 0, 1, [{
cellKind: vscode.NotebookCellKind.Markdown,
language: 'markdown',
metadata: undefined,
outputs: [],
source: 'new2_markdown'
}, {
cellKind: vscode.NotebookCellKind.Code,
language: 'fooLang',
metadata: undefined,
outputs: [],
source: 'new2_code'
}]);
const success = await vscode.workspace.applyEdit(edit);
assert.strictEqual(success, true);
}
assert.strictEqual(document.cells.length, 2);
assert.strictEqual(document.cells[0].document.getText(), 'new2_markdown');
assert.strictEqual(document.cells[1].document.getText(), 'new2_code');
// remove all cells
{
const edit = new vscode.WorkspaceEdit();
edit.replaceNotebookCells(document.uri, 0, document.cells.length, []);
const success = await vscode.workspace.applyEdit(edit);
assert.strictEqual(success, true);
}
assert.strictEqual(document.cells.length, 0);
});
test('workspace edit API (replaceCells, event)', async function () {
const uri = await utils.createRandomFile(undefined, undefined, '.nbdtest');
const document = await vscode.notebook.openNotebookDocument(uri);
assert.strictEqual(document.cells.length, 1);
const edit = new vscode.WorkspaceEdit();
edit.replaceNotebookCells(document.uri, 0, 0, [{
cellKind: vscode.NotebookCellKind.Markdown,
language: 'markdown',
metadata: undefined,
outputs: [],
source: 'new_markdown'
}, {
cellKind: vscode.NotebookCellKind.Code,
language: 'fooLang',
metadata: undefined,
outputs: [],
source: 'new_code'
}]);
const event = utils.asPromise<vscode.NotebookCellsChangeEvent>(vscode.notebook.onDidChangeNotebookCells);
const success = await vscode.workspace.applyEdit(edit);
assert.strictEqual(success, true);
const data = await event;
// check document
assert.strictEqual(document.cells.length, 3);
assert.strictEqual(document.cells[0].document.getText(), 'new_markdown');
assert.strictEqual(document.cells[1].document.getText(), 'new_code');
// check event data
assert.strictEqual(data.document === document, true);
assert.strictEqual(data.changes.length, 1);
assert.strictEqual(data.changes[0].deletedCount, 0);
assert.strictEqual(data.changes[0].deletedItems.length, 0);
assert.strictEqual(data.changes[0].items.length, 2);
assert.strictEqual(data.changes[0].items[0], document.cells[0]);
assert.strictEqual(data.changes[0].items[1], document.cells[1]);
});
});

View file

@ -6,52 +6,13 @@
import 'mocha';
import * as assert from 'assert';
import * as vscode from 'vscode';
import { createRandomFile } from './utils';
export function timeoutAsync(n: number): Promise<void> {
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, n);
});
}
export function once<T>(event: vscode.Event<T>): vscode.Event<T> {
return (listener: any, thisArgs = null, disposables?: any) => {
// we need this, in case the event fires during the listener call
let didFire = false;
let result: vscode.Disposable;
result = event(e => {
if (didFire) {
return;
} else if (result) {
result.dispose();
} else {
didFire = true;
}
return listener.call(thisArgs, e);
}, null, disposables);
if (didFire) {
result.dispose();
}
return result;
};
}
async function getEventOncePromise<T>(event: vscode.Event<T>): Promise<T> {
return new Promise<T>((resolve, _reject) => {
once(event)((result: T) => resolve(result));
});
}
import { createRandomFile, asPromise, disposeAll, closeAllEditors, revertAllDirty } from '../utils';
// Since `workbench.action.splitEditor` command does await properly
// Notebook editor/document events are not guaranteed to be sent to the ext host when promise resolves
// The workaround here is waiting for the first visible notebook editor change event.
async function splitEditor() {
const once = getEventOncePromise(vscode.window.onDidChangeVisibleNotebookEditors);
const once = asPromise(vscode.window.onDidChangeVisibleNotebookEditors);
await vscode.commands.executeCommand('workbench.action.splitEditor');
await once;
}
@ -100,7 +61,7 @@ async function updateNotebookMetadata(uri: vscode.Uri, newMetadata: vscode.Noteb
}
async function withEvent<T>(event: vscode.Event<T>, callback: (e: Promise<T>) => Promise<void>) {
const e = getEventOncePromise<T>(event);
const e = asPromise<T>(event);
await callback(e);
}
@ -112,7 +73,146 @@ function assertInitalState() {
// assert.strictEqual(vscode.notebook.visibleNotebookEditors.length, 0);
}
suite('Notebook API tests', () => {
suite('Notebook API tests', function () {
const disposables: vscode.Disposable[] = [];
suiteTeardown(async function () {
await revertAllDirty();
await closeAllEditors();
disposeAll(disposables);
disposables.length = 0;
});
suiteSetup(function () {
disposables.push(vscode.notebook.registerNotebookContentProvider('notebookCoreTest', {
openNotebook: async (_resource: vscode.Uri): Promise<vscode.NotebookData> => {
if (/.*empty\-.*\.vsctestnb$/.test(_resource.path)) {
return {
metadata: {},
cells: []
};
}
const dto: vscode.NotebookData = {
metadata: {
custom: { testMetadata: false }
},
cells: [
{
source: 'test',
language: 'typescript',
cellKind: vscode.NotebookCellKind.Code,
outputs: [],
metadata: {
custom: { testCellMetadata: 123 }
}
}
]
};
return dto;
},
resolveNotebook: async (_document: vscode.NotebookDocument) => {
return;
},
saveNotebook: async (_document: vscode.NotebookDocument, _cancellation: vscode.CancellationToken) => {
return;
},
saveNotebookAs: async (_targetResource: vscode.Uri, _document: vscode.NotebookDocument, _cancellation: vscode.CancellationToken) => {
return;
},
backupNotebook: async (_document: vscode.NotebookDocument, _context: vscode.NotebookDocumentBackupContext, _cancellation: vscode.CancellationToken) => {
return {
id: '1',
delete: () => { }
};
}
}));
const kernel: vscode.NotebookKernel = {
id: 'mainKernel',
label: 'Notebook Test Kernel',
isPreferred: true,
supportedLanguages: ['typescript'],
executeAllCells: async (_document: vscode.NotebookDocument) => {
const edit = new vscode.WorkspaceEdit();
edit.replaceNotebookCellOutput(_document.uri, 0, [new vscode.NotebookCellOutput([
new vscode.NotebookCellOutputItem('text/plain', ['my output'], undefined)
])]);
return vscode.workspace.applyEdit(edit);
},
cancelAllCellsExecution: async (_document: vscode.NotebookDocument) => { },
executeCell: async (document: vscode.NotebookDocument, cell: vscode.NotebookCell | undefined) => {
if (!cell) {
cell = document.cells[0];
}
if (document.uri.path.endsWith('customRenderer.vsctestnb')) {
const edit = new vscode.WorkspaceEdit();
edit.replaceNotebookCellOutput(document.uri, cell.index, [new vscode.NotebookCellOutput([
new vscode.NotebookCellOutputItem('text/custom', ['test'], undefined)
])]);
return vscode.workspace.applyEdit(edit);
}
const edit = new vscode.WorkspaceEdit();
// const previousOutputs = cell.outputs;
edit.replaceNotebookCellOutput(document.uri, cell.index, [new vscode.NotebookCellOutput([
new vscode.NotebookCellOutputItem('text/plain', ['my output'], undefined)
])]);
return vscode.workspace.applyEdit(edit);
},
cancelCellExecution: async (_document: vscode.NotebookDocument, _cell: vscode.NotebookCell) => { }
};
const kernel2: vscode.NotebookKernel = {
id: 'secondaryKernel',
label: 'Notebook Secondary Test Kernel',
isPreferred: false,
supportedLanguages: ['typescript'],
executeAllCells: async (_document: vscode.NotebookDocument) => {
const edit = new vscode.WorkspaceEdit();
edit.replaceNotebookCellOutput(_document.uri, 0, [new vscode.NotebookCellOutput([
new vscode.NotebookCellOutputItem('text/plain', ['my second output'], undefined)
])]);
return vscode.workspace.applyEdit(edit);
},
cancelAllCellsExecution: async (_document: vscode.NotebookDocument) => { },
executeCell: async (document: vscode.NotebookDocument, cell: vscode.NotebookCell | undefined) => {
if (!cell) {
cell = document.cells[0];
}
const edit = new vscode.WorkspaceEdit();
if (document.uri.path.endsWith('customRenderer.vsctestnb')) {
edit.replaceNotebookCellOutput(document.uri, cell.index, [new vscode.NotebookCellOutput([
new vscode.NotebookCellOutputItem('text/custom', ['test 2'], undefined)
])]);
} else {
edit.replaceNotebookCellOutput(document.uri, cell.index, [new vscode.NotebookCellOutput([
new vscode.NotebookCellOutputItem('text/plain', ['my second output'], undefined)
])]);
}
return vscode.workspace.applyEdit(edit);
},
cancelCellExecution: async (_document: vscode.NotebookDocument, _cell: vscode.NotebookCell) => { }
};
disposables.push(vscode.notebook.registerNotebookKernelProvider({ filenamePattern: '*.vsctestnb' }, {
provideKernels: async () => {
return [kernel, kernel2];
}
}));
});
// test.only('crash', async function () {
// for (let i = 0; i < 200; i++) {
// let resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
@ -141,60 +241,20 @@ suite('Notebook API tests', () => {
test('document open/close event', async function () {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const firstDocumentOpen = getEventOncePromise(vscode.notebook.onDidOpenNotebookDocument);
const resource = await createRandomFile('', undefined, '.vsctestnb');
const firstDocumentOpen = asPromise(vscode.notebook.onDidOpenNotebookDocument);
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
await firstDocumentOpen;
const firstDocumentClose = getEventOncePromise(vscode.notebook.onDidCloseNotebookDocument);
const firstDocumentClose = asPromise(vscode.notebook.onDidCloseNotebookDocument);
await vscode.commands.executeCommand('workbench.action.closeAllEditors');
await firstDocumentClose;
});
test('notebook open/close, all cell-documents are ready', async function () {
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const p = getEventOncePromise(vscode.notebook.onDidOpenNotebookDocument).then(notebook => {
for (let cell of notebook.cells) {
const doc = vscode.workspace.textDocuments.find(doc => doc.uri.toString() === cell.uri.toString());
assert.ok(doc);
assert.strictEqual(doc === cell.document, true);
assert.strictEqual(doc?.languageId, cell.language);
assert.strictEqual(doc?.isDirty, false);
assert.strictEqual(doc?.isClosed, false);
}
});
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
await p;
await vscode.commands.executeCommand('workbench.action.closeAllEditors');
});
test('notebook open/close, notebook ready when cell-document open event is fired', async function () {
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
let didHappen = false;
const p = getEventOncePromise(vscode.workspace.onDidOpenTextDocument).then(doc => {
if (doc.uri.scheme !== 'vscode-notebook-cell') {
return;
}
const notebook = vscode.notebook.notebookDocuments.find(notebook => {
const cell = notebook.cells.find(cell => cell.document === doc);
return Boolean(cell);
});
assert.ok(notebook, `notebook for cell ${doc.uri} NOT found`);
didHappen = true;
});
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
await p;
assert.strictEqual(didHappen, true);
await vscode.commands.executeCommand('workbench.action.closeAllEditors');
});
test('shared document in notebook editors', async function () {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const resource = await createRandomFile('', undefined, '.vsctestnb');
let counter = 0;
const disposables: vscode.Disposable[] = [];
disposables.push(vscode.notebook.onDidOpenNotebookDocument(() => {
@ -217,12 +277,12 @@ suite('Notebook API tests', () => {
test('editor open/close event', async function () {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const firstEditorOpen = getEventOncePromise(vscode.window.onDidChangeVisibleNotebookEditors);
const resource = await createRandomFile('', undefined, '.vsctestnb');
const firstEditorOpen = asPromise(vscode.window.onDidChangeVisibleNotebookEditors);
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
await firstEditorOpen;
const firstEditorClose = getEventOncePromise(vscode.window.onDidChangeVisibleNotebookEditors);
const firstEditorClose = asPromise(vscode.window.onDidChangeVisibleNotebookEditors);
await vscode.commands.executeCommand('workbench.action.closeAllEditors');
await firstEditorClose;
});
@ -230,7 +290,7 @@ suite('Notebook API tests', () => {
test('editor open/close event 2', async function () {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const resource = await createRandomFile('', undefined, '.vsctestnb');
let count = 0;
const disposables: vscode.Disposable[] = [];
disposables.push(vscode.window.onDidChangeVisibleNotebookEditors(() => {
@ -250,10 +310,10 @@ suite('Notebook API tests', () => {
test('editor editing event 2', async function () {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const resource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
const cellsChangeEvent = getEventOncePromise<vscode.NotebookCellsChangeEvent>(vscode.notebook.onDidChangeNotebookCells);
const cellsChangeEvent = asPromise<vscode.NotebookCellsChangeEvent>(vscode.notebook.onDidChangeNotebookCells);
await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
const cellChangeEventRet = await cellsChangeEvent;
assert.strictEqual(cellChangeEventRet.document, vscode.window.activeNotebookEditor?.document);
@ -269,7 +329,7 @@ suite('Notebook API tests', () => {
const secondCell = vscode.window.activeNotebookEditor!.document.cells[1];
const moveCellEvent = getEventOncePromise<vscode.NotebookCellsChangeEvent>(vscode.notebook.onDidChangeNotebookCells);
const moveCellEvent = asPromise<vscode.NotebookCellsChangeEvent>(vscode.notebook.onDidChangeNotebookCells);
await vscode.commands.executeCommand('notebook.cell.moveUp');
const moveCellEventRet = await moveCellEvent;
assert.deepStrictEqual(moveCellEventRet, {
@ -290,7 +350,7 @@ suite('Notebook API tests', () => {
]
});
const cellOutputChange = getEventOncePromise<vscode.NotebookCellOutputsChangeEvent>(vscode.notebook.onDidChangeCellOutputs);
const cellOutputChange = asPromise<vscode.NotebookCellOutputsChangeEvent>(vscode.notebook.onDidChangeCellOutputs);
await vscode.commands.executeCommand('notebook.cell.execute');
const cellOutputsAddedRet = await cellOutputChange;
assert.deepStrictEqual(cellOutputsAddedRet, {
@ -299,7 +359,7 @@ suite('Notebook API tests', () => {
});
assert.strictEqual(cellOutputsAddedRet.cells[0].outputs.length, 1);
const cellOutputClear = getEventOncePromise<vscode.NotebookCellOutputsChangeEvent>(vscode.notebook.onDidChangeCellOutputs);
const cellOutputClear = asPromise<vscode.NotebookCellOutputsChangeEvent>(vscode.notebook.onDidChangeCellOutputs);
await vscode.commands.executeCommand('notebook.cell.clearOutputs');
const cellOutputsCleardRet = await cellOutputClear;
assert.deepStrictEqual(cellOutputsCleardRet, {
@ -323,7 +383,7 @@ suite('Notebook API tests', () => {
test('editor move cell event', async function () {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const resource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove');
@ -331,7 +391,7 @@ suite('Notebook API tests', () => {
const activeCell = vscode.window.activeNotebookEditor!.selection;
assert.strictEqual(vscode.window.activeNotebookEditor!.document.cells.indexOf(activeCell!), 0);
const moveChange = getEventOncePromise(vscode.notebook.onDidChangeNotebookCells);
const moveChange = asPromise(vscode.notebook.onDidChangeNotebookCells);
await vscode.commands.executeCommand('notebook.cell.moveDown');
const ret = await moveChange;
assert.deepStrictEqual(ret, {
@ -365,7 +425,7 @@ suite('Notebook API tests', () => {
test('notebook editor active/visible', async function () {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const resource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
const firstEditor = vscode.window.activeNotebookEditor;
assert.strictEqual(firstEditor && vscode.window.visibleNotebookEditors.indexOf(firstEditor) >= 0, true);
@ -377,7 +437,7 @@ suite('Notebook API tests', () => {
assert.strictEqual(firstEditor && vscode.window.visibleNotebookEditors.indexOf(firstEditor) >= 0, true);
assert.strictEqual(vscode.window.visibleNotebookEditors.length, 2);
const untitledEditorChange = getEventOncePromise(vscode.window.onDidChangeActiveNotebookEditor);
const untitledEditorChange = asPromise(vscode.window.onDidChangeActiveNotebookEditor);
await vscode.commands.executeCommand('workbench.action.files.newUntitledFile');
await untitledEditorChange;
assert.strictEqual(firstEditor && vscode.window.visibleNotebookEditors.indexOf(firstEditor) >= 0, true);
@ -386,7 +446,7 @@ suite('Notebook API tests', () => {
assert.notStrictEqual(secondEditor, vscode.window.activeNotebookEditor);
assert.strictEqual(vscode.window.visibleNotebookEditors.length, 1);
const activeEditorClose = getEventOncePromise(vscode.window.onDidChangeActiveNotebookEditor);
const activeEditorClose = asPromise(vscode.window.onDidChangeActiveNotebookEditor);
await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
await activeEditorClose;
assert.strictEqual(secondEditor, vscode.window.activeNotebookEditor);
@ -399,12 +459,12 @@ suite('Notebook API tests', () => {
test('notebook active editor change', async function () {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const firstEditorOpen = getEventOncePromise(vscode.window.onDidChangeActiveNotebookEditor);
const resource = await createRandomFile('', undefined, '.vsctestnb');
const firstEditorOpen = asPromise(vscode.window.onDidChangeActiveNotebookEditor);
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
await firstEditorOpen;
const firstEditorDeactivate = getEventOncePromise(vscode.window.onDidChangeActiveNotebookEditor);
const firstEditorDeactivate = asPromise(vscode.window.onDidChangeActiveNotebookEditor);
await vscode.commands.executeCommand('workbench.action.splitEditor');
await firstEditorDeactivate;
@ -413,10 +473,10 @@ suite('Notebook API tests', () => {
test('edit API (replaceCells)', async function () {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const resource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
const cellsChangeEvent = getEventOncePromise<vscode.NotebookCellsChangeEvent>(vscode.notebook.onDidChangeNotebookCells);
const cellsChangeEvent = asPromise<vscode.NotebookCellsChangeEvent>(vscode.notebook.onDidChangeNotebookCells);
await vscode.window.activeNotebookEditor!.edit(editBuilder => {
editBuilder.replaceCells(1, 0, [{ cellKind: vscode.NotebookCellKind.Code, language: 'javascript', source: 'test 2', outputs: [], metadata: undefined }]);
});
@ -434,7 +494,7 @@ suite('Notebook API tests', () => {
test('edit API (replaceOutput, USE NotebookCellOutput-type)', async function () {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const resource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
await vscode.window.activeNotebookEditor!.edit(editBuilder => {
@ -463,7 +523,7 @@ suite('Notebook API tests', () => {
test('edit API (replaceOutput)', async function () {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const resource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
await vscode.window.activeNotebookEditor!.edit(editBuilder => {
@ -485,10 +545,10 @@ suite('Notebook API tests', () => {
test('edit API (replaceOutput, event)', async function () {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const resource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
const outputChangeEvent = getEventOncePromise<vscode.NotebookCellOutputsChangeEvent>(vscode.notebook.onDidChangeCellOutputs);
const outputChangeEvent = asPromise<vscode.NotebookCellOutputsChangeEvent>(vscode.notebook.onDidChangeCellOutputs);
await vscode.window.activeNotebookEditor!.edit(editBuilder => {
editBuilder.replaceCellOutput(0, [new vscode.NotebookCellOutput([
new vscode.NotebookCellOutputItem('foo', 'bar')
@ -511,7 +571,7 @@ suite('Notebook API tests', () => {
test('edit API (replaceMetadata)', async function () {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const resource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
await vscode.window.activeNotebookEditor!.edit(editBuilder => {
@ -530,10 +590,10 @@ suite('Notebook API tests', () => {
test('edit API (replaceMetadata, event)', async function () {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const resource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
const event = getEventOncePromise<vscode.NotebookCellMetadataChangeEvent>(vscode.notebook.onDidChangeCellMetadata);
const event = asPromise<vscode.NotebookCellMetadataChangeEvent>(vscode.notebook.onDidChangeCellMetadata);
await vscode.window.activeNotebookEditor!.edit(editBuilder => {
editBuilder.replaceCellMetadata(0, { inputCollapsed: true, executionOrder: 17 });
@ -548,141 +608,13 @@ suite('Notebook API tests', () => {
await saveFileAndCloseAll(resource);
});
test('workspace edit API (replaceCells)', async function () {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
const { document } = vscode.window.activeNotebookEditor!;
assert.strictEqual(document.cells.length, 1);
// inserting two new cells
{
const edit = new vscode.WorkspaceEdit();
edit.replaceNotebookCells(document.uri, 0, 0, [{
cellKind: vscode.NotebookCellKind.Markdown,
language: 'markdown',
metadata: undefined,
outputs: [],
source: 'new_markdown'
}, {
cellKind: vscode.NotebookCellKind.Code,
language: 'fooLang',
metadata: undefined,
outputs: [],
source: 'new_code'
}]);
const success = await vscode.workspace.applyEdit(edit);
assert.strictEqual(success, true);
}
assert.strictEqual(document.cells.length, 3);
assert.strictEqual(document.cells[0].document.getText(), 'new_markdown');
assert.strictEqual(document.cells[1].document.getText(), 'new_code');
// deleting cell 1 and 3
{
const edit = new vscode.WorkspaceEdit();
edit.replaceNotebookCells(document.uri, 0, 1, []);
edit.replaceNotebookCells(document.uri, 2, 3, []);
const success = await vscode.workspace.applyEdit(edit);
assert.strictEqual(success, true);
}
assert.strictEqual(document.cells.length, 1);
assert.strictEqual(document.cells[0].document.getText(), 'new_code');
// replacing all cells
{
const edit = new vscode.WorkspaceEdit();
edit.replaceNotebookCells(document.uri, 0, 1, [{
cellKind: vscode.NotebookCellKind.Markdown,
language: 'markdown',
metadata: undefined,
outputs: [],
source: 'new2_markdown'
}, {
cellKind: vscode.NotebookCellKind.Code,
language: 'fooLang',
metadata: undefined,
outputs: [],
source: 'new2_code'
}]);
const success = await vscode.workspace.applyEdit(edit);
assert.strictEqual(success, true);
}
assert.strictEqual(document.cells.length, 2);
assert.strictEqual(document.cells[0].document.getText(), 'new2_markdown');
assert.strictEqual(document.cells[1].document.getText(), 'new2_code');
// remove all cells
{
const edit = new vscode.WorkspaceEdit();
edit.replaceNotebookCells(document.uri, 0, document.cells.length, []);
const success = await vscode.workspace.applyEdit(edit);
assert.strictEqual(success, true);
}
assert.strictEqual(document.cells.length, 0);
await saveFileAndCloseAll(resource);
});
test('workspace edit API (replaceCells, event)', async function () {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
const { document } = vscode.window.activeNotebookEditor!;
assert.strictEqual(document.cells.length, 1);
const edit = new vscode.WorkspaceEdit();
edit.replaceNotebookCells(document.uri, 0, 0, [{
cellKind: vscode.NotebookCellKind.Markdown,
language: 'markdown',
metadata: undefined,
outputs: [],
source: 'new_markdown'
}, {
cellKind: vscode.NotebookCellKind.Code,
language: 'fooLang',
metadata: undefined,
outputs: [],
source: 'new_code'
}]);
const event = getEventOncePromise<vscode.NotebookCellsChangeEvent>(vscode.notebook.onDidChangeNotebookCells);
const success = await vscode.workspace.applyEdit(edit);
assert.strictEqual(success, true);
const data = await event;
// check document
assert.strictEqual(document.cells.length, 3);
assert.strictEqual(document.cells[0].document.getText(), 'new_markdown');
assert.strictEqual(document.cells[1].document.getText(), 'new_code');
// check event data
assert.strictEqual(data.document === document, true);
assert.strictEqual(data.changes.length, 1);
assert.strictEqual(data.changes[0].deletedCount, 0);
assert.strictEqual(data.changes[0].deletedItems.length, 0);
assert.strictEqual(data.changes[0].items.length, 2);
assert.strictEqual(data.changes[0].items[0], document.cells[0]);
assert.strictEqual(data.changes[0].items[1], document.cells[1]);
await saveFileAndCloseAll(resource);
});
test('edit API batch edits', async function () {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const resource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
const cellsChangeEvent = getEventOncePromise<vscode.NotebookCellsChangeEvent>(vscode.notebook.onDidChangeNotebookCells);
const cellMetadataChangeEvent = getEventOncePromise<vscode.NotebookCellMetadataChangeEvent>(vscode.notebook.onDidChangeCellMetadata);
const cellsChangeEvent = asPromise<vscode.NotebookCellsChangeEvent>(vscode.notebook.onDidChangeNotebookCells);
const cellMetadataChangeEvent = asPromise<vscode.NotebookCellMetadataChangeEvent>(vscode.notebook.onDidChangeCellMetadata);
const version = vscode.window.activeNotebookEditor!.document.version;
await vscode.window.activeNotebookEditor!.edit(editBuilder => {
editBuilder.replaceCells(1, 0, [{ cellKind: vscode.NotebookCellKind.Code, language: 'javascript', source: 'test 2', outputs: [], metadata: undefined }]);
@ -697,11 +629,11 @@ suite('Notebook API tests', () => {
test('edit API batch edits undo/redo', async function () {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const resource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
const cellsChangeEvent = getEventOncePromise<vscode.NotebookCellsChangeEvent>(vscode.notebook.onDidChangeNotebookCells);
const cellMetadataChangeEvent = getEventOncePromise<vscode.NotebookCellMetadataChangeEvent>(vscode.notebook.onDidChangeCellMetadata);
const cellsChangeEvent = asPromise<vscode.NotebookCellsChangeEvent>(vscode.notebook.onDidChangeNotebookCells);
const cellMetadataChangeEvent = asPromise<vscode.NotebookCellMetadataChangeEvent>(vscode.notebook.onDidChangeCellMetadata);
const version = vscode.window.activeNotebookEditor!.document.version;
await vscode.window.activeNotebookEditor!.edit(editBuilder => {
editBuilder.replaceCells(1, 0, [{ cellKind: vscode.NotebookCellKind.Code, language: 'javascript', source: 'test 2', outputs: [], metadata: undefined }]);
@ -724,7 +656,7 @@ suite('Notebook API tests', () => {
test('initialzation should not emit cell change events.', async function () {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const resource = await createRandomFile('', undefined, '.vsctestnb');
let count = 0;
const disposables: vscode.Disposable[] = [];
@ -739,12 +671,13 @@ suite('Notebook API tests', () => {
await saveFileAndCloseAll(resource);
});
});
// });
// suite('notebook workflow', () => {
suite('notebook workflow', () => {
test('notebook open', async function () {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const resource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first');
assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), 'test');
@ -766,7 +699,7 @@ suite('notebook workflow', () => {
test('notebook cell actions', async function () {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const resource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first');
assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), 'test');
@ -840,7 +773,7 @@ suite('notebook workflow', () => {
test('notebook join cells', async function () {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const resource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first');
assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), 'test');
@ -852,7 +785,7 @@ suite('notebook workflow', () => {
edit.insert(vscode.window.activeNotebookEditor!.selection!.uri, new vscode.Position(0, 0), 'var abc = 0;');
await vscode.workspace.applyEdit(edit);
const cellsChangeEvent = getEventOncePromise<vscode.NotebookCellsChangeEvent>(vscode.notebook.onDidChangeNotebookCells);
const cellsChangeEvent = asPromise<vscode.NotebookCellsChangeEvent>(vscode.notebook.onDidChangeNotebookCells);
await vscode.commands.executeCommand('notebook.cell.joinAbove');
await cellsChangeEvent;
@ -864,7 +797,7 @@ suite('notebook workflow', () => {
test('move cells will not recreate cells in ExtHost', async function () {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const resource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove');
@ -884,7 +817,7 @@ suite('notebook workflow', () => {
});
// test.only('document metadata is respected', async function () {
// const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
// const resource = await createRandomFile('', undefined, '.vsctestnb');
// await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
// assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first');
@ -909,7 +842,7 @@ suite('notebook workflow', () => {
test('cell runnable metadata is respected', async () => {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const resource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first');
const editor = vscode.window.activeNotebookEditor!;
@ -918,14 +851,14 @@ suite('notebook workflow', () => {
const cell = editor.document.cells[0];
assert.strictEqual(cell.outputs.length, 0);
let metadataChangeEvent = getEventOncePromise<vscode.NotebookCellMetadataChangeEvent>(vscode.notebook.onDidChangeCellMetadata);
let metadataChangeEvent = asPromise<vscode.NotebookCellMetadataChangeEvent>(vscode.notebook.onDidChangeCellMetadata);
await updateCellMetadata(resource, cell, { ...cell.metadata, runnable: false });
await metadataChangeEvent;
await vscode.commands.executeCommand('notebook.cell.execute');
assert.strictEqual(cell.outputs.length, 0, 'should not execute'); // not runnable, didn't work
metadataChangeEvent = getEventOncePromise<vscode.NotebookCellMetadataChangeEvent>(vscode.notebook.onDidChangeCellMetadata);
metadataChangeEvent = asPromise<vscode.NotebookCellMetadataChangeEvent>(vscode.notebook.onDidChangeCellMetadata);
await updateCellMetadata(resource, cell, { ...cell.metadata, runnable: true });
await metadataChangeEvent;
@ -938,7 +871,7 @@ suite('notebook workflow', () => {
test('document runnable metadata is respected', async () => {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const resource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first');
const editor = vscode.window.activeNotebookEditor!;
@ -973,7 +906,7 @@ suite('notebook workflow', () => {
// TODO@rebornix this is wrong, `await vscode.commands.executeCommand('notebook.execute');` doesn't wait until the workspace edit is applied
test.skip('cell execute command takes arguments', async () => {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const resource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first');
const editor = vscode.window.activeNotebookEditor!;
@ -988,7 +921,7 @@ suite('notebook workflow', () => {
test('cell execute command takes arguments 2', async () => {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const resource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first');
const editor = vscode.window.activeNotebookEditor!;
@ -1011,7 +944,7 @@ suite('notebook workflow', () => {
assert.strictEqual(cell.outputs.length, 0, 'should clear');
});
const secondResource = await createRandomFile('', undefined, 'second', '.vsctestnb');
const secondResource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', secondResource, 'notebookCoreTest');
await withEvent<vscode.NotebookCellOutputsChangeEvent>(vscode.notebook.onDidChangeCellOutputs, async (event) => {
@ -1029,13 +962,13 @@ suite('notebook workflow', () => {
test('document execute command takes arguments', async () => {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const resource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first');
const editor = vscode.window.activeNotebookEditor!;
const cell = editor.document.cells[0];
const metadataChangeEvent = getEventOncePromise<vscode.NotebookDocumentMetadataChangeEvent>(vscode.notebook.onDidChangeNotebookDocumentMetadata);
const metadataChangeEvent = asPromise<vscode.NotebookDocumentMetadataChangeEvent>(vscode.notebook.onDidChangeNotebookDocumentMetadata);
updateNotebookMetadata(editor.document.uri, { ...editor.document.metadata, runnable: true });
await metadataChangeEvent;
assert.strictEqual(editor.document.metadata.runnable, true);
@ -1046,12 +979,12 @@ suite('notebook workflow', () => {
assert.strictEqual(cell.outputs.length, 1, 'should execute'); // runnable, it worked
});
const clearChangeEvent = getEventOncePromise<vscode.NotebookCellOutputsChangeEvent>(vscode.notebook.onDidChangeCellOutputs);
const clearChangeEvent = asPromise<vscode.NotebookCellOutputsChangeEvent>(vscode.notebook.onDidChangeCellOutputs);
await vscode.commands.executeCommand('notebook.cell.clearOutputs');
await clearChangeEvent;
assert.strictEqual(cell.outputs.length, 0, 'should clear');
const secondResource = await createRandomFile('', undefined, 'second', '.vsctestnb');
const secondResource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', secondResource, 'notebookCoreTest');
await withEvent<vscode.NotebookCellOutputsChangeEvent>(vscode.notebook.onDidChangeCellOutputs, async (event) => {
@ -1069,13 +1002,13 @@ suite('notebook workflow', () => {
test('cell execute and select kernel', async () => {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const resource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first');
const editor = vscode.window.activeNotebookEditor!;
const cell = editor.document.cells[0];
const metadataChangeEvent = getEventOncePromise<vscode.NotebookDocumentMetadataChangeEvent>(vscode.notebook.onDidChangeNotebookDocumentMetadata);
const metadataChangeEvent = asPromise<vscode.NotebookDocumentMetadataChangeEvent>(vscode.notebook.onDidChangeNotebookDocumentMetadata);
updateNotebookMetadata(editor.document.uri, { ...editor.document.metadata, runnable: true });
await metadataChangeEvent;
@ -1087,7 +1020,7 @@ suite('notebook workflow', () => {
'my output'
]);
await vscode.commands.executeCommand('notebook.selectKernel', { extension: 'vscode.vscode-notebook-tests', id: 'secondaryKernel' });
await vscode.commands.executeCommand('notebook.selectKernel', { extension: 'vscode.vscode-api-tests', id: 'secondaryKernel' });
await vscode.commands.executeCommand('notebook.cell.execute');
assert.strictEqual(cell.outputs.length, 1, 'should execute'); // runnable, it worked
assert.strictEqual(cell.outputs[0].outputs.length, 1);
@ -1099,12 +1032,12 @@ suite('notebook workflow', () => {
await vscode.commands.executeCommand('workbench.action.files.save');
await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
});
});
// });
suite('notebook dirty state', () => {
// suite('notebook dirty state', () => {
test('notebook open', async function () {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const resource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first');
assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), 'test');
@ -1133,12 +1066,12 @@ suite('notebook dirty state', () => {
await saveFileAndCloseAll(resource);
});
});
// });
suite('notebook undo redo', () => {
// suite('notebook undo redo', () => {
test('notebook open', async function () {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const resource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first');
assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), 'test');
@ -1181,7 +1114,7 @@ suite('notebook undo redo', () => {
// test.skip('execute and then undo redo', async function () {
// assertInitalState();
// const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
// const resource = await createRandomFile('', undefined, '.vsctestnb');
// await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
// const cellsChangeEvent = getEventOncePromise<vscode.NotebookCellsChangeEvent>(vscode.notebook.onDidChangeNotebookCells);
@ -1242,11 +1175,11 @@ suite('notebook undo redo', () => {
// await saveFileAndCloseAll(resource);
// });
});
// });
suite('notebook working copy', () => {
// suite('notebook working copy', () => {
// test('notebook revert on close', async function () {
// const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
// const resource = await createRandomFile('', undefined, '.vsctestnb');
// await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
// await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
// assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), '');
@ -1267,7 +1200,7 @@ suite('notebook working copy', () => {
// });
// test('notebook revert', async function () {
// const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
// const resource = await createRandomFile('', undefined, '.vsctestnb');
// await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
// await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
// assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), '');
@ -1288,7 +1221,7 @@ suite('notebook working copy', () => {
test('multiple tabs: dirty + clean', async function () {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const resource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), '');
@ -1298,7 +1231,7 @@ suite('notebook working copy', () => {
edit.insert(vscode.window.activeNotebookEditor!.selection!.uri, new vscode.Position(0, 0), 'var abc = 0;');
await vscode.workspace.applyEdit(edit);
const secondResource = await createRandomFile('', undefined, 'second', '.vsctestnb');
const secondResource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', secondResource, 'notebookCoreTest');
await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
@ -1314,7 +1247,7 @@ suite('notebook working copy', () => {
test('multiple tabs: two dirty tabs and switching', async function () {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const resource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), '');
@ -1324,7 +1257,7 @@ suite('notebook working copy', () => {
edit.insert(vscode.window.activeNotebookEditor!.selection!.uri, new vscode.Position(0, 0), 'var abc = 0;');
await vscode.workspace.applyEdit(edit);
const secondResource = await createRandomFile('', undefined, 'second', '.vsctestnb');
const secondResource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', secondResource, 'notebookCoreTest');
await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), '');
@ -1353,7 +1286,7 @@ suite('notebook working copy', () => {
test('multiple tabs: different editors with same document', async function () {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const resource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
const firstNotebookEditor = vscode.window.activeNotebookEditor;
assert.strictEqual(firstNotebookEditor !== undefined, true, 'notebook first');
@ -1375,12 +1308,12 @@ suite('notebook working copy', () => {
// await vscode.commands.executeCommand('workbench.action.files.saveAll');
// await vscode.commands.executeCommand('workbench.action.closeAllEditors');
});
});
// });
suite('metadata', () => {
// suite('metadata', () => {
test('custom metadata should be supported', async function () {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const resource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first');
assert.strictEqual(vscode.window.activeNotebookEditor!.document.metadata.custom!['testMetadata'] as boolean, false);
@ -1394,7 +1327,7 @@ suite('metadata', () => {
// TODO@rebornix skip as it crashes the process all the time
test.skip('custom metadata should be supported 2', async function () {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const resource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first');
assert.strictEqual(vscode.window.activeNotebookEditor!.document.metadata.custom!['testMetadata'] as boolean, false);
@ -1409,9 +1342,9 @@ suite('metadata', () => {
await saveFileAndCloseAll(resource);
});
});
// });
suite('regression', () => {
// suite('regression', () => {
// test('microsoft/vscode-github-issue-notebooks#26. Insert template cell in the new empty document', async function () {
// assertInitalState();
// await vscode.commands.executeCommand('workbench.action.files.newUntitledFile', { "viewType": "notebookCoreTest" });
@ -1423,7 +1356,7 @@ suite('regression', () => {
test('#106657. Opening a notebook from markers view is broken ', async function () {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const resource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
const document = vscode.window.activeNotebookEditor?.document!;
@ -1441,7 +1374,7 @@ suite('regression', () => {
test.skip('Cannot open notebook from cell-uri with vscode.open-command', async function () {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const resource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
const document = vscode.window.activeNotebookEditor?.document!;
@ -1459,7 +1392,7 @@ suite('regression', () => {
test('#97830, #97764. Support switch to other editor types', async function () {
assertInitalState();
const resource = await createRandomFile('', undefined, 'empty', '.vsctestnb');
const resource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
const edit = new vscode.WorkspaceEdit();
@ -1469,12 +1402,9 @@ suite('regression', () => {
assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first');
assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), 'var abc = 0;');
// todo@jrieken enforce a kernel (how) and test that its language is picked
// assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.language, 'typescript');
// no kernel -> no default language
assert.strictEqual(vscode.window.activeNotebookEditor!.kernel, undefined);
assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.language, 'plaintext');
assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.language, 'typescript');
await vscode.commands.executeCommand('vscode.openWith', resource, 'default');
assert.strictEqual(vscode.window.activeTextEditor?.document.uri.path, resource.path);
@ -1485,7 +1415,7 @@ suite('regression', () => {
// open text editor, pin, and then open a notebook
test('#96105 - dirty editors', async function () {
assertInitalState();
const resource = await createRandomFile('', undefined, 'empty', '.vsctestnb');
const resource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'default');
const edit = new vscode.WorkspaceEdit();
edit.insert(resource, new vscode.Position(0, 0), 'var abc = 0;');
@ -1509,7 +1439,7 @@ suite('regression', () => {
test('#102423 - copy/paste shares the same text buffer', async function () {
assertInitalState();
const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
const resource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
let activeCell = vscode.window.activeNotebookEditor!.selection;
@ -1530,16 +1460,16 @@ suite('regression', () => {
await vscode.commands.executeCommand('workbench.action.closeAllEditors');
});
});
// });
suite('webview', () => {
// suite('webview', () => {
// for web, `asWebUri` gets `https`?
// test('asWebviewUri', async function () {
// if (vscode.env.uiKind === vscode.UIKind.Web) {
// return;
// }
// const resource = await createRandomFile('', undefined, 'first', '.vsctestnb');
// const resource = await createRandomFile('', undefined, '.vsctestnb');
// await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
// assert.strictEqual(vscode.window.activeNotebookEditor !== undefined, true, 'notebook first');
// const uri = vscode.window.activeNotebookEditor!.asWebviewUri(vscode.Uri.file('./hello.png'));

View file

@ -150,6 +150,13 @@ suite('vscode API - window', () => {
});
test('active editor not always correct... #49125', async function () {
if (!window.state.focused) {
// no focus!
this.skip();
return;
}
if (process.env['BUILD_SOURCEVERSION']) {
this.skip();
return;

View file

@ -17,7 +17,7 @@ vscode.workspace.registerFileSystemProvider(testFs.scheme, testFs, { isCaseSensi
export async function createRandomFile(contents = '', dir: vscode.Uri | undefined = undefined, ext = ''): Promise<vscode.Uri> {
let fakeFile: vscode.Uri;
if (dir) {
assert.equal(dir.scheme, testFs.scheme);
assert.strictEqual(dir.scheme, testFs.scheme);
fakeFile = dir.with({ path: dir.path + '/' + rndName() + ext });
} else {
fakeFile = vscode.Uri.parse(`${testFs.scheme}:/${rndName() + ext}`);
@ -48,6 +48,10 @@ export function closeAllEditors(): Thenable<any> {
return vscode.commands.executeCommand('workbench.action.closeAllEditors');
}
export function saveAllEditors(): Thenable<any> {
return vscode.commands.executeCommand('workbench.action.files.saveAll');
}
export async function revertAllDirty(): Promise<void> {
return vscode.commands.executeCommand('_workbench.revertAllDirty');
}
@ -117,3 +121,19 @@ export function assertNoRpcFromEntry(entry: [obj: any, name: string]) {
assert.strictEqual(rpcPaths.length, 0, rpcPaths.join('\n'));
assert.strictEqual(proxyPaths.length, 0, proxyPaths.join('\n')); // happens...
}
export async function asPromise<T>(event: vscode.Event<T>, timeout = 5000): Promise<T> {
return new Promise<T>((resolve, reject) => {
const handle = setTimeout(() => {
sub.dispose();
reject(new Error('asPromise TIMEOUT reached'));
}, timeout);
const sub = event(e => {
clearTimeout(handle);
sub.dispose();
resolve(e);
});
});
}

View file

@ -34,16 +34,6 @@
}
],
"notebookProvider": [
{
"viewType": "notebookCoreTest",
"displayName": "Notebook Core Test",
"selector": [
{
"filenamePattern": "*.vsctestnb",
"excludeFileNamePattern": ""
}
]
},
{
"viewType": "notebookSmokeTest",
"displayName": "Notebook Smoke Test",

View file

@ -9,128 +9,6 @@ import { smokeTestActivate } from './notebookSmokeTestMain';
export function activate(context: vscode.ExtensionContext): any {
smokeTestActivate(context);
context.subscriptions.push(vscode.notebook.registerNotebookContentProvider('notebookCoreTest', {
openNotebook: async (_resource: vscode.Uri): Promise<vscode.NotebookData> => {
if (/.*empty\-.*\.vsctestnb$/.test(_resource.path)) {
return {
metadata: {},
cells: []
};
}
const dto: vscode.NotebookData = {
metadata: {
custom: { testMetadata: false }
},
cells: [
{
source: 'test',
language: 'typescript',
cellKind: vscode.NotebookCellKind.Code,
outputs: [],
metadata: {
custom: { testCellMetadata: 123 }
}
}
]
};
return dto;
},
resolveNotebook: async (_document: vscode.NotebookDocument) => {
return;
},
saveNotebook: async (_document: vscode.NotebookDocument, _cancellation: vscode.CancellationToken) => {
return;
},
saveNotebookAs: async (_targetResource: vscode.Uri, _document: vscode.NotebookDocument, _cancellation: vscode.CancellationToken) => {
return;
},
backupNotebook: async (_document: vscode.NotebookDocument, _context: vscode.NotebookDocumentBackupContext, _cancellation: vscode.CancellationToken) => {
return {
id: '1',
delete: () => { }
};
}
}));
const kernel: vscode.NotebookKernel = {
id: 'mainKernel',
label: 'Notebook Test Kernel',
isPreferred: true,
supportedLanguages: ['typescript'],
executeAllCells: async (_document: vscode.NotebookDocument) => {
const edit = new vscode.WorkspaceEdit();
edit.replaceNotebookCellOutput(_document.uri, 0, [new vscode.NotebookCellOutput([
new vscode.NotebookCellOutputItem('text/plain', ['my output'], undefined)
])]);
return vscode.workspace.applyEdit(edit);
},
cancelAllCellsExecution: async (_document: vscode.NotebookDocument) => { },
executeCell: async (document: vscode.NotebookDocument, cell: vscode.NotebookCell | undefined) => {
if (!cell) {
cell = document.cells[0];
}
if (document.uri.path.endsWith('customRenderer.vsctestnb')) {
const edit = new vscode.WorkspaceEdit();
edit.replaceNotebookCellOutput(document.uri, cell.index, [new vscode.NotebookCellOutput([
new vscode.NotebookCellOutputItem('text/custom', ['test'], undefined)
])]);
return vscode.workspace.applyEdit(edit);
}
const edit = new vscode.WorkspaceEdit();
// const previousOutputs = cell.outputs;
edit.replaceNotebookCellOutput(document.uri, cell.index, [new vscode.NotebookCellOutput([
new vscode.NotebookCellOutputItem('text/plain', ['my output'], undefined)
])]);
return vscode.workspace.applyEdit(edit);
},
cancelCellExecution: async (_document: vscode.NotebookDocument, _cell: vscode.NotebookCell) => { }
};
const kernel2: vscode.NotebookKernel = {
id: 'secondaryKernel',
label: 'Notebook Secondary Test Kernel',
isPreferred: false,
supportedLanguages: ['typescript'],
executeAllCells: async (_document: vscode.NotebookDocument) => {
const edit = new vscode.WorkspaceEdit();
edit.replaceNotebookCellOutput(_document.uri, 0, [new vscode.NotebookCellOutput([
new vscode.NotebookCellOutputItem('text/plain', ['my second output'], undefined)
])]);
return vscode.workspace.applyEdit(edit);
},
cancelAllCellsExecution: async (_document: vscode.NotebookDocument) => { },
executeCell: async (document: vscode.NotebookDocument, cell: vscode.NotebookCell | undefined) => {
if (!cell) {
cell = document.cells[0];
}
const edit = new vscode.WorkspaceEdit();
if (document.uri.path.endsWith('customRenderer.vsctestnb')) {
edit.replaceNotebookCellOutput(document.uri, cell.index, [new vscode.NotebookCellOutput([
new vscode.NotebookCellOutputItem('text/custom', ['test 2'], undefined)
])]);
} else {
edit.replaceNotebookCellOutput(document.uri, cell.index, [new vscode.NotebookCellOutput([
new vscode.NotebookCellOutputItem('text/plain', ['my second output'], undefined)
])]);
}
return vscode.workspace.applyEdit(edit);
},
cancelCellExecution: async (_document: vscode.NotebookDocument, _cell: vscode.NotebookCell) => { }
};
context.subscriptions.push(vscode.notebook.registerNotebookKernelProvider({ filenamePattern: '*.vsctestnb' }, {
provideKernels: async () => {
return [kernel, kernel2];
}
}));
}

View file

@ -5,7 +5,7 @@
"dataFolderName": ".vscode-oss",
"win32MutexName": "vscodeoss",
"licenseName": "MIT",
"licenseUrl": "https://github.com/microsoft/vscode/blob/master/LICENSE.txt",
"licenseUrl": "https://github.com/microsoft/vscode/blob/main/LICENSE.txt",
"win32DirName": "Microsoft Code OSS",
"win32NameVersion": "Microsoft Code OSS",
"win32RegValueName": "CodeOSS",

View file

@ -14,7 +14,7 @@ header="// Type definitions for Visual Studio Code ${1}
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
* See https://github.com/microsoft/vscode/blob/master/LICENSE.txt for license information.
* See https://github.com/microsoft/vscode/blob/main/LICENSE.txt for license information.
*--------------------------------------------------------------------------------------------*/
/**

View file

@ -9,14 +9,14 @@
const perf = require('./vs/base/common/performance');
perf.mark('code/didStartMain');
const lp = require('./vs/base/node/languagePacks');
const path = require('path');
const fs = require('fs');
const os = require('os');
const { getNLSConfiguration } = require('./vs/base/node/languagePacks');
const bootstrap = require('./bootstrap');
const bootstrapNode = require('./bootstrap-node');
const paths = require('./paths');
/** @type {Partial<import('./vs/platform/product/common/productService').IProductConfiguration> & { applicationName: string}} */
const { getDefaultUserDataPath } = require('./vs/base/node/userDataPath');
/** @type {Partial<import('./vs/platform/product/common/productService').IProductConfiguration>} */
const product = require('../product.json');
const { app, protocol, crashReporter } = require('electron');
@ -84,7 +84,7 @@ let nlsConfigurationPromise = undefined;
const metaDataFile = path.join(__dirname, 'nls.metadata.json');
const locale = getUserDefinedLocale(argvConfig);
if (locale) {
nlsConfigurationPromise = lp.getNLSConfiguration(product.commit, userDataPath, metaDataFile, locale);
nlsConfigurationPromise = getNLSConfiguration(product.commit, userDataPath, metaDataFile, locale);
}
// Load our code once ready
@ -405,7 +405,7 @@ function getUserDataPath(cliArgs) {
return path.join(portable.portableDataPath, 'user-data');
}
return path.resolve(cliArgs['user-data-dir'] || paths.getDefaultUserDataPath());
return path.resolve(cliArgs['user-data-dir'] || getDefaultUserDataPath());
}
/**
@ -560,7 +560,7 @@ async function resolveNlsConfiguration() {
// See above the comment about the loader and case sensitiviness
appLocale = appLocale.toLowerCase();
nlsConfiguration = await lp.getNLSConfiguration(product.commit, userDataPath, metaDataFile, appLocale);
nlsConfiguration = await getNLSConfiguration(product.commit, userDataPath, metaDataFile, appLocale);
if (!nlsConfiguration) {
nlsConfiguration = { locale: appLocale, availableLanguages: {} };
}

View file

@ -1,48 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
//@ts-check
'use strict';
const pkg = require('../package.json');
const path = require('path');
const os = require('os');
/**
* @returns {string}
*/
function getDefaultUserDataPath() {
// Support global VSCODE_APPDATA environment variable
let appDataPath = process.env['VSCODE_APPDATA'];
// Otherwise check per platform
if (!appDataPath) {
switch (process.platform) {
case 'win32':
appDataPath = process.env['APPDATA'];
if (!appDataPath) {
const userProfile = process.env['USERPROFILE'];
if (typeof userProfile !== 'string') {
throw new Error('Windows: Unexpected undefined %USERPROFILE% environment variable');
}
appDataPath = path.join(userProfile, 'AppData', 'Roaming');
}
break;
case 'darwin':
appDataPath = path.join(os.homedir(), 'Library', 'Application Support');
break;
case 'linux':
appDataPath = process.env['XDG_CONFIG_HOME'] || path.join(os.homedir(), '.config');
break;
default:
throw new Error('Platform not supported');
}
}
return path.join(appDataPath, pkg.name);
}
exports.getDefaultUserDataPath = getDefaultUserDataPath;

View file

@ -383,5 +383,16 @@ export function renderMarkdownAsPlaintext(markdown: IMarkdownString) {
if (value.length > 100_000) {
value = `${value.substr(0, 100_000)}`;
}
return sanitizeRenderedMarkdown({ isTrusted: false }, marked.parse(value, { renderer })).toString();
const unescapeInfo = new Map<string, string>([
['&quot;', '"'],
['&amp;', '&'],
['&#39;', '\''],
['&lt;', '<'],
['&gt;', '>'],
]);
const html = marked.parse(value, { renderer }).replace(/&(#\d+|[a-zA-Z]+);/g, m => unescapeInfo.get(m) ?? m);
return sanitizeRenderedMarkdown({ isTrusted: false }, html).toString();
}

View file

@ -14,7 +14,7 @@ import { EventType as TouchEventType, Gesture } from 'vs/base/browser/touch';
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
import { DataTransfers } from 'vs/base/browser/dnd';
import { isFirefox } from 'vs/base/browser/browser';
import { $, addDisposableListener, append, EventHelper, EventLike, EventType, removeTabIndexAndUpdateFocus } from 'vs/base/browser/dom';
import { $, addDisposableListener, append, EventHelper, EventLike, EventType } from 'vs/base/browser/dom';
export interface IBaseActionViewItemOptions {
draggable?: boolean;
@ -181,9 +181,9 @@ export class BaseActionViewItem extends Disposable implements IActionViewItem {
}
}
setFocusable(): void {
setFocusable(focusable: boolean): void {
if (this.element) {
this.element.tabIndex = 0;
this.element.tabIndex = focusable ? 0 : -1;
}
}
@ -288,9 +288,9 @@ export class ActionViewItem extends BaseActionViewItem {
}
}
setFocusable(): void {
setFocusable(focusable: boolean): void {
if (this.label) {
this.label.tabIndex = 0;
this.label.tabIndex = focusable ? 0 : -1;
}
}
@ -356,7 +356,6 @@ export class ActionViewItem extends BaseActionViewItem {
if (this.label) {
this.label.setAttribute('aria-disabled', 'true');
this.label.classList.add('disabled');
removeTabIndexAndUpdateFocus(this.label);
}
if (this.element) {

View file

@ -35,7 +35,8 @@ export interface IActionBarOptions {
readonly triggerKeys?: ActionTrigger;
readonly allowContextMenu?: boolean;
readonly preventLoopNavigation?: boolean;
readonly ignoreOrientationForPreviousAndNextKey?: boolean;
// Pass true here when the up and down keys should not be eaten up by the ActionBar. For example, when an ActionBar is in the list.
readonly respectOrientationForPreviousAndNextKey?: boolean;
}
export interface IActionOptions extends IActionViewItemOptions {
@ -63,6 +64,8 @@ export class ActionBar extends Disposable implements IActionRunner {
// Trigger Key Tracking
private triggerKeyDown: boolean = false;
private focusable: boolean = true;
// Elements
domNode: HTMLElement;
protected actionsList: HTMLElement;
@ -117,22 +120,22 @@ export class ActionBar extends Disposable implements IActionRunner {
switch (this._orientation) {
case ActionsOrientation.HORIZONTAL:
previousKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.LeftArrow, KeyCode.UpArrow] : [KeyCode.LeftArrow];
nextKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.RightArrow, KeyCode.DownArrow] : [KeyCode.RightArrow];
previousKeys = this.options.respectOrientationForPreviousAndNextKey ? [KeyCode.LeftArrow] : [KeyCode.LeftArrow, KeyCode.UpArrow];
nextKeys = this.options.respectOrientationForPreviousAndNextKey ? [KeyCode.RightArrow] : [KeyCode.RightArrow, KeyCode.DownArrow];
break;
case ActionsOrientation.HORIZONTAL_REVERSE:
previousKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.RightArrow, KeyCode.DownArrow] : [KeyCode.RightArrow];
nextKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.LeftArrow, KeyCode.UpArrow] : [KeyCode.LeftArrow];
previousKeys = this.options.respectOrientationForPreviousAndNextKey ? [KeyCode.RightArrow] : [KeyCode.RightArrow, KeyCode.DownArrow];
nextKeys = this.options.respectOrientationForPreviousAndNextKey ? [KeyCode.LeftArrow] : [KeyCode.LeftArrow, KeyCode.UpArrow];
this.domNode.className += ' reverse';
break;
case ActionsOrientation.VERTICAL:
previousKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.LeftArrow, KeyCode.UpArrow] : [KeyCode.UpArrow];
nextKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.RightArrow, KeyCode.DownArrow] : [KeyCode.DownArrow];
previousKeys = this.options.respectOrientationForPreviousAndNextKey ? [KeyCode.UpArrow] : [KeyCode.LeftArrow, KeyCode.UpArrow];
nextKeys = this.options.respectOrientationForPreviousAndNextKey ? [KeyCode.DownArrow] : [KeyCode.RightArrow, KeyCode.DownArrow];
this.domNode.className += ' vertical';
break;
case ActionsOrientation.VERTICAL_REVERSE:
previousKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.RightArrow, KeyCode.DownArrow] : [KeyCode.DownArrow];
nextKeys = this.options.ignoreOrientationForPreviousAndNextKey ? [KeyCode.LeftArrow, KeyCode.UpArrow] : [KeyCode.UpArrow];
previousKeys = this.options.respectOrientationForPreviousAndNextKey ? [KeyCode.DownArrow] : [KeyCode.RightArrow, KeyCode.DownArrow];
nextKeys = this.options.respectOrientationForPreviousAndNextKey ? [KeyCode.UpArrow] : [KeyCode.LeftArrow, KeyCode.UpArrow];
this.domNode.className += ' vertical reverse';
break;
}
@ -219,6 +222,25 @@ export class ActionBar extends Disposable implements IActionRunner {
}
}
// Some action bars should not be focusable at times
// When an action bar is not focusable make sure to make all the elements inside it not focusable
// When an action bar is focusable again, make sure the first item can be focused
setFocusable(focusable: boolean): void {
this.focusable = focusable;
if (this.focusable) {
const first = this.viewItems.find(vi => vi instanceof BaseActionViewItem);
if (first instanceof BaseActionViewItem) {
first.setFocusable(true);
}
} else {
this.viewItems.forEach(vi => {
if (vi instanceof BaseActionViewItem) {
vi.setFocusable(false);
}
});
}
}
private isTriggerKeyEvent(event: StandardKeyboardEvent): boolean {
let ret = false;
this._triggerKeys.keys.forEach(keyCode => {
@ -297,9 +319,9 @@ export class ActionBar extends Disposable implements IActionRunner {
item.setActionContext(this.context);
item.render(actionViewItemElement);
if (this.viewItems.every(i => !i.isEnabled()) && item instanceof BaseActionViewItem && item.isEnabled()) {
if (this.focusable && this.viewItems.length === 0 && item instanceof BaseActionViewItem) {
// We need to allow for the first enabled item to be focused on using tab navigation #106441
item.setFocusable();
item.setFocusable(true);
}
if (index === null || index < 0 || index >= this.actionsList.children.length) {

View file

@ -12,7 +12,7 @@ import { Event as BaseEvent, Emitter } from 'vs/base/common/event';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { Gesture, EventType as TouchEventType } from 'vs/base/browser/touch';
import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels';
import { addDisposableListener, IFocusTracker, EventType, EventHelper, trackFocus, reset, removeTabIndexAndUpdateFocus } from 'vs/base/browser/dom';
import { addDisposableListener, IFocusTracker, EventType, EventHelper, trackFocus, reset } from 'vs/base/browser/dom';
import { IContextMenuProvider } from 'vs/base/browser/contextmenu';
import { Action, IAction, IActionRunner } from 'vs/base/common/actions';
import { CSSIcon, Codicon } from 'vs/base/common/codicons';
@ -214,7 +214,6 @@ export class Button extends Disposable implements IButton {
} else {
this._element.classList.add('disabled');
this._element.setAttribute('aria-disabled', String(true));
removeTabIndexAndUpdateFocus(this._element);
}
}

View file

@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./checkbox';
import * as DOM from 'vs/base/browser/dom';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { Widget } from 'vs/base/browser/ui/widget';
import { Color } from 'vs/base/common/color';
@ -76,6 +75,26 @@ export class CheckboxActionViewItem extends BaseActionViewItem {
}
}
focus(): void {
if (this.checkbox) {
this.checkbox.domNode.tabIndex = 0;
this.checkbox.focus();
}
}
blur(): void {
if (this.checkbox) {
this.checkbox.domNode.tabIndex = -1;
this.checkbox.domNode.blur();
}
}
setFocusable(focusable: boolean): void {
if (this.checkbox) {
this.checkbox.domNode.tabIndex = focusable ? 0 : -1;
}
}
dispose(): void {
this.disposables.dispose();
super.dispose();
@ -191,12 +210,10 @@ export class Checkbox extends Widget {
}
enable(): void {
this.domNode.tabIndex = 0;
this.domNode.setAttribute('aria-disabled', String(false));
}
disable(): void {
DOM.removeTabIndexAndUpdateFocus(this.domNode);
this.domNode.setAttribute('aria-disabled', String(true));
}
}

View file

@ -179,5 +179,4 @@ export class ActionWithDropdownActionViewItem extends ActionViewItem {
this.dropdownMenuActionViewItem.render(this.element);
}
}
}

View file

@ -761,6 +761,11 @@ export class DefaultStyleController implements IStyleController {
`);
}
if (styles.listInactiveFocusForeground) {
content.push(`.monaco-list${suffix} .monaco-list-row.focused { color: ${styles.listInactiveFocusForeground}; }`);
content.push(`.monaco-list${suffix} .monaco-list-row.focused:hover { color: ${styles.listInactiveFocusForeground}; }`); // overwrite :hover style in this case!
}
if (styles.listInactiveFocusBackground) {
content.push(`.monaco-list${suffix} .monaco-list-row.focused { background-color: ${styles.listInactiveFocusBackground}; }`);
content.push(`.monaco-list${suffix} .monaco-list-row.focused:hover { background-color: ${styles.listInactiveFocusBackground}; }`); // overwrite :hover style in this case!
@ -776,7 +781,7 @@ export class DefaultStyleController implements IStyleController {
}
if (styles.listHoverBackground) {
content.push(`.monaco-list${suffix}:not(.drop-target) .monaco-list-row:hover:not(.selected):not(.focused) { background-color: ${styles.listHoverBackground}; }`);
content.push(`.monaco-list${suffix}:not(.drop-target) .monaco-list-row:hover:not(.selected):not(.focused) { background-color: ${styles.listHoverBackground}; }`);
}
if (styles.listHoverForeground) {
@ -867,6 +872,7 @@ export interface IListStyles {
listFocusAndSelectionForeground?: Color;
listInactiveSelectionBackground?: Color;
listInactiveSelectionForeground?: Color;
listInactiveFocusForeground?: Color;
listInactiveFocusBackground?: Color;
listHoverBackground?: Color;
listHoverForeground?: Color;

View file

@ -8,7 +8,7 @@ import * as strings from 'vs/base/common/strings';
import { IActionRunner, IAction, SubmenuAction, Separator, IActionViewItemProvider, EmptySubmenuAction } from 'vs/base/common/actions';
import { ActionBar, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
import { ResolvedKeybinding, KeyCode } from 'vs/base/common/keyCodes';
import { EventType, EventHelper, EventLike, removeTabIndexAndUpdateFocus, isAncestor, addDisposableListener, append, $, clearNode, createStyleSheet, isInShadowDOM, getActiveElement, Dimension, IDomNodePagePosition } from 'vs/base/browser/dom';
import { EventType, EventHelper, EventLike, isAncestor, addDisposableListener, append, $, clearNode, createStyleSheet, isInShadowDOM, getActiveElement, Dimension, IDomNodePagePosition } from 'vs/base/browser/dom';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { RunOnceScheduler } from 'vs/base/common/async';
import { DisposableStore } from 'vs/base/common/lifecycle';
@ -86,6 +86,7 @@ export class Menu extends ActionBar {
context: options.context,
actionRunner: options.actionRunner,
ariaLabel: options.ariaLabel,
respectOrientationForPreviousAndNextKey: true,
triggerKeys: { keys: [KeyCode.Enter, ...(isMacintosh || isLinux ? [KeyCode.Space] : [])], keyDown: true }
});
@ -617,20 +618,23 @@ class BaseMenuActionViewItem extends BaseActionViewItem {
if (this.getAction().enabled) {
if (this.element) {
this.element.classList.remove('disabled');
this.element.removeAttribute('aria-disabled');
}
if (this.item) {
this.item.classList.remove('disabled');
this.item.removeAttribute('aria-disabled');
this.item.tabIndex = 0;
}
} else {
if (this.element) {
this.element.classList.add('disabled');
this.element.setAttribute('aria-disabled', 'true');
}
if (this.item) {
this.item.classList.add('disabled');
removeTabIndexAndUpdateFocus(this.item);
this.item.setAttribute('aria-disabled', 'true');
}
}
}

View file

@ -42,7 +42,7 @@
}
.menubar .menubar-menu-items-holder {
position: absolute;
position: fixed;
left: 0px;
opacity: 1;
z-index: 2000;

View file

@ -398,13 +398,21 @@ export class Sash extends Disposable {
}));
}
private static onMouseEnter(sash: Sash): void {
private static onMouseEnter(sash: Sash, fromLinkedSash: boolean = false): void {
sash.hoverDelayer.trigger(() => sash.el.classList.add('hover'));
if (!fromLinkedSash && sash.linkedSash) {
Sash.onMouseEnter(sash.linkedSash, true);
}
}
private static onMouseLeave(sash: Sash): void {
private static onMouseLeave(sash: Sash, fromLinkedSash: boolean = false): void {
sash.hoverDelayer.cancel();
sash.el.classList.remove('hover');
if (!fromLinkedSash && sash.linkedSash) {
Sash.onMouseLeave(sash.linkedSash, true);
}
}
layout(): void {

View file

@ -736,8 +736,8 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi
this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.Enter).on(e => this.onEnter(e), this));
this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.Escape).on(e => this.onEscape(e), this));
this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.UpArrow).on(this.onUpArrow, this));
this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.DownArrow).on(this.onDownArrow, this));
this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.UpArrow).on(e => this.onUpArrow(e), this));
this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.DownArrow).on(e => this.onDownArrow(e), this));
this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.PageDown).on(this.onPageDown, this));
this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.PageUp).on(this.onPageUp, this));
this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.Home).on(this.onHome, this));
@ -916,8 +916,9 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi
}
// List navigation - have to handle a disabled option (jump over)
private onDownArrow(): void {
private onDownArrow(e: StandardKeyboardEvent): void {
if (this.selected < this.options.length - 1) {
dom.EventHelper.stop(e, true);
// Skip disabled options
const nextOptionDisabled = this.options[this.selected + 1].isDisabled;
@ -937,8 +938,9 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi
}
}
private onUpArrow(): void {
private onUpArrow(e: StandardKeyboardEvent): void {
if (this.selected > 0) {
dom.EventHelper.stop(e, true);
// Skip disabled options
const previousOptionDisabled = this.options[this.selected - 1].isDisabled;
if (previousOptionDisabled && this.selected > 1) {

View file

@ -28,6 +28,7 @@ export interface IToolBarOptions {
anchorAlignmentProvider?: () => AnchorAlignment;
renderDropdownAsChildElement?: boolean;
moreIcon?: CSSIcon;
readonly respectOrientationForPreviousAndNextKey?: boolean;
}
/**
@ -63,6 +64,7 @@ export class ToolBar extends Disposable {
orientation: options.orientation,
ariaLabel: options.ariaLabel,
actionRunner: options.actionRunner,
respectOrientationForPreviousAndNextKey: options.respectOrientationForPreviousAndNextKey,
actionViewItemProvider: (action: IAction) => {
if (action.id === ToggleMenuAction.ID) {
this.toggleMenuActionViewItem = new DropdownMenuActionViewItem(

View file

@ -961,7 +961,7 @@ export interface IAbstractTreeOptionsUpdate extends ITreeRendererOptions {
readonly filterOnType?: boolean;
readonly smoothScrolling?: boolean;
readonly horizontalScrolling?: boolean;
readonly expandOnlyOnDoubleClick?: boolean;
readonly expandOnDoubleClick?: boolean;
readonly expandOnlyOnTwistieClick?: boolean | ((e: any) => boolean); // e is T
}
@ -1121,7 +1121,7 @@ class TreeNodeListMouseController<T, TFilterData, TRef> extends MouseController<
return super.onViewPointer(e);
}
if (this.tree.expandOnlyOnDoubleClick && e.browserEvent.detail !== 2 && !onTwistie) {
if (!this.tree.expandOnDoubleClick && e.browserEvent.detail === 2) {
return super.onViewPointer(e);
}
@ -1129,6 +1129,7 @@ class TreeNodeListMouseController<T, TFilterData, TRef> extends MouseController<
const model = ((this.tree as any).model as ITreeModel<T, TFilterData, TRef>); // internal
const location = model.getNodeLocation(node);
const recursive = e.browserEvent.altKey;
this.tree.setFocus([location]);
model.setCollapsed(location, undefined, recursive);
if (expandOnlyOnTwistieClick && onTwistie) {
@ -1142,7 +1143,7 @@ class TreeNodeListMouseController<T, TFilterData, TRef> extends MouseController<
protected onDoubleClick(e: IListMouseEvent<ITreeNode<T, TFilterData>>): void {
const onTwistie = (e.browserEvent.target as HTMLElement).classList.contains('monaco-tl-twistie');
if (onTwistie) {
if (onTwistie || !this.tree.expandOnDoubleClick) {
return;
}
@ -1262,8 +1263,8 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
get filterOnType(): boolean { return !!this._options.filterOnType; }
get onDidChangeTypeFilterPattern(): Event<string> { return this.typeFilterController ? this.typeFilterController.onDidChangePattern : Event.None; }
get expandOnlyOnDoubleClick(): boolean { return this._options.expandOnlyOnDoubleClick ?? false; }
get expandOnlyOnTwistieClick(): boolean | ((e: T) => boolean) { return typeof this._options.expandOnlyOnTwistieClick === 'undefined' ? false : this._options.expandOnlyOnTwistieClick; }
get expandOnDoubleClick(): boolean { return typeof this._options.expandOnDoubleClick === 'undefined' ? true : this._options.expandOnDoubleClick; }
get expandOnlyOnTwistieClick(): boolean | ((e: T) => boolean) { return typeof this._options.expandOnlyOnTwistieClick === 'undefined' ? true : this._options.expandOnlyOnTwistieClick; }
private readonly _onDidUpdateOptions = new Emitter<IAbstractTreeOptions<T, TFilterData>>();
readonly onDidUpdateOptions: Event<IAbstractTreeOptions<T, TFilterData>> = this._onDidUpdateOptions.event;

View file

@ -56,6 +56,10 @@
overflow: hidden;
}
.monaco-tl-twistie::before {
border-radius: 20px;
}
.monaco-tl-twistie.collapsed::before {
transform: rotate(-90deg);
}

View file

@ -8,7 +8,7 @@ import { URI, UriComponents } from 'vs/base/common/uri';
/**
* @returns whether the provided parameter is a JavaScript Array or not.
*/
export function isArray<T>(array: T | {}): array is T extends readonly any[] ? (unknown extends T ? never : readonly any[]) : any[] {
export function isArray(array: any): array is any[] {
return Array.isArray(array);
}

View file

@ -3,256 +3,257 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/// <reference path="../../../typings/require.d.ts" />
//@ts-check
'use strict';
/**
* @param {NodeRequire} nodeRequire
* @param {typeof import('path')} path
* @param {typeof import('fs')} fs
* @param {typeof import('../common/performance')} perf
*/
function factory(nodeRequire, path, fs, perf) {
(function () {
'use strict';
/**
* @param {string} file
* @returns {Promise<boolean>}
* @param {NodeRequire} nodeRequire
* @param {typeof import('path')} path
* @param {typeof import('fs')} fs
* @param {typeof import('../common/performance')} perf
*/
function exists(file) {
return new Promise(c => fs.exists(file, c));
}
function factory(nodeRequire, path, fs, perf) {
/**
* @param {string} file
* @returns {Promise<void>}
*/
function touch(file) {
return new Promise((c, e) => { const d = new Date(); fs.utimes(file, d, d, err => err ? e(err) : c()); });
}
/**
* @param {string} dir
* @returns {Promise<string>}
*/
function mkdirp(dir) {
return new Promise((c, e) => fs.mkdir(dir, { recursive: true }, err => (err && err.code !== 'EEXIST') ? e(err) : c(dir)));
}
/**
* @param {string} location
* @returns {Promise<void>}
*/
function rimraf(location) {
return new Promise((c, e) => fs.rmdir(location, { recursive: true }, err => (err && err.code !== 'ENOENT') ? e(err) : c()));
}
/**
* @param {string} file
* @returns {Promise<string>}
*/
function readFile(file) {
return new Promise((c, e) => fs.readFile(file, 'utf8', (err, data) => err ? e(err) : c(data)));
}
/**
* @param {string} file
* @param {string} content
* @returns {Promise<void>}
*/
function writeFile(file, content) {
return new Promise((c, e) => fs.writeFile(file, content, 'utf8', err => err ? e(err) : c()));
}
/**
* @param {string} userDataPath
* @returns {object}
*/
function getLanguagePackConfigurations(userDataPath) {
const configFile = path.join(userDataPath, 'languagepacks.json');
try {
return nodeRequire(configFile);
} catch (err) {
// Do nothing. If we can't read the file we have no
// language pack config.
/**
* @param {string} file
* @returns {Promise<boolean>}
*/
function exists(file) {
return new Promise(c => fs.exists(file, c));
}
return undefined;
}
/**
* @param {object} config
* @param {string} locale
*/
function resolveLanguagePackLocale(config, locale) {
try {
while (locale) {
if (config[locale]) {
return locale;
} else {
const index = locale.lastIndexOf('-');
if (index > 0) {
locale = locale.substring(0, index);
/**
* @param {string} file
* @returns {Promise<void>}
*/
function touch(file) {
return new Promise((c, e) => { const d = new Date(); fs.utimes(file, d, d, err => err ? e(err) : c()); });
}
/**
* @param {string} dir
* @returns {Promise<string>}
*/
function mkdirp(dir) {
return new Promise((c, e) => fs.mkdir(dir, { recursive: true }, err => (err && err.code !== 'EEXIST') ? e(err) : c(dir)));
}
/**
* @param {string} location
* @returns {Promise<void>}
*/
function rimraf(location) {
return new Promise((c, e) => fs.rmdir(location, { recursive: true }, err => (err && err.code !== 'ENOENT') ? e(err) : c()));
}
/**
* @param {string} file
* @returns {Promise<string>}
*/
function readFile(file) {
return new Promise((c, e) => fs.readFile(file, 'utf8', (err, data) => err ? e(err) : c(data)));
}
/**
* @param {string} file
* @param {string} content
* @returns {Promise<void>}
*/
function writeFile(file, content) {
return new Promise((c, e) => fs.writeFile(file, content, 'utf8', err => err ? e(err) : c()));
}
/**
* @param {string} userDataPath
* @returns {object}
*/
function getLanguagePackConfigurations(userDataPath) {
const configFile = path.join(userDataPath, 'languagepacks.json');
try {
return nodeRequire(configFile);
} catch (err) {
// Do nothing. If we can't read the file we have no
// language pack config.
}
return undefined;
}
/**
* @param {object} config
* @param {string} locale
*/
function resolveLanguagePackLocale(config, locale) {
try {
while (locale) {
if (config[locale]) {
return locale;
} else {
return undefined;
const index = locale.lastIndexOf('-');
if (index > 0) {
locale = locale.substring(0, index);
} else {
return undefined;
}
}
}
} catch (err) {
console.error('Resolving language pack configuration failed.', err);
}
} catch (err) {
console.error('Resolving language pack configuration failed.', err);
}
return undefined;
}
/**
* @param {string} commit
* @param {string} userDataPath
* @param {string} metaDataFile
* @param {string} locale
*/
function getNLSConfiguration(commit, userDataPath, metaDataFile, locale) {
if (locale === 'pseudo') {
return Promise.resolve({ locale: locale, availableLanguages: {}, pseudo: true });
return undefined;
}
if (process.env['VSCODE_DEV']) {
return Promise.resolve({ locale: locale, availableLanguages: {} });
}
// We have a built version so we have extracted nls file. Try to find
// the right file to use.
// Check if we have an English or English US locale. If so fall to default since that is our
// English translation (we don't ship *.nls.en.json files)
if (locale && (locale === 'en' || locale === 'en-us')) {
return Promise.resolve({ locale: locale, availableLanguages: {} });
}
const initialLocale = locale;
perf.mark('code/willGenerateNls');
const defaultResult = function (locale) {
perf.mark('code/didGenerateNls');
return Promise.resolve({ locale: locale, availableLanguages: {} });
};
try {
if (!commit) {
return defaultResult(initialLocale);
/**
* @param {string} commit
* @param {string} userDataPath
* @param {string} metaDataFile
* @param {string} locale
*/
function getNLSConfiguration(commit, userDataPath, metaDataFile, locale) {
if (locale === 'pseudo') {
return Promise.resolve({ locale: locale, availableLanguages: {}, pseudo: true });
}
const configs = getLanguagePackConfigurations(userDataPath);
if (!configs) {
return defaultResult(initialLocale);
if (process.env['VSCODE_DEV']) {
return Promise.resolve({ locale: locale, availableLanguages: {} });
}
locale = resolveLanguagePackLocale(configs, locale);
if (!locale) {
return defaultResult(initialLocale);
// We have a built version so we have extracted nls file. Try to find
// the right file to use.
// Check if we have an English or English US locale. If so fall to default since that is our
// English translation (we don't ship *.nls.en.json files)
if (locale && (locale === 'en' || locale === 'en-us')) {
return Promise.resolve({ locale: locale, availableLanguages: {} });
}
const packConfig = configs[locale];
let mainPack;
if (!packConfig || typeof packConfig.hash !== 'string' || !packConfig.translations || typeof (mainPack = packConfig.translations['vscode']) !== 'string') {
return defaultResult(initialLocale);
}
return exists(mainPack).then(fileExists => {
if (!fileExists) {
const initialLocale = locale;
perf.mark('code/willGenerateNls');
const defaultResult = function (locale) {
perf.mark('code/didGenerateNls');
return Promise.resolve({ locale: locale, availableLanguages: {} });
};
try {
if (!commit) {
return defaultResult(initialLocale);
}
const packId = packConfig.hash + '.' + locale;
const cacheRoot = path.join(userDataPath, 'clp', packId);
const coreLocation = path.join(cacheRoot, commit);
const translationsConfigFile = path.join(cacheRoot, 'tcf.json');
const corruptedFile = path.join(cacheRoot, 'corrupted.info');
const result = {
locale: initialLocale,
availableLanguages: { '*': locale },
_languagePackId: packId,
_translationsConfigFile: translationsConfigFile,
_cacheRoot: cacheRoot,
_resolvedLanguagePackCoreLocation: coreLocation,
_corruptedFile: corruptedFile
};
return exists(corruptedFile).then(corrupted => {
// The nls cache directory is corrupted.
let toDelete;
if (corrupted) {
toDelete = rimraf(cacheRoot);
} else {
toDelete = Promise.resolve(undefined);
const configs = getLanguagePackConfigurations(userDataPath);
if (!configs) {
return defaultResult(initialLocale);
}
locale = resolveLanguagePackLocale(configs, locale);
if (!locale) {
return defaultResult(initialLocale);
}
const packConfig = configs[locale];
let mainPack;
if (!packConfig || typeof packConfig.hash !== 'string' || !packConfig.translations || typeof (mainPack = packConfig.translations['vscode']) !== 'string') {
return defaultResult(initialLocale);
}
return exists(mainPack).then(fileExists => {
if (!fileExists) {
return defaultResult(initialLocale);
}
return toDelete.then(() => {
return exists(coreLocation).then(fileExists => {
if (fileExists) {
// We don't wait for this. No big harm if we can't touch
touch(coreLocation).catch(() => { });
perf.mark('code/didGenerateNls');
return result;
}
return mkdirp(coreLocation).then(() => {
return Promise.all([readFile(metaDataFile), readFile(mainPack)]);
}).then(values => {
const metadata = JSON.parse(values[0]);
const packData = JSON.parse(values[1]).contents;
const bundles = Object.keys(metadata.bundles);
const writes = [];
for (const bundle of bundles) {
const modules = metadata.bundles[bundle];
const target = Object.create(null);
for (const module of modules) {
const keys = metadata.keys[module];
const defaultMessages = metadata.messages[module];
const translations = packData[module];
let targetStrings;
if (translations) {
targetStrings = [];
for (let i = 0; i < keys.length; i++) {
const elem = keys[i];
const key = typeof elem === 'string' ? elem : elem.key;
let translatedMessage = translations[key];
if (translatedMessage === undefined) {
translatedMessage = defaultMessages[i];
}
targetStrings.push(translatedMessage);
}
} else {
targetStrings = defaultMessages;
}
target[module] = targetStrings;
}
writes.push(writeFile(path.join(coreLocation, bundle.replace(/\//g, '!') + '.nls.json'), JSON.stringify(target)));
const packId = packConfig.hash + '.' + locale;
const cacheRoot = path.join(userDataPath, 'clp', packId);
const coreLocation = path.join(cacheRoot, commit);
const translationsConfigFile = path.join(cacheRoot, 'tcf.json');
const corruptedFile = path.join(cacheRoot, 'corrupted.info');
const result = {
locale: initialLocale,
availableLanguages: { '*': locale },
_languagePackId: packId,
_translationsConfigFile: translationsConfigFile,
_cacheRoot: cacheRoot,
_resolvedLanguagePackCoreLocation: coreLocation,
_corruptedFile: corruptedFile
};
return exists(corruptedFile).then(corrupted => {
// The nls cache directory is corrupted.
let toDelete;
if (corrupted) {
toDelete = rimraf(cacheRoot);
} else {
toDelete = Promise.resolve(undefined);
}
return toDelete.then(() => {
return exists(coreLocation).then(fileExists => {
if (fileExists) {
// We don't wait for this. No big harm if we can't touch
touch(coreLocation).catch(() => { });
perf.mark('code/didGenerateNls');
return result;
}
writes.push(writeFile(translationsConfigFile, JSON.stringify(packConfig.translations)));
return Promise.all(writes);
}).then(() => {
perf.mark('code/didGenerateNls');
return result;
}).catch(err => {
console.error('Generating translation files failed.', err);
return defaultResult(locale);
return mkdirp(coreLocation).then(() => {
return Promise.all([readFile(metaDataFile), readFile(mainPack)]);
}).then(values => {
const metadata = JSON.parse(values[0]);
const packData = JSON.parse(values[1]).contents;
const bundles = Object.keys(metadata.bundles);
const writes = [];
for (const bundle of bundles) {
const modules = metadata.bundles[bundle];
const target = Object.create(null);
for (const module of modules) {
const keys = metadata.keys[module];
const defaultMessages = metadata.messages[module];
const translations = packData[module];
let targetStrings;
if (translations) {
targetStrings = [];
for (let i = 0; i < keys.length; i++) {
const elem = keys[i];
const key = typeof elem === 'string' ? elem : elem.key;
let translatedMessage = translations[key];
if (translatedMessage === undefined) {
translatedMessage = defaultMessages[i];
}
targetStrings.push(translatedMessage);
}
} else {
targetStrings = defaultMessages;
}
target[module] = targetStrings;
}
writes.push(writeFile(path.join(coreLocation, bundle.replace(/\//g, '!') + '.nls.json'), JSON.stringify(target)));
}
writes.push(writeFile(translationsConfigFile, JSON.stringify(packConfig.translations)));
return Promise.all(writes);
}).then(() => {
perf.mark('code/didGenerateNls');
return result;
}).catch(err => {
console.error('Generating translation files failed.', err);
return defaultResult(locale);
});
});
});
});
});
});
} catch (err) {
console.error('Generating translation files failed.', err);
return defaultResult(locale);
} catch (err) {
console.error('Generating translation files failed.', err);
return defaultResult(locale);
}
}
return {
getNLSConfiguration
};
}
return {
getNLSConfiguration
};
}
// @ts-ignore
if (typeof define === 'function') {
// amd
// @ts-ignore
define(['path', 'fs', 'vs/base/common/performance'], function (path, fs, perf) { return factory(require.__$__nodeRequire, path, fs, perf); });
} else if (typeof module === 'object' && typeof module.exports === 'object') {
const path = require('path');
const fs = require('fs');
const perf = require('../common/performance');
module.exports = factory(require, path, fs, perf);
} else {
throw new Error('Unknown context');
}
if (typeof define === 'function') {
// amd
define(['require', 'path', 'fs', 'vs/base/common/performance'], function (require, /** @type {typeof import('path')} */ path, /** @type {typeof import('fs')} */ fs, /** @type {typeof import('../common/performance')} */ perf) { return factory(require.__$__nodeRequire, path, fs, perf); });
} else if (typeof module === 'object' && typeof module.exports === 'object') {
const path = require('path');
const fs = require('fs');
const perf = require('../common/performance');
module.exports = factory(require, path, fs, perf);
} else {
throw new Error('Unknown context');
}
}());

9
src/vs/base/node/userDataPath.d.ts vendored Normal file
View file

@ -0,0 +1,9 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/**
* Returns the user data path to use.
*/
export function getDefaultUserDataPath(): string;

View file

@ -0,0 +1,72 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/// <reference path="../../../typings/require.d.ts" />
//@ts-check
(function () {
'use strict';
/**
* @param {typeof import('path')} path
* @param {typeof import('os')} os
* @param {string} productName
*/
function factory(path, os, productName) {
function getDefaultUserDataPath() {
// Support global VSCODE_APPDATA environment variable
let appDataPath = process.env['VSCODE_APPDATA'];
// Otherwise check per platform
if (!appDataPath) {
switch (process.platform) {
case 'win32':
appDataPath = process.env['APPDATA'];
if (!appDataPath) {
const userProfile = process.env['USERPROFILE'];
if (typeof userProfile !== 'string') {
throw new Error('Windows: Unexpected undefined %USERPROFILE% environment variable');
}
appDataPath = path.join(userProfile, 'AppData', 'Roaming');
}
break;
case 'darwin':
appDataPath = path.join(os.homedir(), 'Library', 'Application Support');
break;
case 'linux':
appDataPath = process.env['XDG_CONFIG_HOME'] || path.join(os.homedir(), '.config');
break;
default:
throw new Error('Platform not supported');
}
}
return path.join(appDataPath, productName);
}
return {
getDefaultUserDataPath
};
}
if (typeof define === 'function') {
define(['require', 'path', 'os', 'vs/base/common/network', 'vs/base/common/resources'], function (require, /** @type {typeof import('path')} */ path, /** @type {typeof import('os')} */ os, /** @type {typeof import('../common/network')} */ network, /** @type {typeof import("../common/resources")} */ resources) {
const rootPath = resources.dirname(network.FileAccess.asFileUri('', require));
const pkg = require.__$__nodeRequire(resources.joinPath(rootPath, 'package.json').fsPath);
return factory(path, os, pkg.name);
}); // amd
} else if (typeof module === 'object' && typeof module.exports === 'object') {
const pkg = require('../../../../package.json');
const path = require('path');
const os = require('os');
module.exports = factory(path, os, pkg.name); // commonjs
} else {
throw new Error('Unknown context');
}
}());

View file

@ -56,7 +56,7 @@ export interface IQuickInputStyles {
countBadge: ICountBadgetyles;
button: IButtonStyles;
progressBar: IProgressBarStyles;
list: IListStyles & { listInactiveFocusForeground?: Color; pickerGroupBorder?: Color; pickerGroupForeground?: Color; };
list: IListStyles & { pickerGroupBorder?: Color; pickerGroupForeground?: Color; };
}
export interface IQuickInputWidgetStyles {
@ -1706,10 +1706,6 @@ export class QuickInputController extends Disposable {
this.ui.list.style(this.styles.list);
const content: string[] = [];
if (this.styles.list.listInactiveFocusForeground) {
content.push(`.monaco-list .monaco-list-row.focused { color: ${this.styles.list.listInactiveFocusForeground}; }`);
content.push(`.monaco-list .monaco-list-row.focused:hover { color: ${this.styles.list.listInactiveFocusForeground}; }`); // overwrite :hover style in this case!
}
if (this.styles.list.pickerGroupBorder) {
content.push(`.quick-input-list .quick-input-list-entry { border-top-color: ${this.styles.list.pickerGroupBorder}; }`);
}

View file

@ -89,6 +89,8 @@ export class Storage extends Disposable implements IStorage {
private pendingDeletes = new Set<string>();
private pendingInserts = new Map<string, string>();
private pendingClose: Promise<void> | undefined = undefined;
private readonly whenFlushedCallbacks: Function[] = [];
constructor(
@ -256,10 +258,15 @@ export class Storage extends Disposable implements IStorage {
}
async close(): Promise<void> {
if (this.state === StorageState.Closed) {
return; // return if already closed
if (!this.pendingClose) {
this.pendingClose = this.doClose();
}
return this.pendingClose;
}
private async doClose(): Promise<void> {
// Update state
this.state = StorageState.Closed;

View file

@ -3,9 +3,13 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { FileAccess } from 'vs/base/common/network';
import * as assert from 'assert';
import { getDefaultUserDataPath } from 'vs/base/node/userDataPath';
const pathsPath = FileAccess.asFileUri('paths', require).fsPath;
const paths = require.__$__nodeRequire<{ getDefaultUserDataPath(): string }>(pathsPath);
suite('User data path', () => {
export const getDefaultUserDataPath = paths.getDefaultUserDataPath;
test('getDefaultUserDataPath', () => {
const path = getDefaultUserDataPath();
assert.ok(path.length > 0);
});
});

View file

@ -4,9 +4,9 @@
*--------------------------------------------------------------------------------------------*/
//@ts-check
'use strict';
(function () {
'use strict';
const bootstrap = bootstrapLib();
const bootstrapWindow = bootstrapWindowLib();
@ -38,5 +38,4 @@
}
//#endregion
}());

View file

@ -58,8 +58,7 @@ import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
import { LoggerService } from 'vs/platform/log/node/loggerService';
import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog';
import { UserDataAutoSyncService } from 'vs/platform/userDataSync/electron-sandbox/userDataAutoSyncService';
import { NativeStorageService } from 'vs/platform/storage/node/storageService';
import { GlobalStorageDatabaseChannelClient } from 'vs/platform/storage/node/storageIpc';
import { NativeStorageService2 } from 'vs/platform/storage/electron-sandbox/storageService2';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { GlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService';
import { UserDataSyncResourceEnablementService } from 'vs/platform/userDataSync/common/userDataSyncResourceEnablementService';
@ -175,8 +174,8 @@ class SharedProcessMain extends Disposable {
await configurationService.initialize();
// Storage
const storageService = new NativeStorageService(new GlobalStorageDatabaseChannelClient(mainProcessService.getChannel('storage')), logService, environmentService);
// Storage (global access only)
const storageService = new NativeStorageService2(undefined, mainProcessService, environmentService);
services.set(IStorageService, storageService);
await storageService.initialize();

View file

@ -6,9 +6,9 @@
/// <reference path="../../../../typings/require.d.ts" />
//@ts-check
'use strict';
(function () {
'use strict';
const bootstrapWindow = bootstrapWindowLib();
// Add a perf entry right from the top
@ -179,5 +179,4 @@
}
//#endregion
}());

View file

@ -58,8 +58,8 @@ import { joinPath } from 'vs/base/common/resources';
import { localize } from 'vs/nls';
import { Schemas } from 'vs/base/common/network';
import { SnapUpdateService } from 'vs/platform/update/electron-main/updateService.snap';
import { IStorageMainService, StorageMainService } from 'vs/platform/storage/node/storageMainService';
import { GlobalStorageDatabaseChannel } from 'vs/platform/storage/node/storageIpc';
import { IStorageMainService, StorageMainService } from 'vs/platform/storage/electron-main/storageMainService';
import { StorageDatabaseChannel } from 'vs/platform/storage/electron-main/storageIpc';
import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService';
import { IBackupMainService } from 'vs/platform/backup/electron-main/backup';
import { WorkspacesHistoryMainService, IWorkspacesHistoryMainService } from 'vs/platform/workspaces/electron-main/workspacesHistoryMainService';
@ -102,7 +102,7 @@ export class CodeApplication extends Disposable {
private readonly userEnv: IProcessEnvironment,
@IInstantiationService private readonly mainInstantiationService: IInstantiationService,
@ILogService private readonly logService: ILogService,
@IEnvironmentMainService private readonly environmentService: IEnvironmentMainService,
@IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService,
@ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IStateService private readonly stateService: IStateService,
@ -168,7 +168,7 @@ export class CodeApplication extends Disposable {
event.preventDefault();
});
app.on('remote-get-current-web-contents', event => {
if (this.environmentService.args.driver) {
if (this.environmentMainService.args.driver) {
return; // the driver needs access to web contents
}
@ -190,7 +190,7 @@ export class CodeApplication extends Disposable {
}
const srcUri = uri.fsPath.toLowerCase();
const rootUri = URI.file(this.environmentService.appRoot).fsPath.toLowerCase();
const rootUri = URI.file(this.environmentMainService.appRoot).fsPath.toLowerCase();
return srcUri.startsWith(rootUri + sep);
};
@ -255,7 +255,7 @@ export class CodeApplication extends Disposable {
runningTimeout = setTimeout(() => {
this.windowsMainService?.open({
context: OpenContext.DOCK /* can also be opening from finder while app is running */,
cli: this.environmentService.args,
cli: this.environmentMainService.args,
urisToOpen: macOpenFileURIs,
gotoLineMode: false,
preferNewWindow: true /* dropping on the dock or opening from finder prefers to open in a new window */
@ -328,7 +328,7 @@ export class CodeApplication extends Disposable {
args = window.config;
env = { ...process.env, ...window.config.userEnv };
} else {
args = this.environmentService.args;
args = this.environmentMainService.args;
env = process.env;
}
@ -376,7 +376,7 @@ export class CodeApplication extends Disposable {
}
}
if (typeof path !== 'string' || !isAbsolute(path) || !isEqualOrParent(path, this.environmentService.cachedLanguagesPath, !isLinux)) {
if (typeof path !== 'string' || !isAbsolute(path) || !isEqualOrParent(path, this.environmentMainService.cachedLanguagesPath, !isLinux)) {
return undefined;
}
@ -404,8 +404,8 @@ export class CodeApplication extends Disposable {
async startup(): Promise<void> {
this.logService.debug('Starting VS Code');
this.logService.debug(`from: ${this.environmentService.appRoot}`);
this.logService.debug('args:', this.environmentService.args);
this.logService.debug(`from: ${this.environmentMainService.appRoot}`);
this.logService.debug('args:', this.environmentMainService.args);
// Make sure we associate the program with the app user model id
// This will help Windows to associate the running program with
@ -448,10 +448,10 @@ export class CodeApplication extends Disposable {
const appInstantiationService = await this.initServices(machineId, sharedProcess, sharedProcessReady);
// Create driver
if (this.environmentService.driverHandle) {
const server = await serveDriver(mainProcessElectronServer, this.environmentService.driverHandle, this.environmentService, appInstantiationService);
if (this.environmentMainService.driverHandle) {
const server = await serveDriver(mainProcessElectronServer, this.environmentMainService.driverHandle, this.environmentMainService, appInstantiationService);
this.logService.info('Driver started at:', this.environmentService.driverHandle);
this.logService.info('Driver started at:', this.environmentMainService.driverHandle);
this._register(server);
}
@ -468,7 +468,7 @@ export class CodeApplication extends Disposable {
appInstantiationService.invokeFunction(accessor => this.afterWindowOpen(accessor));
// Tracing: Stop tracing after windows are ready if enabled
if (this.environmentService.args.trace) {
if (this.environmentMainService.args.trace) {
appInstantiationService.invokeFunction(accessor => this.stopTracingEventually(accessor, windows));
}
}
@ -509,7 +509,7 @@ export class CodeApplication extends Disposable {
// Spawn shared process after the first window has opened and 3s have passed
this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen).then(() => {
this._register(new RunOnceScheduler(async () => {
sharedProcess.spawn(await resolveShellEnv(this.logService, this.environmentService.args, process.env));
sharedProcess.spawn(await resolveShellEnv(this.logService, this.environmentMainService.args, process.env));
}, 3000)).schedule();
});
@ -580,23 +580,21 @@ export class CodeApplication extends Disposable {
services.set(IExtensionUrlTrustService, new SyncDescriptor(ExtensionUrlTrustService));
// Storage
const storageMainService = new StorageMainService(this.logService, this.environmentService);
services.set(IStorageMainService, storageMainService);
this.lifecycleMainService.onWillShutdown(e => e.join(storageMainService.close()));
services.set(IStorageMainService, new SyncDescriptor(StorageMainService));
// Backups
const backupMainService = new BackupMainService(this.environmentService, this.configurationService, this.logService);
const backupMainService = new BackupMainService(this.environmentMainService, this.configurationService, this.logService);
services.set(IBackupMainService, backupMainService);
// URL handling
services.set(IURLService, new SyncDescriptor(NativeURLService));
// Telemetry
if (!this.environmentService.isExtensionDevelopment && !this.environmentService.args['disable-telemetry'] && !!product.enableTelemetry) {
if (!this.environmentMainService.isExtensionDevelopment && !this.environmentMainService.args['disable-telemetry'] && !!product.enableTelemetry) {
const channel = getDelayedChannel(sharedProcessReady.then(client => client.getChannel('telemetryAppender')));
const appender = new TelemetryAppenderClient(channel);
const commonProperties = resolveCommonProperties(this.fileService, release(), process.arch, product.commit, product.version, machineId, product.msftInternalDomains, this.environmentService.installSourcePath);
const piiPaths = [this.environmentService.appRoot, this.environmentService.extensionsPath];
const commonProperties = resolveCommonProperties(this.fileService, release(), process.arch, product.commit, product.version, machineId, product.msftInternalDomains, this.environmentMainService.installSourcePath);
const piiPaths = [this.environmentMainService.appRoot, this.environmentMainService.extensionsPath];
const config: ITelemetryServiceConfig = { appender, commonProperties, piiPaths, sendErrorTelemetry: true };
services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [config]));
@ -666,7 +664,7 @@ export class CodeApplication extends Disposable {
mainProcessElectronServer.registerChannel('webview', webviewChannel);
// Storage (main & shared process)
const storageChannel = this._register(new GlobalStorageDatabaseChannel(this.logService, accessor.get(IStorageMainService)));
const storageChannel = this._register(new StorageDatabaseChannel(this.logService, accessor.get(IStorageMainService)));
mainProcessElectronServer.registerChannel('storage', storageChannel);
sharedProcessClient.then(client => client.registerChannel('storage', storageChannel));
@ -699,7 +697,7 @@ export class CodeApplication extends Disposable {
const pendingWindowOpenablesFromProtocolLinks: IWindowOpenable[] = [];
const pendingProtocolLinksToHandle = [
// Windows/Linux: protocol handler invokes CLI with --open-url
...this.environmentService.args['open-url'] ? this.environmentService.args._urls || [] : [],
...this.environmentMainService.args['open-url'] ? this.environmentMainService.args._urls || [] : [],
// macOS: open-url events
...((<any>global).getOpenUrls() || []) as string[]
@ -736,7 +734,7 @@ export class CodeApplication extends Disposable {
// or open new windows. The URL handler will be invoked from
// protocol invocations outside of VSCode.
const app = this;
const environmentService = this.environmentService;
const environmentService = this.environmentMainService;
urlService.registerHandler({
async handleURL(uri: URI, options?: IOpenURLOptions): Promise<boolean> {
@ -791,10 +789,10 @@ export class CodeApplication extends Disposable {
urlService.registerHandler(new URLHandlerChannelClient(urlHandlerChannel));
// Watch Electron URLs and forward them to the UrlService
this._register(new ElectronURLListener(pendingProtocolLinksToHandle, urlService, windowsMainService, this.environmentService));
this._register(new ElectronURLListener(pendingProtocolLinksToHandle, urlService, windowsMainService, this.environmentMainService));
// Open our first window
const args = this.environmentService.args;
const args = this.environmentMainService.args;
const macOpenFiles: string[] = (<any>global).macOpenFiles;
const context = isLaunchedFromCli(process.env) ? OpenContext.CLI : OpenContext.DESKTOP;
const hasCliArgs = args._.length;
@ -864,7 +862,7 @@ export class CodeApplication extends Disposable {
mnemonicButtonLabel(localize({ key: 'cancel', comment: ['&& denotes a mnemonic'] }, "&&No")),
],
cancelId: 1,
message: localize('confirmOpenMessage', "An external application wants to open '{0}' in {1}. Do you want to open this file or folder?", getPathLabel(uri.fsPath, this.environmentService), product.nameShort),
message: localize('confirmOpenMessage', "An external application wants to open '{0}' in {1}. Do you want to open this file or folder?", getPathLabel(uri.fsPath, this.environmentMainService), product.nameShort),
detail: localize('confirmOpenDetail', "If you did not initiate this request, it may represent an attempted attack on your system. Unless you took an explicit action to initiate this request, you should press 'No'"),
noLink: true
});
@ -960,13 +958,13 @@ export class CodeApplication extends Disposable {
}
// Start to fetch shell environment (if needed) after window has opened
resolveShellEnv(this.logService, this.environmentService.args, process.env);
resolveShellEnv(this.logService, this.environmentMainService.args, process.env);
// If enable-crash-reporter argv is undefined then this is a fresh start,
// based on telemetry.enableCrashreporter settings, generate a UUID which
// will be used as crash reporter id and also update the json file.
try {
const argvContent = await this.fileService.readFile(this.environmentService.argvResource);
const argvContent = await this.fileService.readFile(this.environmentMainService.argvResource);
const argvString = argvContent.value.toString();
const argvJSON = JSON.parse(stripComments(argvString));
if (argvJSON['enable-crash-reporter'] === undefined) {
@ -984,7 +982,7 @@ export class CodeApplication extends Disposable {
];
const newArgvString = argvString.substring(0, argvString.length - 2).concat(',\n', additionalArgvContent.join('\n'));
await this.fileService.writeFile(this.environmentService.argvResource, VSBuffer.fromString(newArgvString));
await this.fileService.writeFile(this.environmentMainService.argvResource, VSBuffer.fromString(newArgvString));
}
} catch (error) {
this.logService.error(error);
@ -1004,7 +1002,7 @@ export class CodeApplication extends Disposable {
recordingStopped = true; // only once
const path = await contentTracing.stopRecording(joinPath(this.environmentService.userHome, `${product.applicationName}-${Math.random().toString(16).slice(-4)}.trace.txt`).fsPath);
const path = await contentTracing.stopRecording(joinPath(this.environmentMainService.userHome, `${product.applicationName}-${Math.random().toString(16).slice(-4)}.trace.txt`).fsPath);
if (!timeout) {
dialogMainService.showMessageBox({

View file

@ -201,7 +201,7 @@ export class ProxyAuthHandler extends Disposable {
const proxyAuthResponseHandler = async (event: ElectronEvent, channel: string, reply: Credentials & { remember: boolean } | undefined /* canceled */) => {
if (channel === payload.replyChannel) {
this.logService.trace(`auth#doResolveProxyCredentials - exit - received credentials from window ${window.id}`);
window.win.webContents.off('ipc-message', proxyAuthResponseHandler);
window.win?.webContents.off('ipc-message', proxyAuthResponseHandler);
// We got credentials from the window
if (reply) {
@ -229,7 +229,7 @@ export class ProxyAuthHandler extends Disposable {
}
};
window.win.webContents.on('ipc-message', proxyAuthResponseHandler);
window.win?.webContents.on('ipc-message', proxyAuthResponseHandler);
});
// Remember credentials for the session in case

View file

@ -181,9 +181,9 @@ class CodeMain {
return [new InstantiationService(services, true), instanceEnvironment, environmentService, configurationService, stateService, bufferLogService];
}
private patchEnvironment(environmentService: IEnvironmentMainService): IProcessEnvironment {
private patchEnvironment(environmentMainService: IEnvironmentMainService): IProcessEnvironment {
const instanceEnvironment: IProcessEnvironment = {
VSCODE_IPC_HOOK: environmentService.mainIPCHandle
VSCODE_IPC_HOOK: environmentMainService.mainIPCHandle
};
['VSCODE_NLS_CONFIG', 'VSCODE_PORTABLE'].forEach(key => {
@ -198,16 +198,16 @@ class CodeMain {
return instanceEnvironment;
}
private initServices(environmentService: IEnvironmentMainService, configurationService: ConfigurationService, stateService: StateService): Promise<unknown> {
private initServices(environmentMainService: IEnvironmentMainService, configurationService: ConfigurationService, stateService: StateService): Promise<unknown> {
// Environment service (paths)
const environmentServiceInitialization = Promise.all<void | undefined>([
environmentService.extensionsPath,
environmentService.nodeCachedDataDir,
environmentService.logsPath,
environmentService.globalStorageHome.fsPath,
environmentService.workspaceStorageHome.fsPath,
environmentService.backupHome
environmentMainService.extensionsPath,
environmentMainService.nodeCachedDataDir,
environmentMainService.logsPath,
environmentMainService.globalStorageHome.fsPath,
environmentMainService.workspaceStorageHome.fsPath,
environmentMainService.backupHome
].map(path => path ? promises.mkdir(path, { recursive: true }) : undefined));
// Configuration service
@ -219,14 +219,14 @@ class CodeMain {
return Promise.all([environmentServiceInitialization, configurationServiceInitialization, stateServiceInitialization]);
}
private async doStartup(args: NativeParsedArgs, logService: ILogService, environmentService: IEnvironmentMainService, lifecycleMainService: ILifecycleMainService, instantiationService: IInstantiationService, retry: boolean): Promise<NodeIPCServer> {
private async doStartup(args: NativeParsedArgs, logService: ILogService, environmentMainService: IEnvironmentMainService, lifecycleMainService: ILifecycleMainService, instantiationService: IInstantiationService, retry: boolean): Promise<NodeIPCServer> {
// Try to setup a server for running. If that succeeds it means
// we are the first instance to startup. Otherwise it is likely
// that another instance is already running.
let mainProcessNodeIpcServer: NodeIPCServer;
try {
mainProcessNodeIpcServer = await nodeIPCServe(environmentService.mainIPCHandle);
mainProcessNodeIpcServer = await nodeIPCServe(environmentMainService.mainIPCHandle);
once(lifecycleMainService.onWillShutdown)(() => mainProcessNodeIpcServer.dispose());
} catch (error) {
@ -235,7 +235,7 @@ class CodeMain {
if (error.code !== 'EADDRINUSE') {
// Show a dialog for errors that can be resolved by the user
this.handleStartupDataDirError(environmentService, error);
this.handleStartupDataDirError(environmentMainService, error);
// Any other runtime error is just printed to the console
throw error;
@ -244,7 +244,7 @@ class CodeMain {
// there's a running instance, let's connect to it
let client: NodeIPCClient<string>;
try {
client = await nodeIPCConnect(environmentService.mainIPCHandle, 'main');
client = await nodeIPCConnect(environmentMainService.mainIPCHandle, 'main');
} catch (error) {
// Handle unexpected connection errors by showing a dialog to the user
@ -263,18 +263,18 @@ class CodeMain {
// let's delete it, since we can't connect to it and then
// retry the whole thing
try {
unlinkSync(environmentService.mainIPCHandle);
unlinkSync(environmentMainService.mainIPCHandle);
} catch (error) {
logService.warn('Could not delete obsolete instance handle', error);
throw error;
}
return this.doStartup(args, logService, environmentService, lifecycleMainService, instantiationService, false);
return this.doStartup(args, logService, environmentMainService, lifecycleMainService, instantiationService, false);
}
// Tests from CLI require to be the only instance currently
if (environmentService.extensionTestsLocationURI && !environmentService.debugExtensionHost.break) {
if (environmentMainService.extensionTestsLocationURI && !environmentMainService.debugExtensionHost.break) {
const msg = 'Running extension tests from the command line is currently only supported if no other instance of Code is running.';
logService.error(msg);
client.dispose();
@ -344,9 +344,9 @@ class CodeMain {
return mainProcessNodeIpcServer;
}
private handleStartupDataDirError(environmentService: IEnvironmentMainService, error: NodeJS.ErrnoException): void {
private handleStartupDataDirError(environmentMainService: IEnvironmentMainService, error: NodeJS.ErrnoException): void {
if (error.code === 'EACCES' || error.code === 'EPERM') {
const directories = coalesce([environmentService.userDataPath, environmentService.extensionsPath, XDG_RUNTIME_DIR]).map(folder => getPathLabel(folder, environmentService));
const directories = coalesce([environmentMainService.userDataPath, environmentMainService.extensionsPath, XDG_RUNTIME_DIR]).map(folder => getPathLabel(folder, environmentMainService));
this.showStartupWarningDialog(
localize('startupDataDirError', "Unable to write program user data."),
@ -369,9 +369,9 @@ class CodeMain {
});
}
private async windowsAllowSetForegroundWindow(launchService: ILaunchMainService, logService: ILogService): Promise<void> {
private async windowsAllowSetForegroundWindow(launchMainService: ILaunchMainService, logService: ILogService): Promise<void> {
if (isWindows) {
const processId = await launchService.getMainProcessId();
const processId = await launchMainService.getMainProcessId();
logService.trace('Sending some foreground love to the running instance:', processId);

View file

@ -48,10 +48,10 @@ export class FileProtocolHandler extends Disposable {
}
injectWindowsMainService(windowsMainService: IWindowsMainService): void {
this._register(windowsMainService.onWindowReady(window => {
this._register(windowsMainService.onDidSignalReadyWindow(window => {
if (window.config?.extensionDevelopmentPath || window.config?.extensionTestsPath) {
const disposables = new DisposableStore();
disposables.add(Event.any(window.onClose, window.onDestroy)(() => disposables.dispose()));
disposables.add(Event.any(window.onDidClose, window.onDidDestroy)(() => disposables.dispose()));
// Allow access to extension development path
if (window.config.extensionDevelopmentPath) {

View file

@ -4,9 +4,9 @@
*--------------------------------------------------------------------------------------------*/
//@ts-check
'use strict';
(function () {
'use strict';
const bootstrapWindow = bootstrapWindowLib();
// Load issue reporter into window

View file

@ -4,9 +4,9 @@
*--------------------------------------------------------------------------------------------*/
//@ts-check
'use strict';
(function () {
'use strict';
const bootstrapWindow = bootstrapWindowLib();
// Load process explorer into window

View file

@ -6,9 +6,9 @@
/// <reference path="../../../../typings/require.d.ts" />
//@ts-check
'use strict';
(function () {
'use strict';
const bootstrapWindow = bootstrapWindowLib();
// Add a perf entry right from the top
@ -76,5 +76,4 @@
}
//#endregion
}());

View file

@ -118,6 +118,10 @@ class ModesContentComputer implements IHoverComputer<HoverPartInfo[]> {
const maxColumn = model.getLineMaxColumn(lineNumber);
const lineDecorations = this._editor.getLineDecorations(lineNumber).filter((d) => {
if (d.options.isWholeLine) {
return true;
}
const startColumn = (d.range.startLineNumber === lineNumber) ? d.range.startColumn : 1;
const endColumn = (d.range.endLineNumber === lineNumber) ? d.range.endColumn : maxColumn;
if (startColumn > hoverRange.startColumn || hoverRange.endColumn > endColumn) {

View file

@ -21,7 +21,7 @@ import { Context as SuggestContext, CompletionItem } from './suggest';
import { CompletionModel } from './completionModel';
import { attachListStyler } from 'vs/platform/theme/common/styler';
import { IThemeService, IColorTheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { registerColor, editorWidgetBackground, listFocusBackground, activeContrastBorder, listHighlightForeground, editorForeground, editorWidgetBorder, focusBorder, textLinkForeground, textCodeBlockBackground } from 'vs/platform/theme/common/colorRegistry';
import { registerColor, editorWidgetBackground, quickInputListFocusBackground, activeContrastBorder, listHighlightForeground, editorForeground, editorWidgetBorder, focusBorder, textLinkForeground, textCodeBlockBackground } from 'vs/platform/theme/common/colorRegistry';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { TimeoutTimer, CancelablePromise, createCancelablePromise, disposableTimeout } from 'vs/base/common/async';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@ -40,7 +40,7 @@ import { clamp } from 'vs/base/common/numbers';
export const editorSuggestWidgetBackground = registerColor('editorSuggestWidget.background', { dark: editorWidgetBackground, light: editorWidgetBackground, hc: editorWidgetBackground }, nls.localize('editorSuggestWidgetBackground', 'Background color of the suggest widget.'));
export const editorSuggestWidgetBorder = registerColor('editorSuggestWidget.border', { dark: editorWidgetBorder, light: editorWidgetBorder, hc: editorWidgetBorder }, nls.localize('editorSuggestWidgetBorder', 'Border color of the suggest widget.'));
export const editorSuggestWidgetForeground = registerColor('editorSuggestWidget.foreground', { dark: editorForeground, light: editorForeground, hc: editorForeground }, nls.localize('editorSuggestWidgetForeground', 'Foreground color of the suggest widget.'));
export const editorSuggestWidgetSelectedBackground = registerColor('editorSuggestWidget.selectedBackground', { dark: listFocusBackground, light: listFocusBackground, hc: listFocusBackground }, nls.localize('editorSuggestWidgetSelectedBackground', 'Background color of the selected entry in the suggest widget.'));
export const editorSuggestWidgetSelectedBackground = registerColor('editorSuggestWidget.selectedBackground', { dark: quickInputListFocusBackground, light: quickInputListFocusBackground, hc: quickInputListFocusBackground }, nls.localize('editorSuggestWidgetSelectedBackground', 'Background color of the selected entry in the suggest widget.'));
export const editorSuggestWidgetHighlightForeground = registerColor('editorSuggestWidget.highlightForeground', { dark: listHighlightForeground, light: listHighlightForeground, hc: listHighlightForeground }, nls.localize('editorSuggestWidgetHighlightForeground', 'Color of the match highlights in the suggest widget.'));
const enum State {

View file

@ -52,7 +52,7 @@ class TestGlobalStyleSheet extends GlobalStyleSheet {
suite('Decoration Render Options', () => {
let options: IDecorationRenderOptions = {
gutterIconPath: URI.parse('https://github.com/microsoft/vscode/blob/master/resources/linux/code.png'),
gutterIconPath: URI.parse('https://github.com/microsoft/vscode/blob/main/resources/linux/code.png'),
gutterIconSize: 'contain',
backgroundColor: 'red',
borderColor: 'yellow'
@ -79,7 +79,7 @@ suite('Decoration Render Options', () => {
const s = new TestCodeEditorServiceImpl(themeServiceMock, styleSheet);
s.registerDecorationType('example', options);
const sheet = readStyleSheet(styleSheet);
assert(sheet.indexOf(`{background:url('https://github.com/microsoft/vscode/blob/master/resources/linux/code.png') center center no-repeat;background-size:contain;}`) >= 0);
assert(sheet.indexOf(`{background:url('https://github.com/microsoft/vscode/blob/main/resources/linux/code.png') center center no-repeat;background-size:contain;}`) >= 0);
assert(sheet.indexOf(`{background-color:red;border-color:yellow;box-sizing: border-box;}`) >= 0);
});

View file

@ -321,34 +321,30 @@ export class ExecuteCommandAction extends Action {
export class SubmenuItemAction extends SubmenuAction {
readonly item: ISubmenuItem;
constructor(
item: ISubmenuItem,
menuService: IMenuService,
contextKeyService: IContextKeyService,
options?: IMenuActionOptions
readonly item: ISubmenuItem,
private readonly _menuService: IMenuService,
private readonly _contextKeyService: IContextKeyService,
private readonly _options?: IMenuActionOptions
) {
super(`submenuitem.${item.submenu.id}`, typeof item.title === 'string' ? item.title : item.title.value, [], 'submenu');
}
get actions(): readonly IAction[] {
const result: IAction[] = [];
const menu = menuService.createMenu(item.submenu, contextKeyService);
const groups = menu.getActions(options);
const menu = this._menuService.createMenu(this.item.submenu, this._contextKeyService);
const groups = menu.getActions(this._options);
menu.dispose();
for (let group of groups) {
const [, actions] = group;
for (const [, actions] of groups) {
if (actions.length > 0) {
result.push(...actions);
result.push(new Separator());
}
}
if (result.length) {
result.pop(); // remove last separator
}
super(`submenuitem.${item.submenu.id}`, typeof item.title === 'string' ? item.title : item.title.value, result, 'submenu');
this.item = item;
return result;
}
}

View file

@ -38,12 +38,12 @@ export class BackupMainService implements IBackupMainService {
private readonly backupPathComparer = { isEqual: (pathA: string, pathB: string) => isEqual(pathA, pathB, !isLinux) };
constructor(
@IEnvironmentMainService environmentService: IEnvironmentMainService,
@IEnvironmentMainService environmentMainService: IEnvironmentMainService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@ILogService private readonly logService: ILogService
) {
this.backupHome = environmentService.backupHome;
this.workspacesJsonPath = environmentService.backupWorkspacesPath;
this.backupHome = environmentMainService.backupHome;
this.workspacesJsonPath = environmentMainService.backupWorkspacesPath;
}
async initialize(): Promise<void> {

View file

@ -43,7 +43,12 @@ export class ElectronExtensionHostDebugBroadcastChannel<TContext> extends Extens
return {};
}
const debug = codeWindow.win.webContents.debugger;
const win = codeWindow.win;
if (!win) {
return {};
}
const debug = win.webContents.debugger;
let listeners = debug.isAttached() ? Infinity : 0;
const server = createServer(listener => {
@ -61,7 +66,7 @@ export class ElectronExtensionHostDebugBroadcastChannel<TContext> extends Extens
const onMessage = (_event: Event, method: string, params: unknown, sessionId?: string) =>
writeMessage(({ method, params, sessionId }));
codeWindow.win.on('close', () => {
win.on('close', () => {
debug.removeListener('message', onMessage);
listener.end();
closed = true;
@ -103,7 +108,7 @@ export class ElectronExtensionHostDebugBroadcastChannel<TContext> extends Extens
});
await new Promise<void>(r => server.listen(0, r));
codeWindow.win.on('close', () => server.close());
win.on('close', () => server.close());
return { rendererDebugPort: (server.address() as AddressInfo).port };
}

View file

@ -62,7 +62,7 @@ export class Driver implements IDriver, IWindowDriverRegistry {
await this.whenUnfrozen(windowId);
const window = this.windowsMainService.getWindowById(windowId);
if (!window) {
if (!window?.win) {
throw new Error('Invalid window');
}
const webContents = window.win.webContents;
@ -101,7 +101,7 @@ export class Driver implements IDriver, IWindowDriverRegistry {
}
const window = this.windowsMainService.getWindowById(windowId);
if (!window) {
if (!window?.win) {
throw new Error('Invalid window');
}
const webContents = window.win.webContents;
@ -207,10 +207,10 @@ export class Driver implements IDriver, IWindowDriverRegistry {
export async function serve(
windowServer: IPCServer,
handle: string,
environmentService: IEnvironmentMainService,
environmentMainService: IEnvironmentMainService,
instantiationService: IInstantiationService
): Promise<IDisposable> {
const verbose = environmentService.driverVerbose;
const verbose = environmentMainService.driverVerbose;
const driver = instantiationService.createInstance(Driver, windowServer, { verbose });
const windowDriverRegistryChannel = new WindowDriverRegistryChannel(driver);

View file

@ -3,14 +3,14 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as os from 'os';
import { homedir, tmpdir } from 'os';
import product from 'vs/platform/product/common/product';
import { IDebugParams, IExtensionHostDebugParams, INativeEnvironmentService } from 'vs/platform/environment/common/environment';
import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
import * as paths from 'vs/base/node/paths';
import * as path from 'vs/base/common/path';
import * as resources from 'vs/base/common/resources';
import { getDefaultUserDataPath } from 'vs/base/node/userDataPath';
import { dirname, join, normalize, resolve } from 'vs/base/common/path';
import { joinPath } from 'vs/base/common/resources';
import { memoize } from 'vs/base/common/decorators';
import product from 'vs/platform/product/common/product';
import { toLocalISOString } from 'vs/base/common/date';
import { FileAccess } from 'vs/base/common/network';
import { URI } from 'vs/base/common/uri';
@ -22,46 +22,46 @@ export class NativeEnvironmentService implements INativeEnvironmentService {
get args(): NativeParsedArgs { return this._args; }
@memoize
get appRoot(): string { return path.dirname(FileAccess.asFileUri('', require).fsPath); }
get appRoot(): string { return dirname(FileAccess.asFileUri('', require).fsPath); }
readonly logsPath: string;
@memoize
get userHome(): URI { return URI.file(os.homedir()); }
get userHome(): URI { return URI.file(homedir()); }
@memoize
get userDataPath(): string {
const vscodePortable = process.env['VSCODE_PORTABLE'];
if (vscodePortable) {
return path.join(vscodePortable, 'user-data');
return join(vscodePortable, 'user-data');
}
return parseUserDataDir(this._args, process);
}
@memoize
get appSettingsHome(): URI { return URI.file(path.join(this.userDataPath, 'User')); }
get appSettingsHome(): URI { return URI.file(join(this.userDataPath, 'User')); }
@memoize
get tmpDir(): URI { return URI.file(os.tmpdir()); }
get tmpDir(): URI { return URI.file(tmpdir()); }
@memoize
get userRoamingDataHome(): URI { return this.appSettingsHome; }
@memoize
get settingsResource(): URI { return resources.joinPath(this.userRoamingDataHome, 'settings.json'); }
get settingsResource(): URI { return joinPath(this.userRoamingDataHome, 'settings.json'); }
@memoize
get userDataSyncHome(): URI { return resources.joinPath(this.userRoamingDataHome, 'sync'); }
get userDataSyncHome(): URI { return joinPath(this.userRoamingDataHome, 'sync'); }
@memoize
get userDataSyncLogResource(): URI { return URI.file(path.join(this.logsPath, 'userDataSync.log')); }
get userDataSyncLogResource(): URI { return URI.file(join(this.logsPath, 'userDataSync.log')); }
@memoize
get sync(): 'on' | 'off' | undefined { return this.args.sync; }
@memoize
get machineSettingsResource(): URI { return resources.joinPath(URI.file(path.join(this.userDataPath, 'Machine')), 'settings.json'); }
get machineSettingsResource(): URI { return joinPath(URI.file(join(this.userDataPath, 'Machine')), 'settings.json'); }
@memoize
get globalStorageHome(): URI { return URI.joinPath(this.appSettingsHome, 'globalStorage'); }
@ -70,32 +70,32 @@ export class NativeEnvironmentService implements INativeEnvironmentService {
get workspaceStorageHome(): URI { return URI.joinPath(this.appSettingsHome, 'workspaceStorage'); }
@memoize
get keybindingsResource(): URI { return resources.joinPath(this.userRoamingDataHome, 'keybindings.json'); }
get keybindingsResource(): URI { return joinPath(this.userRoamingDataHome, 'keybindings.json'); }
@memoize
get keyboardLayoutResource(): URI { return resources.joinPath(this.userRoamingDataHome, 'keyboardLayout.json'); }
get keyboardLayoutResource(): URI { return joinPath(this.userRoamingDataHome, 'keyboardLayout.json'); }
@memoize
get argvResource(): URI {
const vscodePortable = process.env['VSCODE_PORTABLE'];
if (vscodePortable) {
return URI.file(path.join(vscodePortable, 'argv.json'));
return URI.file(join(vscodePortable, 'argv.json'));
}
return resources.joinPath(this.userHome, product.dataFolderName, 'argv.json');
return joinPath(this.userHome, product.dataFolderName, 'argv.json');
}
@memoize
get snippetsHome(): URI { return resources.joinPath(this.userRoamingDataHome, 'snippets'); }
get snippetsHome(): URI { return joinPath(this.userRoamingDataHome, 'snippets'); }
@memoize
get isExtensionDevelopment(): boolean { return !!this._args.extensionDevelopmentPath; }
@memoize
get untitledWorkspacesHome(): URI { return URI.file(path.join(this.userDataPath, 'Workspaces')); }
get untitledWorkspacesHome(): URI { return URI.file(join(this.userDataPath, 'Workspaces')); }
@memoize
get installSourcePath(): string { return path.join(this.userDataPath, 'installSource'); }
get installSourcePath(): string { return join(this.userDataPath, 'installSource'); }
@memoize
get builtinExtensionsPath(): string {
@ -103,7 +103,7 @@ export class NativeEnvironmentService implements INativeEnvironmentService {
if (fromArgs) {
return fromArgs;
} else {
return path.normalize(path.join(FileAccess.asFileUri('', require).fsPath, '..', 'extensions'));
return normalize(join(FileAccess.asFileUri('', require).fsPath, '..', 'extensions'));
}
}
@ -112,7 +112,7 @@ export class NativeEnvironmentService implements INativeEnvironmentService {
if (fromArgs) {
return fromArgs;
} else {
return path.join(this.userDataPath, 'CachedExtensionVSIXs');
return join(this.userDataPath, 'CachedExtensionVSIXs');
}
}
@ -131,10 +131,10 @@ export class NativeEnvironmentService implements INativeEnvironmentService {
const vscodePortable = process.env['VSCODE_PORTABLE'];
if (vscodePortable) {
return path.join(vscodePortable, 'extensions');
return join(vscodePortable, 'extensions');
}
return resources.joinPath(this.userHome, product.dataFolderName, 'extensions').fsPath;
return joinPath(this.userHome, product.dataFolderName, 'extensions').fsPath;
}
@memoize
@ -145,7 +145,7 @@ export class NativeEnvironmentService implements INativeEnvironmentService {
if (/^[^:/?#]+?:\/\//.test(p)) {
return URI.parse(p);
}
return URI.file(path.normalize(p));
return URI.file(normalize(p));
});
}
return undefined;
@ -158,7 +158,7 @@ export class NativeEnvironmentService implements INativeEnvironmentService {
if (/^[^:/?#]+?:\/\//.test(s)) {
return URI.parse(s);
}
return URI.file(path.normalize(s));
return URI.file(normalize(s));
}
return undefined;
}
@ -188,7 +188,7 @@ export class NativeEnvironmentService implements INativeEnvironmentService {
get logLevel(): string | undefined { return this._args.log; }
@memoize
get serviceMachineIdResource(): URI { return resources.joinPath(URI.file(this.userDataPath), 'machineid'); }
get serviceMachineIdResource(): URI { return joinPath(URI.file(this.userDataPath), 'machineid'); }
get crashReporterId(): string | undefined { return this._args['crash-reporter-id']; }
get crashReporterDirectory(): string | undefined { return this._args['crash-reporter-directory']; }
@ -196,13 +196,13 @@ export class NativeEnvironmentService implements INativeEnvironmentService {
get driverHandle(): string | undefined { return this._args['driver']; }
@memoize
get telemetryLogResource(): URI { return URI.file(path.join(this.logsPath, 'telemetry.log')); }
get telemetryLogResource(): URI { return URI.file(join(this.logsPath, 'telemetry.log')); }
get disableTelemetry(): boolean { return !!this._args['disable-telemetry']; }
constructor(protected _args: NativeParsedArgs) {
if (!_args.logsPath) {
const key = toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '');
_args.logsPath = path.join(this.userDataPath, 'logs', key);
_args.logsPath = join(this.userDataPath, 'logs', key);
}
this.logsPath = _args.logsPath;
}
@ -231,15 +231,15 @@ export function parsePathArg(arg: string | undefined, process: NodeJS.Process):
// Determine if the arg is relative or absolute, if relative use the original CWD
// (VSCODE_CWD), not the potentially overridden one (process.cwd()).
const resolved = path.resolve(arg);
const resolved = resolve(arg);
if (path.normalize(arg) === resolved) {
if (normalize(arg) === resolved) {
return resolved;
}
return path.resolve(process.env['VSCODE_CWD'] || process.cwd(), arg);
return resolve(process.env['VSCODE_CWD'] || process.cwd(), arg);
}
export function parseUserDataDir(args: NativeParsedArgs, process: NodeJS.Process): string {
return parsePathArg(args['user-data-dir'], process) || path.resolve(paths.getDefaultUserDataPath());
return parsePathArg(args['user-data-dir'], process) || resolve(getDefaultUserDataPath());
}

View file

@ -38,7 +38,7 @@ export class IssueMainService implements ICommonIssueService {
constructor(
private machineId: string,
private userEnv: IProcessEnvironment,
@IEnvironmentMainService private readonly environmentService: IEnvironmentMainService,
@IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService,
@ILaunchMainService private readonly launchMainService: ILaunchMainService,
@ILogService private readonly logService: ILogService,
@IDiagnosticsService private readonly diagnosticsService: IDiagnosticsService,
@ -271,7 +271,7 @@ export class IssueMainService implements ICommonIssueService {
this._processExplorerWindow.setMenuBarVisibility(false);
const windowConfiguration = {
appRoot: this.environmentService.appRoot,
appRoot: this.environmentMainService.appRoot,
windowId: this._processExplorerWindow.id,
userEnv: this.userEnv,
machineId: this.machineId,
@ -397,13 +397,13 @@ export class IssueMainService implements ICommonIssueService {
}
const windowConfiguration = {
appRoot: this.environmentService.appRoot,
appRoot: this.environmentMainService.appRoot,
windowId: this._issueWindow.id,
machineId: this.machineId,
userEnv: this.userEnv,
data,
features,
disableExtensions: this.environmentService.disableExtensions,
disableExtensions: this.environmentMainService.disableExtensions,
os: {
type: os.type(),
arch: os.arch(),

View file

@ -21,6 +21,7 @@ import { IMainProcessInfo, IWindowInfo } from 'vs/platform/launch/common/launch'
import { isLaunchedFromCli } from 'vs/platform/environment/node/argvHelper';
import { CancellationToken } from 'vs/base/common/cancellation';
import { isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { assertIsDefined } from 'vs/base/common/types';
export const ID = 'launchMainService';
export const ILaunchMainService = createDecorator<ILaunchMainService>(ID);
@ -293,8 +294,9 @@ export class LaunchMainService implements ILaunchMainService {
private codeWindowToInfo(window: ICodeWindow): IWindowInfo {
const folderURIs = this.getFolderURIs(window);
const win = assertIsDefined(window.win);
return this.browserWindowToInfo(window.win, folderURIs, window.remoteAuthority);
return this.browserWindowToInfo(win, folderURIs, window.remoteAuthority);
}
private browserWindowToInfo(window: BrowserWindow, folderURIs: URI[] = [], remoteAuthority?: string): IWindowInfo {

Some files were not shown because too many files have changed in this diff Show more