mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-13 04:21:41 -04:00
Merge from vscode 8aa90d444f5d051984e8055f547c4252d53479b3 (#5587)
* Merge from vscode 8aa90d444f5d051984e8055f547c4252d53479b3 * pipeline errors * fix build
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,4 +1,5 @@
|
||||
.DS_Store
|
||||
.cache
|
||||
npm-debug.log
|
||||
Thumbs.db
|
||||
node_modules/
|
||||
@@ -14,6 +15,9 @@ out-editor-min/
|
||||
out-monaco-editor-core/
|
||||
out-vscode/
|
||||
out-vscode-min/
|
||||
out-vscode-reh/
|
||||
out-vscode-reh-min/
|
||||
out-vscode-reh-pkg/
|
||||
build/node_modules
|
||||
coverage/
|
||||
test_data/
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
steps:
|
||||
- task: NodeTool@0
|
||||
inputs:
|
||||
versionSpec: '8.x'
|
||||
versionSpec: '10.15.1'
|
||||
displayName: 'Install Node.js'
|
||||
|
||||
- script: |
|
||||
git submodule update --init --recursive
|
||||
nvm install 10.15.1
|
||||
nvm use 10.15.1
|
||||
npm i -g yarn
|
||||
displayName: 'preinstall'
|
||||
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as cp from 'child_process';
|
||||
import * as cp from 'child_process';
|
||||
import * as path from 'path';
|
||||
|
||||
function yarnInstall(packageName: string): void {
|
||||
cp.execSync(`yarn add --no-lockfile ${packageName}`);
|
||||
cp.execSync(`yarn add --no-lockfile ${packageName}`, { cwd: path.join( process.cwd(), 'remote') });
|
||||
}
|
||||
|
||||
const product = require('../../../product.json');
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
yarn gulp vscode-darwin-min
|
||||
yarn gulp vscode-reh-darwin-min
|
||||
yarn gulp upload-vscode-sourcemaps
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
# remove pkg from archive
|
||||
zip -d ../VSCode-darwin.zip "*.pkg"
|
||||
@@ -15,6 +16,19 @@ node build/azure-pipelines/common/publish.js \
|
||||
true \
|
||||
../VSCode-darwin.zip
|
||||
|
||||
# package Remote Extension Host
|
||||
pushd .. && mv vscode-reh-darwin vscode-server-darwin && zip -Xry vscode-server-darwin.zip vscode-server-darwin && popd
|
||||
|
||||
# publish Remote Extension Host
|
||||
node build/azure-pipelines/common/publish.js \
|
||||
"$VSCODE_QUALITY" \
|
||||
server-darwin \
|
||||
archive-unsigned \
|
||||
"vscode-server-darwin.zip" \
|
||||
$VERSION \
|
||||
true \
|
||||
../vscode-server-darwin.zip
|
||||
|
||||
# publish hockeyapp symbols
|
||||
node build/azure-pipelines/common/symbols.js "$VSCODE_MIXIN_PASSWORD" "$VSCODE_HOCKEYAPP_TOKEN" "$VSCODE_ARCH" "$VSCODE_HOCKEYAPP_ID_MACOS"
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
yarn gulp "vscode-linux-$VSCODE_ARCH-min"
|
||||
yarn gulp "vscode-linux-$VSCODE_ARCH-min"
|
||||
|
||||
if [[ "$VSCODE_ARCH" != "ia32" ]]; then
|
||||
yarn gulp vscode-reh-linux-$VSCODE_ARCH-min
|
||||
fi
|
||||
@@ -20,6 +20,19 @@ rm -rf $ROOT/code-*.tar.*
|
||||
|
||||
node build/azure-pipelines/common/publish.js "$VSCODE_QUALITY" "$PLATFORM_LINUX" archive-unsigned "$TARBALL_FILENAME" "$VERSION" true "$TARBALL_PATH"
|
||||
|
||||
# Publish Remote Extension Host
|
||||
if [[ "$VSCODE_ARCH" != "ia32" ]]; then
|
||||
LEGACY_SERVER_BUILD_NAME="vscode-reh-$PLATFORM_LINUX"
|
||||
SERVER_BUILD_NAME="vscode-server-$PLATFORM_LINUX"
|
||||
SERVER_TARBALL_FILENAME="vscode-server-$PLATFORM_LINUX.tar.gz"
|
||||
SERVER_TARBALL_PATH="$ROOT/$SERVER_TARBALL_FILENAME"
|
||||
|
||||
rm -rf $ROOT/vscode-server-*.tar.*
|
||||
(cd $ROOT && mv $LEGACY_SERVER_BUILD_NAME $SERVER_BUILD_NAME && tar -czf $SERVER_TARBALL_PATH $SERVER_BUILD_NAME)
|
||||
|
||||
node build/azure-pipelines/common/publish.js "$VSCODE_QUALITY" "server-$PLATFORM_LINUX" archive-unsigned "$SERVER_TARBALL_FILENAME" "$VERSION" true "$SERVER_TARBALL_PATH"
|
||||
fi
|
||||
|
||||
# Publish hockeyapp symbols
|
||||
node build/azure-pipelines/common/symbols.js "$VSCODE_MIXIN_PASSWORD" "$VSCODE_HOCKEYAPP_TOKEN" "$VSCODE_ARCH" "$VSCODE_HOCKEYAPP_ID_LINUX64"
|
||||
|
||||
|
||||
2
build/azure-pipelines/publish-types/.gitignore
vendored
Normal file
2
build/azure-pipelines/publish-types/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
node_modules/
|
||||
*.js
|
||||
36
build/azure-pipelines/publish-types/check-version.js
Normal file
36
build/azure-pipelines/publish-types/check-version.js
Normal file
@@ -0,0 +1,36 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const cp = require("child_process");
|
||||
let tag = '';
|
||||
try {
|
||||
tag = cp
|
||||
.execSync('git describe --tags `git rev-list --tags --max-count=1`')
|
||||
.toString()
|
||||
.trim();
|
||||
if (!isValidTag(tag)) {
|
||||
throw Error(`Invalid tag ${tag}`);
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
console.error(err);
|
||||
console.error('Failed to update types');
|
||||
process.exit(1);
|
||||
}
|
||||
function isValidTag(t) {
|
||||
if (t.split('.').length !== 3) {
|
||||
return false;
|
||||
}
|
||||
const [major, minor, bug] = t.split('.');
|
||||
// Only release for tags like 1.34.0
|
||||
if (bug !== '0') {
|
||||
return false;
|
||||
}
|
||||
if (parseInt(major, 10) === NaN || parseInt(minor, 10) === NaN) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
43
build/azure-pipelines/publish-types/check-version.ts
Normal file
43
build/azure-pipelines/publish-types/check-version.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as cp from 'child_process';
|
||||
|
||||
let tag = '';
|
||||
try {
|
||||
tag = cp
|
||||
.execSync('git describe --tags `git rev-list --tags --max-count=1`')
|
||||
.toString()
|
||||
.trim();
|
||||
|
||||
if (!isValidTag(tag)) {
|
||||
throw Error(`Invalid tag ${tag}`);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
console.error('Failed to update types');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function isValidTag(t: string) {
|
||||
if (t.split('.').length !== 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const [major, minor, bug] = t.split('.');
|
||||
|
||||
// Only release for tags like 1.34.0
|
||||
if (bug !== '0') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (parseInt(major, 10) === NaN || parseInt(minor, 10) === NaN) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
67
build/azure-pipelines/publish-types/publish-types.yml
Normal file
67
build/azure-pipelines/publish-types/publish-types.yml
Normal file
@@ -0,0 +1,67 @@
|
||||
# Publish @types/vscode for each release
|
||||
|
||||
trigger:
|
||||
branches:
|
||||
include: ['refs/tags/*']
|
||||
|
||||
pr: none
|
||||
|
||||
steps:
|
||||
- task: NodeTool@0
|
||||
inputs:
|
||||
versionSpec: "10.15.1"
|
||||
|
||||
- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2
|
||||
inputs:
|
||||
versionSpec: "1.10.1"
|
||||
|
||||
- bash: |
|
||||
# Install build dependencies
|
||||
(cd build && yarn)
|
||||
node build/azure-pipelines/publish-types/check-version.js
|
||||
displayName: Check version
|
||||
|
||||
- bash: |
|
||||
git config --global user.email "vscode@microsoft.com"
|
||||
git config --global user.name "VSCode"
|
||||
|
||||
git clone https://$(GITHUB_TOKEN)@github.com/DefinitelyTyped/DefinitelyTyped.git --depth=1
|
||||
node build/azure-pipelines/publish-types/update-types.js
|
||||
|
||||
TAG_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`)
|
||||
|
||||
cd DefinitelyTyped
|
||||
|
||||
git diff --color | cat
|
||||
git add -A
|
||||
git status
|
||||
git checkout -b "vscode-types-$TAG_VERSION"
|
||||
git commit -m "VS Code $TAG_VERSION Extension API"
|
||||
git push origin "vscode-types-$TAG_VERSION"
|
||||
|
||||
displayName: Push update to DefinitelyTyped
|
||||
|
||||
- bash: |
|
||||
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:"
|
||||
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="[@octref, @jrieken, @kmaetzel, @egamma]. Please review and merge PR to publish @types/vscode."
|
||||
|
||||
curl -X POST -H "Authorization: Bearer $(SLACK_TOKEN)" \
|
||||
-H 'Content-type: application/json; charset=utf-8' \
|
||||
--data '{"channel":"'"$CHANNEL"'", "link_names": true, "text":"'"$MESSAGE"'"}' \
|
||||
https://slack.com/api/chat.postMessage
|
||||
|
||||
curl -X POST -H "Authorization: Bearer $(SLACK_TOKEN)" \
|
||||
-H 'Content-type: application/json; charset=utf-8' \
|
||||
--data '{"channel":"'"$CHANNEL"'", "link_names": true, "text":"'"$LINK"'"}' \
|
||||
https://slack.com/api/chat.postMessage
|
||||
|
||||
curl -X POST -H "Authorization: Bearer $(SLACK_TOKEN)" \
|
||||
-H 'Content-type: application/json; charset=utf-8' \
|
||||
--data '{"channel":"'"$CHANNEL"'", "link_names": true, "text":"'"$MESSAGE2"'"}' \
|
||||
https://slack.com/api/chat.postMessage
|
||||
|
||||
displayName: Send message on Slack
|
||||
62
build/azure-pipelines/publish-types/update-types.js
Normal file
62
build/azure-pipelines/publish-types/update-types.js
Normal file
@@ -0,0 +1,62 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const fs = require("fs");
|
||||
const cp = require("child_process");
|
||||
const path = require("path");
|
||||
let tag = '';
|
||||
try {
|
||||
tag = cp
|
||||
.execSync('git describe --tags `git rev-list --tags --max-count=1`')
|
||||
.toString()
|
||||
.trim();
|
||||
const dtsUri = `https://raw.githubusercontent.com/microsoft/vscode/${tag}/src/vs/vscode.d.ts`;
|
||||
const outPath = path.resolve(process.cwd(), 'DefinitelyTyped/types/vscode/index.d.ts');
|
||||
cp.execSync(`curl ${dtsUri} --output ${outPath}`);
|
||||
updateDTSFile(outPath, tag);
|
||||
console.log(`Done updating vscode.d.ts at ${outPath}`);
|
||||
}
|
||||
catch (err) {
|
||||
console.error(err);
|
||||
console.error('Failed to update types');
|
||||
process.exit(1);
|
||||
}
|
||||
function updateDTSFile(outPath, tag) {
|
||||
const oldContent = fs.readFileSync(outPath, 'utf-8');
|
||||
const newContent = getNewFileContent(oldContent, tag);
|
||||
fs.writeFileSync(outPath, newContent);
|
||||
}
|
||||
function getNewFileContent(content, tag) {
|
||||
const oldheader = [
|
||||
`/*---------------------------------------------------------------------------------------------`,
|
||||
` * Copyright (c) Microsoft Corporation. All rights reserved.`,
|
||||
` * Licensed under the Source EULA. See License.txt in the project root for license information.`,
|
||||
` *--------------------------------------------------------------------------------------------*/`
|
||||
].join('\n');
|
||||
return getNewFileHeader(tag) + content.slice(oldheader.length);
|
||||
}
|
||||
function getNewFileHeader(tag) {
|
||||
const [major, minor] = tag.split('.');
|
||||
const shorttag = `${major}.${minor}`;
|
||||
const header = [
|
||||
`// Type definitions for Visual Studio Code ${shorttag}`,
|
||||
`// Project: https://github.com/microsoft/vscode`,
|
||||
`// Definitions by: Visual Studio Code Team, Microsoft <https://github.com/Microsoft>`,
|
||||
`// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped`,
|
||||
``,
|
||||
`/*---------------------------------------------------------------------------------------------`,
|
||||
` * Copyright (c) Microsoft Corporation. All rights reserved.`,
|
||||
` * Licensed under the Source EULA.`,
|
||||
` * See https://github.com/Microsoft/vscode/blob/master/LICENSE.txt for license information.`,
|
||||
` *--------------------------------------------------------------------------------------------*/`,
|
||||
``,
|
||||
`/**`,
|
||||
` * Type Definition for Visual Studio Code ${shorttag} Extension API`,
|
||||
` * See https://code.visualstudio.com/api for more information`,
|
||||
` */`
|
||||
].join('\n');
|
||||
return header;
|
||||
}
|
||||
73
build/azure-pipelines/publish-types/update-types.ts
Normal file
73
build/azure-pipelines/publish-types/update-types.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as cp from 'child_process';
|
||||
import * as path from 'path';
|
||||
|
||||
let tag = '';
|
||||
try {
|
||||
tag = cp
|
||||
.execSync('git describe --tags `git rev-list --tags --max-count=1`')
|
||||
.toString()
|
||||
.trim();
|
||||
|
||||
const dtsUri = `https://raw.githubusercontent.com/microsoft/vscode/${tag}/src/vs/vscode.d.ts`;
|
||||
const outPath = path.resolve(process.cwd(), 'DefinitelyTyped/types/vscode/index.d.ts');
|
||||
cp.execSync(`curl ${dtsUri} --output ${outPath}`);
|
||||
|
||||
updateDTSFile(outPath, tag);
|
||||
|
||||
console.log(`Done updating vscode.d.ts at ${outPath}`);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
console.error('Failed to update types');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function updateDTSFile(outPath: string, tag: string) {
|
||||
const oldContent = fs.readFileSync(outPath, 'utf-8');
|
||||
const newContent = getNewFileContent(oldContent, tag);
|
||||
|
||||
fs.writeFileSync(outPath, newContent);
|
||||
}
|
||||
|
||||
function getNewFileContent(content: string, tag: string) {
|
||||
const oldheader = [
|
||||
`/*---------------------------------------------------------------------------------------------`,
|
||||
` * Copyright (c) Microsoft Corporation. All rights reserved.`,
|
||||
` * Licensed under the Source EULA. See License.txt in the project root for license information.`,
|
||||
` *--------------------------------------------------------------------------------------------*/`
|
||||
].join('\n');
|
||||
|
||||
return getNewFileHeader(tag) + content.slice(oldheader.length);
|
||||
}
|
||||
|
||||
function getNewFileHeader(tag: string) {
|
||||
const [major, minor] = tag.split('.');
|
||||
const shorttag = `${major}.${minor}`;
|
||||
|
||||
const header = [
|
||||
`// Type definitions for Visual Studio Code ${shorttag}`,
|
||||
`// Project: https://github.com/microsoft/vscode`,
|
||||
`// Definitions by: Visual Studio Code Team, Microsoft <https://github.com/Microsoft>`,
|
||||
`// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped`,
|
||||
``,
|
||||
`/*---------------------------------------------------------------------------------------------`,
|
||||
` * Copyright (c) Microsoft Corporation. All rights reserved.`,
|
||||
` * Licensed under the Source EULA.`,
|
||||
` * See https://github.com/Microsoft/vscode/blob/master/LICENSE.txt for license information.`,
|
||||
` *--------------------------------------------------------------------------------------------*/`,
|
||||
``,
|
||||
`/**`,
|
||||
` * Type Definition for Visual Studio Code ${shorttag} Extension API`,
|
||||
` * See https://code.visualstudio.com/api for more information`,
|
||||
` */`
|
||||
].join('\n');
|
||||
|
||||
return header;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
. build/azure-pipelines/win32/exec.ps1
|
||||
$ErrorActionPreference = "Stop"
|
||||
exec { yarn gulp "vscode-win32-$env:VSCODE_ARCH-min" }
|
||||
exec { yarn gulp "vscode-reh-win32-$env:VSCODE_ARCH-min" }
|
||||
exec { yarn gulp "vscode-win32-$env:VSCODE_ARCH-inno-updater" }
|
||||
@@ -10,8 +10,16 @@ $Root = "$Repo\.."
|
||||
$SystemExe = "$Repo\.build\win32-$Arch\system-setup\VSCodeSetup.exe"
|
||||
$UserExe = "$Repo\.build\win32-$Arch\user-setup\VSCodeSetup.exe"
|
||||
$Zip = "$Repo\.build\win32-$Arch\archive\VSCode-win32-$Arch.zip"
|
||||
$LegacyServer = "$Root\vscode-reh-win32-$Arch"
|
||||
$ServerName = "vscode-server-win32-$Arch"
|
||||
$Server = "$Root\$ServerName"
|
||||
$ServerZip = "$Repo\.build\vscode-server-win32-$Arch.zip"
|
||||
$Build = "$Root\VSCode-win32-$Arch"
|
||||
|
||||
# Create server archive
|
||||
exec { Rename-Item -Path $LegacyServer -NewName $ServerName }
|
||||
exec { .\node_modules\7zip\7zip-lite\7z.exe a -tzip $ServerZip $Server -r }
|
||||
|
||||
# get version
|
||||
$PackageJson = Get-Content -Raw -Path "$Build\resources\app\package.json" | ConvertFrom-Json
|
||||
$Version = $PackageJson.version
|
||||
@@ -22,6 +30,7 @@ $AssetPlatform = if ("$Arch" -eq "ia32") { "win32" } else { "win32-x64" }
|
||||
exec { node build/azure-pipelines/common/publish.js $Quality "$AssetPlatform-archive" archive "VSCode-win32-$Arch-$Version.zip" $Version true $Zip }
|
||||
exec { node build/azure-pipelines/common/publish.js $Quality "$AssetPlatform" setup "VSCodeSetup-$Arch-$Version.exe" $Version true $SystemExe }
|
||||
exec { node build/azure-pipelines/common/publish.js $Quality "$AssetPlatform-user" setup "VSCodeUserSetup-$Arch-$Version.exe" $Version true $UserExe }
|
||||
exec { node build/azure-pipelines/common/publish.js $Quality "server-$AssetPlatform" archive "vscode-server-win32-$Arch.zip" $Version true $ServerZip }
|
||||
|
||||
# publish hockeyapp symbols
|
||||
$hockeyAppId = if ("$Arch" -eq "ia32") { "$env:VSCODE_HOCKEYAPP_ID_WIN32" } else { "$env:VSCODE_HOCKEYAPP_ID_WIN64" }
|
||||
|
||||
16
build/gulpfile.reh.js
Normal file
16
build/gulpfile.reh.js
Normal file
@@ -0,0 +1,16 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const gulp = require('gulp');
|
||||
|
||||
const noop = () => { return Promise.resolve(); };
|
||||
|
||||
gulp.task('vscode-reh-win32-ia32-min', noop);
|
||||
gulp.task('vscode-reh-win32-x64-min', noop);
|
||||
gulp.task('vscode-reh-darwin-min', noop);
|
||||
gulp.task('vscode-reh-linux-x64-min', noop);
|
||||
gulp.task('vscode-reh-linux-arm-min', noop);
|
||||
@@ -110,6 +110,10 @@
|
||||
"name": "vs/workbench/contrib/quickopen",
|
||||
"project": "vscode-workbench"
|
||||
},
|
||||
{
|
||||
"name": "vs/workbench/contrib/remote",
|
||||
"project": "vscode-workbench"
|
||||
},
|
||||
{
|
||||
"name": "vs/workbench/contrib/relauncher",
|
||||
"project": "vscode-workbench"
|
||||
|
||||
@@ -36,6 +36,8 @@ function yarnInstall(location, opts) {
|
||||
|
||||
yarnInstall('extensions'); // node modules shared by all extensions
|
||||
|
||||
yarnInstall('remote'); // node modules used by vscode server
|
||||
|
||||
const allExtensionFolders = fs.readdirSync('extensions');
|
||||
const extensions = allExtensionFolders.filter(e => {
|
||||
try {
|
||||
|
||||
@@ -96,6 +96,14 @@
|
||||
{
|
||||
"fileMatch": "/.vscode/extensions.json",
|
||||
"url": "vscode://schemas/extensions"
|
||||
},
|
||||
{
|
||||
"fileMatch": "/.devcontainer/devcontainer.json",
|
||||
"url": "./schemas/devContainer.schema.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": "/.devcontainer.json",
|
||||
"url": "./schemas/devContainer.schema.json"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/schema#",
|
||||
"description": "Defines a dev container",
|
||||
"allowComments": true,
|
||||
"type": "object",
|
||||
"definitions": {
|
||||
"devContainerCommon": {
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "A name to show for the workspace folder."
|
||||
},
|
||||
"extensions": {
|
||||
"type": "array",
|
||||
"description": "An array of extensions that should be installed into the container.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"type": "object",
|
||||
"description": "Machine specific settings that should be copied into the container."
|
||||
},
|
||||
"postCreateCommand": {
|
||||
"type": ["string", "array"],
|
||||
"description": "A command to run after creating the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"devPort": {
|
||||
"type": "integer",
|
||||
"description": "The port VS Code can use to connect to its backend."
|
||||
}
|
||||
}
|
||||
},
|
||||
"nonComposeBase": {
|
||||
"properties": {
|
||||
"appPort": {
|
||||
"type": ["integer", "string", "array"],
|
||||
"description": "Application ports that are exposed by the container. This can be a single port or an array of ports. Each port can be a number or a string. A number is mapped to the same port on the host. A string is passed to Docker unchanged and can be used to map ports differently, e.g. \"8000:8010\".",
|
||||
"items": {
|
||||
"type": ["integer", "string"]
|
||||
}
|
||||
},
|
||||
"runArgs": {
|
||||
"type": "array",
|
||||
"description": "The arguments required when starting in the container.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"shutdownAction": {
|
||||
"type": "string",
|
||||
"enum": ["none", "stopContainer"],
|
||||
"description": "Action to take when VS Code is shutting down. The default is to stop the container."
|
||||
},
|
||||
"overrideCommand": {
|
||||
"type": "boolean",
|
||||
"description": "Whether to overwrite the command specified in the image. The default is true."
|
||||
},
|
||||
"workspaceFolder": {
|
||||
"type": "string",
|
||||
"description": "The path of the workspace folder inside the container."
|
||||
},
|
||||
"workspaceMount": {
|
||||
"type": "string",
|
||||
"description": "The --mount parameter for docker run. The default is to mount the project folder at /workspaces/$project."
|
||||
}
|
||||
}
|
||||
},
|
||||
"dockerFileContainer": {
|
||||
"properties": {
|
||||
"dockerFile": {
|
||||
"type": "string",
|
||||
"description": "The location of the Dockerfile that defines the contents of the container. The path is relative to the folder containing the `devcontainer.json` file."
|
||||
},
|
||||
"context": {
|
||||
"type": "string",
|
||||
"description": "The location of the context folder for building the Docker image. The path is relative to the folder containing the `devcontainer.json` file."
|
||||
}
|
||||
},
|
||||
"required": ["dockerFile"]
|
||||
},
|
||||
"imageContainer": {
|
||||
"properties": {
|
||||
"image": {
|
||||
"type": "string",
|
||||
"description": "The docker image that will be used to create the container."
|
||||
}
|
||||
},
|
||||
"required": ["image"]
|
||||
},
|
||||
"composeContainer": {
|
||||
"properties": {
|
||||
"dockerComposeFile": {
|
||||
"type": ["string", "array"],
|
||||
"description": "The name of the docker-compose file(s) used to start the services.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"service": {
|
||||
"type": "string",
|
||||
"description": "The service you want to work on."
|
||||
},
|
||||
"workspaceFolder": {
|
||||
"type": "string",
|
||||
"description": "The path of the workspace folder inside the container."
|
||||
},
|
||||
"shutdownAction": {
|
||||
"type": "string",
|
||||
"enum": ["none", "stopCompose"],
|
||||
"description": "Action to take when VS Code is shutting down. The default is to stop the containers."
|
||||
}
|
||||
},
|
||||
"required": ["dockerComposeFile", "service", "workspaceFolder"]
|
||||
}
|
||||
},
|
||||
"allOf": [
|
||||
{
|
||||
"oneOf": [
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/definitions/dockerFileContainer"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/imageContainer"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/nonComposeBase"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/composeContainer"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/devContainerCommon"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
body {
|
||||
html, body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif;
|
||||
font-size: 14px;
|
||||
padding: 0 26px;
|
||||
@@ -32,7 +32,6 @@ body {
|
||||
box-shadow: 2px 2px 2px rgba(0,0,0,.25);
|
||||
}
|
||||
|
||||
|
||||
body.scrollBeyondLastLine {
|
||||
margin-bottom: calc(100vh - 22px);
|
||||
}
|
||||
@@ -169,19 +168,14 @@ blockquote {
|
||||
|
||||
code {
|
||||
font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback";
|
||||
font-size: 14px;
|
||||
line-height: 19px;
|
||||
font-size: 1rem;
|
||||
line-height: 1.357rem;
|
||||
}
|
||||
|
||||
body.wordWrap pre {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.mac code {
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
pre:not(.hljs),
|
||||
pre.hljs code > div {
|
||||
padding: 16px;
|
||||
|
||||
@@ -19,12 +19,12 @@ export class ShowPreviewSecuritySelectorCommand implements Command {
|
||||
|
||||
public execute(resource: string | undefined) {
|
||||
if (this.previewManager.activePreviewResource) {
|
||||
this.previewSecuritySelector.showSecutitySelectorForResource(this.previewManager.activePreviewResource);
|
||||
this.previewSecuritySelector.showSecuritySelectorForResource(this.previewManager.activePreviewResource);
|
||||
} else if (resource) {
|
||||
const source = vscode.Uri.parse(resource);
|
||||
this.previewSecuritySelector.showSecutitySelectorForResource(source.query ? vscode.Uri.parse(source.query) : source);
|
||||
this.previewSecuritySelector.showSecuritySelectorForResource(source.query ? vscode.Uri.parse(source.query) : source);
|
||||
} else if (vscode.window.activeTextEditor && isMarkdownFile(vscode.window.activeTextEditor.document)) {
|
||||
this.previewSecuritySelector.showSecutitySelectorForResource(vscode.window.activeTextEditor.document.uri);
|
||||
this.previewSecuritySelector.showSecuritySelectorForResource(vscode.window.activeTextEditor.document.uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,10 @@ const previewStrings = {
|
||||
'Content Disabled Security Warning')
|
||||
};
|
||||
|
||||
function escapeAttribute(value: string): string {
|
||||
return value.replace(/"/g, '"');
|
||||
}
|
||||
|
||||
export class MarkdownContentProvider {
|
||||
constructor(
|
||||
private readonly engine: MarkdownEngine,
|
||||
@@ -75,9 +79,9 @@ export class MarkdownContentProvider {
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
|
||||
${csp}
|
||||
<meta id="vscode-markdown-preview-data"
|
||||
data-settings="${JSON.stringify(initialData).replace(/"/g, '"')}"
|
||||
data-strings="${JSON.stringify(previewStrings).replace(/"/g, '"')}"
|
||||
data-state="${JSON.stringify(state || {}).replace(/"/g, '"')}">
|
||||
data-settings="${escapeAttribute(JSON.stringify(initialData))}"
|
||||
data-strings="${escapeAttribute(JSON.stringify(previewStrings))}"
|
||||
data-state="${escapeAttribute(JSON.stringify(state || {}))}">
|
||||
<script src="${this.extensionResourcePath('pre.js')}" nonce="${nonce}"></script>
|
||||
${this.getStyles(sourceUri, nonce, config, state)}
|
||||
<base href="${markdownDocument.uri.with({ scheme: 'vscode-resource' }).toString(true)}">
|
||||
@@ -142,7 +146,7 @@ export class MarkdownContentProvider {
|
||||
private computeCustomStyleSheetIncludes(resource: vscode.Uri, config: MarkdownPreviewConfiguration): string {
|
||||
if (Array.isArray(config.styles)) {
|
||||
return config.styles.map(style => {
|
||||
return `<link rel="stylesheet" class="code-user-style" data-source="${style.replace(/"/g, '"')}" href="${this.fixHref(resource, style).replace(/"/g, '"')}" type="text/css" media="screen">`;
|
||||
return `<link rel="stylesheet" class="code-user-style" data-source="${escapeAttribute(style)}" href="${escapeAttribute(this.fixHref(resource, style))}" type="text/css" media="screen">`;
|
||||
}).join('\n');
|
||||
}
|
||||
return '';
|
||||
@@ -150,7 +154,7 @@ export class MarkdownContentProvider {
|
||||
|
||||
private getSettingsOverrideStyles(nonce: string, config: MarkdownPreviewConfiguration): string {
|
||||
return `<style nonce="${nonce}">
|
||||
body {
|
||||
html, body {
|
||||
${config.fontFamily ? `font-family: ${config.fontFamily};` : ''}
|
||||
${isNaN(config.fontSize) ? '' : `font-size: ${config.fontSize}px;`}
|
||||
${isNaN(config.lineHeight) ? '' : `line-height: ${config.lineHeight};`}
|
||||
@@ -175,7 +179,7 @@ export class MarkdownContentProvider {
|
||||
|
||||
private getStyles(resource: vscode.Uri, nonce: string, config: MarkdownPreviewConfiguration, state?: any): string {
|
||||
const baseStyles = this.contributionProvider.contributions.previewStyles
|
||||
.map(resource => `<link rel="stylesheet" type="text/css" href="${resource.toString()}">`)
|
||||
.map(resource => `<link rel="stylesheet" type="text/css" href="${escapeAttribute(resource.toString())}">`)
|
||||
.join('\n');
|
||||
|
||||
return `${baseStyles}
|
||||
@@ -186,7 +190,7 @@ export class MarkdownContentProvider {
|
||||
|
||||
private getScripts(nonce: string): string {
|
||||
return this.contributionProvider.contributions.previewScripts
|
||||
.map(resource => `<script async src="${resource.toString()}" nonce="${nonce}" charset="UTF-8"></script>`)
|
||||
.map(resource => `<script async src="${escapeAttribute(resource.toString())}" nonce="${nonce}" charset="UTF-8"></script>`)
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
|
||||
@@ -6,32 +6,32 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { Logger } from '../logger';
|
||||
import { MarkdownContributionProvider } from '../markdownExtensions';
|
||||
import { disposeAll } from '../util/dispose';
|
||||
import { disposeAll, Disposable } from '../util/dispose';
|
||||
import { MarkdownFileTopmostLineMonitor } from '../util/topmostLineMonitor';
|
||||
import { MarkdownPreview, PreviewSettings } from './preview';
|
||||
import { MarkdownPreviewConfigurationManager } from './previewConfig';
|
||||
import { MarkdownContentProvider } from './previewContentProvider';
|
||||
|
||||
|
||||
export class MarkdownPreviewManager implements vscode.WebviewPanelSerializer {
|
||||
export class MarkdownPreviewManager extends Disposable implements vscode.WebviewPanelSerializer {
|
||||
private static readonly markdownPreviewActiveContextKey = 'markdownPreviewFocus';
|
||||
|
||||
private readonly _topmostLineMonitor = new MarkdownFileTopmostLineMonitor();
|
||||
private readonly _previewConfigurations = new MarkdownPreviewConfigurationManager();
|
||||
private readonly _previews: MarkdownPreview[] = [];
|
||||
private _activePreview: MarkdownPreview | undefined = undefined;
|
||||
private readonly _disposables: vscode.Disposable[] = [];
|
||||
|
||||
public constructor(
|
||||
private readonly _contentProvider: MarkdownContentProvider,
|
||||
private readonly _logger: Logger,
|
||||
private readonly _contributions: MarkdownContributionProvider
|
||||
) {
|
||||
this._disposables.push(vscode.window.registerWebviewPanelSerializer(MarkdownPreview.viewType, this));
|
||||
super();
|
||||
this._register(vscode.window.registerWebviewPanelSerializer(MarkdownPreview.viewType, this));
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
disposeAll(this._disposables);
|
||||
super.dispose();
|
||||
disposeAll(this._previews);
|
||||
}
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ export class PreviewSecuritySelector {
|
||||
private readonly webviewManager: MarkdownPreviewManager
|
||||
) { }
|
||||
|
||||
public async showSecutitySelectorForResource(resource: vscode.Uri): Promise<void> {
|
||||
public async showSecuritySelectorForResource(resource: vscode.Uri): Promise<void> {
|
||||
interface PreviewSecurityPickItem extends vscode.QuickPickItem {
|
||||
readonly type: 'moreinfo' | 'toggle' | MarkdownPreviewSecurityLevel;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"version": "0.0.1",
|
||||
"description": "Dependencies shared by all extensions",
|
||||
"dependencies": {
|
||||
"typescript": "3.5.0-dev.20190517"
|
||||
"typescript": "3.5.0-dev.20190522"
|
||||
},
|
||||
"scripts": {
|
||||
"postinstall": "node ./postinstall"
|
||||
|
||||
2
extensions/vscode-test-resolver/.gitignore
vendored
Normal file
2
extensions/vscode-test-resolver/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
out
|
||||
node_modules
|
||||
22
extensions/vscode-test-resolver/.vscode/launch.json
vendored
Normal file
22
extensions/vscode-test-resolver/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
// A launch configuration that compiles the extension and then opens it inside a new window
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Extension",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "${execPath}",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}",
|
||||
"--remote=test+test"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/out/**/*.js"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
6
extensions/vscode-test-resolver/.vscodeignore
Normal file
6
extensions/vscode-test-resolver/.vscodeignore
Normal file
@@ -0,0 +1,6 @@
|
||||
.vscode/**
|
||||
typings/**
|
||||
**/*.ts
|
||||
**/*.map
|
||||
.gitignore
|
||||
tsconfig.json
|
||||
89
extensions/vscode-test-resolver/package.json
Normal file
89
extensions/vscode-test-resolver/package.json
Normal file
@@ -0,0 +1,89 @@
|
||||
{
|
||||
"name": "vscode-test-resolver",
|
||||
"description": "Test resolver for VS Code",
|
||||
"version": "0.0.1",
|
||||
"publisher": "vscode",
|
||||
"enableProposedApi": true,
|
||||
"private": true,
|
||||
"engines": {
|
||||
"vscode": "^1.25.0"
|
||||
},
|
||||
"extensionKind": "ui",
|
||||
"scripts": {
|
||||
"compile": "node ./node_modules/vscode/bin/compile -watch -p ./",
|
||||
"vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:vscode-test-resolver ./tsconfig.json"
|
||||
},
|
||||
"activationEvents": [
|
||||
"onResolveRemoteAuthority:test",
|
||||
"onCommand:vscode-testresolver.newWindow",
|
||||
"onCommand:vscode-testresolver.newWindowWithError",
|
||||
"onCommand:vscode-testresolver.showLog"
|
||||
],
|
||||
"main": "./out/extension",
|
||||
"devDependencies": {
|
||||
"@types/node": "^10.12.21",
|
||||
"vscode": "1.1.5"
|
||||
},
|
||||
"contributes": {
|
||||
"resourceLabelFormatters": [
|
||||
{
|
||||
"scheme": "vscode-remote",
|
||||
"authority": "test+*",
|
||||
"formatting": {
|
||||
"label": "${path}",
|
||||
"separator": "/",
|
||||
"tildify": true,
|
||||
"workspaceSuffix": "TestResolver"
|
||||
}
|
||||
}
|
||||
],
|
||||
"commands": [
|
||||
{
|
||||
"title": "New Window",
|
||||
"category": "Remote-TestResolver",
|
||||
"command": "vscode-testresolver.newWindow"
|
||||
},
|
||||
{
|
||||
"title": "New Window with Error",
|
||||
"category": "Remote-TestResolver",
|
||||
"command": "vscode-testresolver.newWindowWithError"
|
||||
},
|
||||
{
|
||||
"title": "Show Log",
|
||||
"category": "Remote-TestResolver",
|
||||
"command": "vscode-testresolver.showLog"
|
||||
}
|
||||
],
|
||||
"menus": {
|
||||
"statusBar/windowIndicator": [
|
||||
{
|
||||
"command": "vscode-testresolver.newWindow",
|
||||
"when": "!remoteAuthority",
|
||||
"group": "9_local_testresolver@2"
|
||||
},
|
||||
{
|
||||
"command": "vscode-testresolver.newWindowWithError",
|
||||
"when": "!remoteAuthority",
|
||||
"group": "9_local_testresolver@3"
|
||||
},
|
||||
{
|
||||
"command": "vscode-testresolver.showLog",
|
||||
"when": "remoteAuthority =~ /^test\\+.*$/",
|
||||
"group": "1_remote_testresolver_open@3"
|
||||
},
|
||||
{
|
||||
"command": "vscode-testresolver.newWindow",
|
||||
"when": "remoteAuthority =~ /^test\\+.*$/",
|
||||
"group": "1_remote_testresolver_open@1"
|
||||
},
|
||||
{
|
||||
"command": "vscode-testresolver.newWindowWithError",
|
||||
"when": "remoteAuthority =~ /^test\\+.*$/",
|
||||
"group": "1_remote_testresolver_open@2"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
117
extensions/vscode-test-resolver/src/download.ts
Normal file
117
extensions/vscode-test-resolver/src/download.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as https from 'https';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as cp from 'child_process';
|
||||
import { parse as parseUrl } from 'url';
|
||||
|
||||
function ensureFolderExists(loc: string) {
|
||||
if (!fs.existsSync(loc)) {
|
||||
const parent = path.dirname(loc);
|
||||
if (parent) {
|
||||
ensureFolderExists(parent);
|
||||
}
|
||||
fs.mkdirSync(loc);
|
||||
}
|
||||
}
|
||||
|
||||
function getDownloadUrl(updateUrl: string, commit: string, platform: string, quality: string): string {
|
||||
return `${updateUrl}/commit:${commit}/server-${platform}/${quality}`;
|
||||
}
|
||||
|
||||
async function downloadVSCodeServerArchive(updateUrl: string, commit: string, quality: string, destDir: string): Promise<string> {
|
||||
ensureFolderExists(destDir);
|
||||
|
||||
const platform = process.platform === 'win32' ? 'win32-x64' : process.platform === 'darwin' ? 'darwin' : 'linux-x64';
|
||||
const downloadUrl = getDownloadUrl(updateUrl, commit, platform, quality);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
console.log(`Downloading VS Code Server from: ${downloadUrl}`);
|
||||
const requestOptions: https.RequestOptions = parseUrl(downloadUrl);
|
||||
|
||||
https.get(requestOptions, res => {
|
||||
if (res.statusCode !== 302) {
|
||||
reject('Failed to get VS Code server archive location');
|
||||
}
|
||||
const archiveUrl = res.headers.location;
|
||||
if (!archiveUrl) {
|
||||
reject('Failed to get VS Code server archive location');
|
||||
return;
|
||||
}
|
||||
|
||||
const archiveRequestOptions: https.RequestOptions = parseUrl(archiveUrl);
|
||||
if (archiveUrl.endsWith('.zip')) {
|
||||
const archivePath = path.resolve(destDir, `vscode-server-${commit}.zip`);
|
||||
const outStream = fs.createWriteStream(archivePath);
|
||||
outStream.on('close', () => {
|
||||
resolve(archivePath);
|
||||
});
|
||||
https.get(archiveRequestOptions, res => {
|
||||
res.pipe(outStream);
|
||||
});
|
||||
} else {
|
||||
const zipPath = path.resolve(destDir, `vscode-server-${commit}.tgz`);
|
||||
const outStream = fs.createWriteStream(zipPath);
|
||||
https.get(archiveRequestOptions, res => {
|
||||
res.pipe(outStream);
|
||||
});
|
||||
outStream.on('close', () => {
|
||||
resolve(zipPath);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Unzip a .zip or .tar.gz VS Code archive
|
||||
*/
|
||||
function unzipVSCodeServer(vscodeArchivePath: string, extractDir: string) {
|
||||
if (vscodeArchivePath.endsWith('.zip')) {
|
||||
const tempDir = fs.mkdtempSync('vscode-server');
|
||||
if (process.platform === 'win32') {
|
||||
cp.spawnSync('powershell.exe', [
|
||||
'-NoProfile',
|
||||
'-ExecutionPolicy', 'Bypass',
|
||||
'-NonInteractive',
|
||||
'-NoLogo',
|
||||
'-Command',
|
||||
`Microsoft.PowerShell.Archive\\Expand-Archive -Path "${vscodeArchivePath}" -DestinationPath "${tempDir}"`
|
||||
]);
|
||||
} else {
|
||||
cp.spawnSync('unzip', [vscodeArchivePath, '-d', `${tempDir}`]);
|
||||
}
|
||||
fs.renameSync(path.join(tempDir, process.platform === 'win32' ? 'vscode-server-win32-x64' : 'vscode-server-darwin'), extractDir);
|
||||
} else {
|
||||
// tar does not create extractDir by default
|
||||
if (!fs.existsSync(extractDir)) {
|
||||
fs.mkdirSync(extractDir);
|
||||
}
|
||||
cp.spawnSync('tar', ['-xzf', vscodeArchivePath, '-C', extractDir, '--strip-components', '1']);
|
||||
}
|
||||
}
|
||||
|
||||
export async function downloadAndUnzipVSCodeServer(updateUrl: string, commit: string, quality: string = 'stable', destDir: string): Promise<string> {
|
||||
|
||||
const extractDir = path.join(destDir, commit);
|
||||
if (fs.existsSync(extractDir)) {
|
||||
console.log(`Found ${extractDir}. Skipping download.`);
|
||||
} else {
|
||||
console.log(`Downloading VS Code Server ${quality} - ${commit} into ${extractDir}.`);
|
||||
try {
|
||||
const vscodeArchivePath = await downloadVSCodeServerArchive(updateUrl, commit, quality, destDir);
|
||||
if (fs.existsSync(vscodeArchivePath)) {
|
||||
unzipVSCodeServer(vscodeArchivePath, extractDir);
|
||||
// Remove archive
|
||||
fs.unlinkSync(vscodeArchivePath);
|
||||
}
|
||||
} catch (err) {
|
||||
throw Error(`Failed to download and unzip VS Code ${quality} - ${commit}`);
|
||||
}
|
||||
}
|
||||
return Promise.resolve(extractDir);
|
||||
}
|
||||
190
extensions/vscode-test-resolver/src/extension.ts
Normal file
190
extensions/vscode-test-resolver/src/extension.ts
Normal file
@@ -0,0 +1,190 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as cp from 'child_process';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import { downloadAndUnzipVSCodeServer } from './download';
|
||||
|
||||
let startPromise: Thenable<vscode.ResolvedAuthority> | undefined = void 0;
|
||||
let extHostProcess: cp.ChildProcess | undefined;
|
||||
const enum CharCode {
|
||||
Backspace = 8,
|
||||
LineFeed = 10
|
||||
}
|
||||
|
||||
let outputChannel: vscode.OutputChannel;
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
|
||||
function doResolve(_authority: string, progress: vscode.Progress<{ message?: string; increment?: number }>): Promise<vscode.ResolvedAuthority> {
|
||||
return new Promise(async (res, rej) => {
|
||||
progress.report({ message: 'Starting Test Resolver' });
|
||||
outputChannel = vscode.window.createOutputChannel('TestResolver');
|
||||
|
||||
let isStarted = false;
|
||||
async function processError(message: string) {
|
||||
outputChannel.appendLine(message);
|
||||
if (!isStarted) {
|
||||
outputChannel.show();
|
||||
|
||||
const result = await vscode.window.showErrorMessage(message, { modal: true }, ...getActions());
|
||||
if (result) {
|
||||
await result.execute();
|
||||
}
|
||||
rej(vscode.RemoteAuthorityResolverError.NotAvailable(message, true));
|
||||
}
|
||||
}
|
||||
|
||||
let lastProgressLine = '';
|
||||
function processOutput(output: string) {
|
||||
outputChannel.append(output);
|
||||
for (let i = 0; i < output.length; i++) {
|
||||
const chr = output.charCodeAt(i);
|
||||
if (chr === CharCode.LineFeed) {
|
||||
const match = lastProgressLine.match(/Extension host agent listening on (\d+)/);
|
||||
if (match) {
|
||||
isStarted = true;
|
||||
res(new vscode.ResolvedAuthority('localhost', parseInt(match[1], 10))); // success!
|
||||
}
|
||||
lastProgressLine = '';
|
||||
} else if (chr === CharCode.Backspace) {
|
||||
lastProgressLine = lastProgressLine.substr(0, lastProgressLine.length - 1);
|
||||
} else {
|
||||
lastProgressLine += output.charAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_authority === 'test+error' || vscode.workspace.getConfiguration('testresolver').get('error') === true) {
|
||||
processError('Unable to start the Test Resolver.');
|
||||
return;
|
||||
}
|
||||
|
||||
const { updateUrl, commit, quality } = getProductConfiguration();
|
||||
if (!commit) { // dev mode
|
||||
const vscodePath = path.resolve(path.join(context.extensionPath, '..', '..'));
|
||||
const nodeExec = process.platform === 'win32' ? 'node.exe' : 'node';
|
||||
const nodePath = path.join(vscodePath, '.build', 'node-remote', nodeExec);
|
||||
|
||||
if (!fs.existsSync(nodePath)) {
|
||||
try {
|
||||
progress.report({ message: 'Installing node' });
|
||||
outputChannel.appendLine(`Installing node at ${nodePath}`);
|
||||
cp.execSync(`node ${path.join(vscodePath, 'node_modules/gulp/bin/gulp.js')} node-remote`);
|
||||
} catch (e) {
|
||||
processError(`Problem downloading node: ${e.message}`);
|
||||
|
||||
}
|
||||
}
|
||||
outputChannel.appendLine(`Using node at ${nodePath}`);
|
||||
|
||||
const env = getNewEnv();
|
||||
env['PATH'] = path.join(vscodePath, 'resources', 'server', 'bin') + path.delimiter + env['PATH']; // allow calling code-dev.sh
|
||||
|
||||
outputChannel.appendLine(env['PATH'] || '');
|
||||
|
||||
extHostProcess = cp.spawn(nodePath, [path.join('out', 'remoteExtensionHostAgent'), '--port=0'], { cwd: vscodePath, env });
|
||||
} else {
|
||||
const serverBin = path.resolve(os.homedir(), '.vscode-remote', 'bin');
|
||||
progress.report({ message: 'Installing VSCode Server' });
|
||||
const serverLocation = await downloadAndUnzipVSCodeServer(updateUrl, commit, quality, serverBin);
|
||||
outputChannel.appendLine(`Using server build at ${serverLocation}`);
|
||||
|
||||
const commandArgs = ['--port=0', '--disable-telemetry'];
|
||||
|
||||
const env = getNewEnv();
|
||||
env['PATH'] = path.join(serverLocation, 'bin') + path.delimiter + env['PATH']; // code command for the terminal
|
||||
|
||||
extHostProcess = cp.spawn(path.join(serverLocation, 'server.sh'), commandArgs, { env, cwd: serverLocation });
|
||||
}
|
||||
extHostProcess.stdout.on('data', (data: Buffer) => processOutput(data.toString()));
|
||||
extHostProcess.stderr.on('data', (data: Buffer) => processOutput(data.toString()));
|
||||
extHostProcess.on('error', (error: Error) => processError(`remoteExtensionHostAgent failed with error:\n${error.message}`));
|
||||
extHostProcess.on('close', (code: number) => processError(`remoteExtensionHostAgent closed unexpectedly.\nError code: ${code}`));
|
||||
});
|
||||
}
|
||||
|
||||
vscode.workspace.registerRemoteAuthorityResolver('test', {
|
||||
resolve(_authority: string): Thenable<vscode.ResolvedAuthority> {
|
||||
if (!startPromise) {
|
||||
startPromise = vscode.window.withProgress({
|
||||
location: vscode.ProgressLocation.Notification,
|
||||
title: 'Open TestResolver Remote ([details](command:remote-testresolver.showLog))',
|
||||
cancellable: false
|
||||
}, (progress) => doResolve(_authority, progress));
|
||||
}
|
||||
return startPromise;
|
||||
}
|
||||
});
|
||||
|
||||
vscode.commands.registerCommand('vscode-testresolver.newWindow', () => {
|
||||
return vscode.commands.executeCommand('vscode.newWindow', { remoteAuthority: 'test+test' });
|
||||
});
|
||||
vscode.commands.registerCommand('vscode-testresolver.newWindowWithError', () => {
|
||||
return vscode.commands.executeCommand('vscode.newWindow', { remoteAuthority: 'test+error' });
|
||||
});
|
||||
vscode.commands.registerCommand('vscode-testresolver.showLog', () => {
|
||||
if (outputChannel) {
|
||||
outputChannel.show();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
type ActionItem = (vscode.MessageItem & { execute: () => void; });
|
||||
|
||||
function getActions(): ActionItem[] {
|
||||
const actions: ActionItem[] = [];
|
||||
const isDirty = vscode.workspace.textDocuments.some(d => d.isDirty) || vscode.workspace.workspaceFile && vscode.workspace.workspaceFile.scheme === 'untitled';
|
||||
|
||||
actions.push({
|
||||
title: 'Retry',
|
||||
execute: async () => {
|
||||
await vscode.commands.executeCommand('workbench.action.reloadWindow');
|
||||
}
|
||||
});
|
||||
if (!isDirty) {
|
||||
actions.push({
|
||||
title: 'Close Remote',
|
||||
execute: async () => {
|
||||
await vscode.commands.executeCommand('vscode.newWindow', { reuseWindow: true });
|
||||
}
|
||||
});
|
||||
}
|
||||
actions.push({
|
||||
title: 'Ignore',
|
||||
isCloseAffordance: true,
|
||||
execute: async () => {
|
||||
vscode.commands.executeCommand('vscode-testresolver.showLog'); // no need to wait
|
||||
}
|
||||
});
|
||||
return actions;
|
||||
}
|
||||
|
||||
export interface IProductConfiguration {
|
||||
updateUrl: string;
|
||||
commit: string;
|
||||
quality: string;
|
||||
}
|
||||
|
||||
function getProductConfiguration(): IProductConfiguration {
|
||||
const content = fs.readFileSync(path.join(vscode.env.appRoot, 'product.json')).toString();
|
||||
return JSON.parse(content) as IProductConfiguration;
|
||||
}
|
||||
|
||||
function getNewEnv(): { [x: string]: string | undefined } {
|
||||
const env = { ...process.env };
|
||||
delete env['ELECTRON_RUN_AS_NODE'];
|
||||
return env;
|
||||
}
|
||||
|
||||
export function deactivate() {
|
||||
if (extHostProcess) {
|
||||
extHostProcess.kill();
|
||||
}
|
||||
}
|
||||
8
extensions/vscode-test-resolver/src/typings/ref.d.ts
vendored
Normal file
8
extensions/vscode-test-resolver/src/typings/ref.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/// <reference path="../../../../src/vs/vscode.d.ts" />
|
||||
/// <reference path="../../../../src/vs/vscode.proposed.d.ts" />
|
||||
/// <reference types='@types/node'/>
|
||||
9
extensions/vscode-test-resolver/tsconfig.json
Normal file
9
extensions/vscode-test-resolver/tsconfig.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "../shared.tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out"
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
]
|
||||
}
|
||||
1879
extensions/vscode-test-resolver/yarn.lock
Normal file
1879
extensions/vscode-test-resolver/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
typescript@3.5.0-dev.20190517:
|
||||
version "3.5.0-dev.20190517"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.0-dev.20190517.tgz#5a85f1091cf33fde39b04f898c5730e30edd3e39"
|
||||
integrity sha512-KoBHq6ytEApXKTDtmTu4Sp/tC5SPe4FpvwutLEANhwdMPblqZdh7APuH7I/ceMlgfHSa7B00JgF7NokUJQi0/g==
|
||||
typescript@3.5.0-dev.20190522:
|
||||
version "3.5.0-dev.20190522"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.0-dev.20190522.tgz#ade4702c6e599a7e8905d7acaaf416f7e32ba0fc"
|
||||
integrity sha512-V+QsNMtXl8lGwov4O04D+E6vtiL0TWEpz6iDxI2tAZizEn7y8Hh9SXzz3JRWepr7dfdqqxEv9CT3J18zpaZKJQ==
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
"native-keymap": "1.2.5",
|
||||
"native-watchdog": "1.0.0",
|
||||
"ng2-charts": "^1.6.0",
|
||||
"node-pty": "0.8.1",
|
||||
"node-pty": "0.9.0-beta9",
|
||||
"pretty-data": "^0.40.0",
|
||||
"reflect-metadata": "^0.1.8",
|
||||
"rxjs": "5.4.0",
|
||||
@@ -76,7 +76,7 @@
|
||||
"vscode-ripgrep": "^1.2.5",
|
||||
"vscode-sqlite3": "4.0.7",
|
||||
"vscode-textmate": "^4.0.1",
|
||||
"vscode-xterm": "3.14.0-beta2",
|
||||
"vscode-xterm": "3.14.0-beta3",
|
||||
"yauzl": "^2.9.1",
|
||||
"yazl": "^2.4.3",
|
||||
"zone.js": "^0.8.4"
|
||||
@@ -163,7 +163,6 @@
|
||||
"typemoq": "^0.3.2",
|
||||
"typescript": "3.4.5",
|
||||
"typescript-formatter": "7.1.0",
|
||||
"typescript-tslint-plugin": "^0.0.7",
|
||||
"uglify-es": "^3.0.18",
|
||||
"underscore": "^1.8.2",
|
||||
"vinyl": "^2.0.0",
|
||||
|
||||
3
remote/.yarnrc
Normal file
3
remote/.yarnrc
Normal file
@@ -0,0 +1,3 @@
|
||||
disturl "http://nodejs.org/dist"
|
||||
target "10.2.1"
|
||||
runtime "node"
|
||||
20
remote/installDevModules.sh
Executable file
20
remote/installDevModules.sh
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
set -ex
|
||||
|
||||
# Install Node and Yarn
|
||||
export NODE_VERSION=10.2.1
|
||||
export YARN_VERSION=1.10.1
|
||||
curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz"
|
||||
curl -fsSLO --compressed "https://yarnpkg.com/downloads/$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz"
|
||||
tar -xJf "node-v$NODE_VERSION-linux-x64.tar.xz" -C "$HOME" --no-same-owner
|
||||
tar -xzf "yarn-v$YARN_VERSION.tar.gz" -C "$HOME"
|
||||
mkdir -p "$HOME/bin"
|
||||
ln -s "$HOME/node-v$NODE_VERSION-linux-x64/bin/node" "$HOME/bin/node"
|
||||
ln -s "$HOME/yarn-v$YARN_VERSION/bin/yarn" "$HOME/bin/yarn"
|
||||
ln -s "$HOME/yarn-v$YARN_VERSION/bin/yarnpkg" "$HOME/bin/yarnpkg"
|
||||
rm "node-v$NODE_VERSION-linux-x64.tar.xz" "yarn-v$YARN_VERSION.tar.gz"
|
||||
|
||||
# Compile native /remote node_modules
|
||||
PATH="$HOME/bin:$PATH" \
|
||||
PYTHON=/usr/bin/python2.7 \
|
||||
yarn --ignore-optional
|
||||
16
remote/installDevPackagesAsRoot.sh
Executable file
16
remote/installDevPackagesAsRoot.sh
Executable file
@@ -0,0 +1,16 @@
|
||||
#!/bin/bash
|
||||
set -ex
|
||||
|
||||
# Install libraries and tools
|
||||
apt-get update
|
||||
apt-get install -y \
|
||||
curl \
|
||||
make \
|
||||
gcc \
|
||||
g++ \
|
||||
python2.7 \
|
||||
libx11-dev \
|
||||
libxkbfile-dev \
|
||||
libsecret-1-dev \
|
||||
xz-utils
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
17
remote/launchDevMode.sh
Executable file
17
remote/launchDevMode.sh
Executable file
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
export NODE_ENV=development
|
||||
export VSCODE_DEV=1
|
||||
export VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH="$HOME/.vscode-remote/bin/dev-remote/node_modules"
|
||||
|
||||
cd $VSCODE_REPO
|
||||
|
||||
if [ -z "$extensions" ] ; then
|
||||
echo No extensions to install.
|
||||
mkdir -p /root/.vscode-remote
|
||||
else
|
||||
(PATH="$HOME/bin:$PATH" node out/remoteExtensionHostAgent.js ${VSCODE_TELEMETRY_ARG} ${extensions} || true)
|
||||
fi
|
||||
|
||||
PATH="$HOME/bin:$PATH" node out/remoteExtensionHostAgent.js ${VSCODE_TELEMETRY_ARG} --port $PORT
|
||||
28
remote/package.json
Normal file
28
remote/package.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "vscode-reh",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"applicationinsights": "1.0.8",
|
||||
"getmac": "^1.4.6",
|
||||
"graceful-fs": "4.1.11",
|
||||
"http-proxy-agent": "^2.1.0",
|
||||
"https-proxy-agent": "^2.2.1",
|
||||
"iconv-lite": "0.4.23",
|
||||
"jschardet": "1.6.0",
|
||||
"keytar": "4.2.1",
|
||||
"minimist": "1.2.0",
|
||||
"native-watchdog": "1.0.0",
|
||||
"node-pty": "0.8.1",
|
||||
"semver": "^5.5.0",
|
||||
"spdlog": "0.8.1",
|
||||
"vscode-chokidar": "1.6.5",
|
||||
"vscode-nsfw": "1.1.1",
|
||||
"vscode-proxy-agent": "0.4.0",
|
||||
"vscode-ripgrep": "^1.2.5",
|
||||
"yauzl": "^2.9.1",
|
||||
"yazl": "^2.4.3"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"vscode-windows-ca-certs": "0.1.0"
|
||||
}
|
||||
}
|
||||
1129
remote/yarn.lock
Normal file
1129
remote/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@@ -3,12 +3,22 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
const httpServer = require('http-server');
|
||||
const opn = require('opn');
|
||||
const cp = require('child_process');
|
||||
const path = require('path');
|
||||
|
||||
const url = 'http://127.0.0.1:8080/out/vs/code/browser/workbench/workbench.html';
|
||||
const proc = cp.execFile(path.join(__dirname, process.platform === 'win32' ? 'remoteExtensionAgent.bat' : 'remoteExtensionAgent.sh'));
|
||||
|
||||
httpServer.createServer({ root: '.', cache: 5 }).listen(8080);
|
||||
console.log(`Open ${url} in your browser`);
|
||||
let launched = false;
|
||||
proc.stdout.on("data", data => {
|
||||
if (!launched && data.toString().indexOf('Extension host agent listening on 8000')) {
|
||||
launched = true;
|
||||
|
||||
opn(url);
|
||||
setTimeout(() => {
|
||||
const url = 'http://127.0.0.1:8000';
|
||||
console.log(`Open ${url} in your browser`);
|
||||
|
||||
opn(url);
|
||||
}, 100);
|
||||
}
|
||||
});
|
||||
169
src/typings/node-pty.d.ts
vendored
169
src/typings/node-pty.d.ts
vendored
@@ -1,5 +1,6 @@
|
||||
/**
|
||||
* Copyright (c) 2017, Daniel Imms (Source EULA).
|
||||
* Copyright (c) 2018, Microsoft Corporation (Source EULA).
|
||||
*/
|
||||
|
||||
declare module 'node-pty' {
|
||||
@@ -11,76 +12,132 @@ declare module 'node-pty' {
|
||||
* escaped properly.
|
||||
* @param options The options of the terminal.
|
||||
* @see CommandLineToArgvW https://msdn.microsoft.com/en-us/library/windows/desktop/bb776391(v=vs.85).aspx
|
||||
* @see Parsing C++ Command-Line Arguments https://msdn.microsoft.com/en-us/library/17w5ykft.aspx
|
||||
* @see Parsing C++ Comamnd-Line Arguments https://msdn.microsoft.com/en-us/library/17w5ykft.aspx
|
||||
* @see GetCommandLine https://msdn.microsoft.com/en-us/library/windows/desktop/ms683156.aspx
|
||||
*/
|
||||
export function spawn(file: string, args: string[] | string, options: IPtyForkOptions): IPty;
|
||||
export function spawn(file: string, args: string[] | string, options: IPtyForkOptions | IWindowsPtyForkOptions): IPty;
|
||||
|
||||
export interface IPtyForkOptions {
|
||||
name?: string;
|
||||
cols?: number;
|
||||
rows?: number;
|
||||
cwd?: string;
|
||||
env?: { [key: string]: string };
|
||||
uid?: number;
|
||||
gid?: number;
|
||||
encoding?: string;
|
||||
/**
|
||||
* Whether to use the experimental ConPTY system on Windows. This setting will be ignored on
|
||||
* non-Windows.
|
||||
*/
|
||||
experimentalUseConpty?: boolean;
|
||||
name?: string;
|
||||
cols?: number;
|
||||
rows?: number;
|
||||
cwd?: string;
|
||||
env?: { [key: string]: string };
|
||||
uid?: number;
|
||||
gid?: number;
|
||||
encoding?: string;
|
||||
}
|
||||
|
||||
export interface IWindowsPtyForkOptions {
|
||||
name?: string;
|
||||
cols?: number;
|
||||
rows?: number;
|
||||
cwd?: string;
|
||||
env?: { [key: string]: string };
|
||||
encoding?: string;
|
||||
/**
|
||||
* Whether to use the experimental ConPTY system on Windows. When this is not set, ConPTY will
|
||||
* be used when the Windows build number is >= 18309 (it's available in 17134 and 17692 but is
|
||||
* too unstable to enable by default).
|
||||
*
|
||||
* This setting does nothing on non-Windows.
|
||||
*/
|
||||
experimentalUseConpty?: boolean;
|
||||
/**
|
||||
* Whether to use PSEUDOCONSOLE_INHERIT_CURSOR in conpty.
|
||||
* @see https://docs.microsoft.com/en-us/windows/console/createpseudoconsole
|
||||
*/
|
||||
conptyInheritCursor?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface representing a pseudoterminal, on Windows this is emulated via the winpty library.
|
||||
*/
|
||||
export interface IPty {
|
||||
/**
|
||||
* The process ID of the outer process.
|
||||
*/
|
||||
pid: number;
|
||||
/**
|
||||
* The process ID of the outer process.
|
||||
*/
|
||||
readonly pid: number;
|
||||
|
||||
/**
|
||||
* The title of the active process.
|
||||
*/
|
||||
process: string;
|
||||
/**
|
||||
* The column size in characters.
|
||||
*/
|
||||
readonly cols: number;
|
||||
|
||||
/**
|
||||
* Adds a listener to the data event, fired when data is returned from the pty.
|
||||
* @param event The name of the event.
|
||||
* @param listener The callback function.
|
||||
*/
|
||||
on(event: 'data', listener: (data: string) => void): void;
|
||||
/**
|
||||
* The row size in characters.
|
||||
*/
|
||||
readonly rows: number;
|
||||
|
||||
/**
|
||||
* Adds a listener to the exit event, fired when the pty exits.
|
||||
* @param event The name of the event.
|
||||
* @param listener The callback function, exitCode is the exit code of the process and signal is
|
||||
* the signal that triggered the exit. signal is not supported on Windows.
|
||||
*/
|
||||
on(event: 'exit', listener: (exitCode: number, signal?: number) => void): void;
|
||||
/**
|
||||
* The title of the active process.
|
||||
*/
|
||||
readonly process: string;
|
||||
|
||||
/**
|
||||
* Resizes the dimensions of the pty.
|
||||
* @param columns The number of columns to use.
|
||||
* @param rows The number of rows to use.
|
||||
*/
|
||||
resize(columns: number, rows: number): void;
|
||||
/**
|
||||
* Adds an event listener for when a data event fires. This happens when data is returned from
|
||||
* the pty.
|
||||
* @returns an `IDisposable` to stop listening.
|
||||
*/
|
||||
readonly onData: IEvent<string>;
|
||||
|
||||
/**
|
||||
* Writes data to the pty.
|
||||
* @param data The data to write.
|
||||
*/
|
||||
write(data: string): void;
|
||||
/**
|
||||
* Adds an event listener for when an exit event fires. This happens when the pty exits.
|
||||
* @returns an `IDisposable` to stop listening.
|
||||
*/
|
||||
readonly onExit: IEvent<{ exitCode: number, signal?: number }>;
|
||||
|
||||
/**
|
||||
* Kills the pty.
|
||||
* @param signal The signal to use, defaults to SIGHUP. If the TIOCSIG/TIOCSIGNAL ioctl is not
|
||||
* supported then the process will be killed instead. This parameter is not supported on
|
||||
* Windows.
|
||||
* @throws Will throw when signal is used on Windows.
|
||||
*/
|
||||
kill(signal?: string): void;
|
||||
/**
|
||||
* Adds a listener to the data event, fired when data is returned from the pty.
|
||||
* @param event The name of the event.
|
||||
* @param listener The callback function.
|
||||
* @deprecated Use IPty.onData
|
||||
*/
|
||||
on(event: 'data', listener: (data: string) => void): void;
|
||||
|
||||
/**
|
||||
* Adds a listener to the exit event, fired when the pty exits.
|
||||
* @param event The name of the event.
|
||||
* @param listener The callback function, exitCode is the exit code of the process and signal is
|
||||
* the signal that triggered the exit. signal is not supported on Windows.
|
||||
* @deprecated Use IPty.onExit
|
||||
*/
|
||||
on(event: 'exit', listener: (exitCode: number, signal?: number) => void): void;
|
||||
|
||||
/**
|
||||
* Resizes the dimensions of the pty.
|
||||
* @param columns THe number of columns to use.
|
||||
* @param rows The number of rows to use.
|
||||
*/
|
||||
resize(columns: number, rows: number): void;
|
||||
|
||||
/**
|
||||
* Writes data to the pty.
|
||||
* @param data The data to write.
|
||||
*/
|
||||
write(data: string): void;
|
||||
|
||||
/**
|
||||
* Kills the pty.
|
||||
* @param signal The signal to use, defaults to SIGHUP. This parameter is not supported on
|
||||
* Windows.
|
||||
* @throws Will throw when signal is used on Windows.
|
||||
*/
|
||||
kill(signal?: string): void;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An object that can be disposed via a dispose function.
|
||||
*/
|
||||
export interface IDisposable {
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* An event that can be listened to.
|
||||
* @returns an `IDisposable` to stop listening.
|
||||
*/
|
||||
export interface IEvent<T> {
|
||||
(listener: (e: T) => any): IDisposable;
|
||||
}
|
||||
}
|
||||
|
||||
8
src/typings/vscode-xterm.d.ts
vendored
8
src/typings/vscode-xterm.d.ts
vendored
@@ -1025,9 +1025,11 @@ declare module 'vscode-xterm' {
|
||||
|
||||
charMeasure?: { height: number, width: number };
|
||||
|
||||
renderer: {
|
||||
_renderLayers: any[];
|
||||
onIntersectionChange: any;
|
||||
_renderCoordinator: {
|
||||
_renderer: {
|
||||
_renderLayers: any[];
|
||||
};
|
||||
_onIntersectionChange: any;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import { ButtonGroup, IButtonStyles } from 'vs/base/browser/ui/button/button';
|
||||
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { mnemonicButtonLabel } from 'vs/base/common/labels';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
|
||||
export interface IDialogOptions {
|
||||
cancelId?: number;
|
||||
@@ -30,6 +31,11 @@ export interface IDialogStyles extends IButtonStyles {
|
||||
dialogBorder?: Color;
|
||||
}
|
||||
|
||||
interface ButtonMapEntry {
|
||||
label: string;
|
||||
index: number;
|
||||
}
|
||||
|
||||
export class Dialog extends Disposable {
|
||||
private element: HTMLElement | undefined;
|
||||
private modal: HTMLElement | undefined;
|
||||
@@ -92,12 +98,13 @@ export class Dialog extends Disposable {
|
||||
|
||||
let focusedButton = 0;
|
||||
this.buttonGroup = new ButtonGroup(this.buttonsContainer, this.buttons.length, { title: true });
|
||||
const buttonMap = this.rearrangeButtons(this.buttons, this.options.cancelId);
|
||||
this.buttonGroup.buttons.forEach((button, index) => {
|
||||
button.label = mnemonicButtonLabel(this.buttons[index], true);
|
||||
button.label = mnemonicButtonLabel(buttonMap[index].label, true);
|
||||
|
||||
this._register(button.onDidClick(e => {
|
||||
EventHelper.stop(e);
|
||||
resolve(index);
|
||||
resolve(buttonMap[index].index);
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -228,4 +235,22 @@ export class Dialog extends Disposable {
|
||||
this.focusToReturn = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private rearrangeButtons(buttons: Array<string>, cancelId: number | undefined): ButtonMapEntry[] {
|
||||
const buttonMap: ButtonMapEntry[] = [];
|
||||
// Maps each button to its current label and old index so that when we move them around it's not a problem
|
||||
buttons.forEach((button, index) => {
|
||||
buttonMap.push({ label: button, index: index });
|
||||
});
|
||||
|
||||
if (isMacintosh) {
|
||||
if (cancelId !== undefined) {
|
||||
const cancelButton = buttonMap.splice(cancelId, 1)[0];
|
||||
buttonMap.reverse();
|
||||
buttonMap.splice(buttonMap.length - 1, 0, cancelButton);
|
||||
}
|
||||
}
|
||||
|
||||
return buttonMap;
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,8 @@ export interface IListVirtualDelegate<T> {
|
||||
export interface IListRenderer<T, TTemplateData> {
|
||||
templateId: string;
|
||||
renderTemplate(container: HTMLElement): TTemplateData;
|
||||
renderElement(element: T, index: number, templateData: TTemplateData, dynamicHeightProbing?: boolean): void;
|
||||
disposeElement?(element: T, index: number, templateData: TTemplateData, dynamicHeightProbing?: boolean): void;
|
||||
renderElement(element: T, index: number, templateData: TTemplateData, height: number | undefined): void;
|
||||
disposeElement?(element: T, index: number, templateData: TTemplateData, height: number | undefined): void;
|
||||
disposeTemplate(templateData: TTemplateData): void;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ class PagedRenderer<TElement, TTemplateData> implements IListRenderer<number, IT
|
||||
return { data, disposable: { dispose: () => { } } };
|
||||
}
|
||||
|
||||
renderElement(index: number, _: number, data: ITemplateData<TTemplateData>, dynamicHeightProbing?: boolean): void {
|
||||
renderElement(index: number, _: number, data: ITemplateData<TTemplateData>, height: number | undefined): void {
|
||||
if (data.disposable) {
|
||||
data.disposable.dispose();
|
||||
}
|
||||
@@ -47,7 +47,7 @@ class PagedRenderer<TElement, TTemplateData> implements IListRenderer<number, IT
|
||||
const model = this.modelProvider();
|
||||
|
||||
if (model.isResolved(index)) {
|
||||
return this.renderer.renderElement(model.get(index), index, data.data, dynamicHeightProbing);
|
||||
return this.renderer.renderElement(model.get(index), index, data.data, height);
|
||||
}
|
||||
|
||||
const cts = new CancellationTokenSource();
|
||||
@@ -55,7 +55,7 @@ class PagedRenderer<TElement, TTemplateData> implements IListRenderer<number, IT
|
||||
data.disposable = { dispose: () => cts.cancel() };
|
||||
|
||||
this.renderer.renderPlaceholder(index, data.data);
|
||||
promise.then(entry => this.renderer.renderElement(entry, index, data.data!, dynamicHeightProbing));
|
||||
promise.then(entry => this.renderer.renderElement(entry, index, data.data!, height));
|
||||
}
|
||||
|
||||
disposeTemplate(data: ITemplateData<TTemplateData>): void {
|
||||
|
||||
@@ -586,7 +586,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
|
||||
}
|
||||
|
||||
if (renderer) {
|
||||
renderer.renderElement(item.element, index, item.row.templateData);
|
||||
renderer.renderElement(item.element, index, item.row.templateData, item.size);
|
||||
}
|
||||
|
||||
const uri = this.dnd.getDragURI(item.element);
|
||||
@@ -647,7 +647,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
|
||||
|
||||
const renderer = this.renderers.get(item.templateId);
|
||||
if (renderer && renderer.disposeElement) {
|
||||
renderer.disposeElement(item.element, index, item.row!.templateData);
|
||||
renderer.disposeElement(item.element, index, item.row!.templateData, item.size);
|
||||
}
|
||||
|
||||
this.cache.release(item.row!);
|
||||
@@ -1088,10 +1088,10 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
|
||||
|
||||
const renderer = this.renderers.get(item.templateId);
|
||||
if (renderer) {
|
||||
renderer.renderElement(item.element, index, row.templateData, true);
|
||||
renderer.renderElement(item.element, index, row.templateData, undefined);
|
||||
|
||||
if (renderer.disposeElement) {
|
||||
renderer.disposeElement(item.element, index, row.templateData, true);
|
||||
renderer.disposeElement(item.element, index, row.templateData, undefined);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -979,20 +979,20 @@ class PipelineRenderer<T> implements IListRenderer<T, any> {
|
||||
return this.renderers.map(r => r.renderTemplate(container));
|
||||
}
|
||||
|
||||
renderElement(element: T, index: number, templateData: any[], dynamicHeightProbing?: boolean): void {
|
||||
renderElement(element: T, index: number, templateData: any[], height: number | undefined): void {
|
||||
let i = 0;
|
||||
|
||||
for (const renderer of this.renderers) {
|
||||
renderer.renderElement(element, index, templateData[i++], dynamicHeightProbing);
|
||||
renderer.renderElement(element, index, templateData[i++], height);
|
||||
}
|
||||
}
|
||||
|
||||
disposeElement(element: T, index: number, templateData: any[], dynamicHeightProbing?: boolean): void {
|
||||
disposeElement(element: T, index: number, templateData: any[], height: number | undefined): void {
|
||||
let i = 0;
|
||||
|
||||
for (const renderer of this.renderers) {
|
||||
if (renderer.disposeElement) {
|
||||
renderer.disposeElement(element, index, templateData[i], dynamicHeightProbing);
|
||||
renderer.disposeElement(element, index, templateData[i], height);
|
||||
}
|
||||
|
||||
i += 1;
|
||||
|
||||
@@ -105,6 +105,15 @@ export class Menu extends ActionBar {
|
||||
|
||||
this.menuDisposables = [];
|
||||
|
||||
addDisposableListener(menuElement, EventType.KEY_DOWN, (e) => {
|
||||
const event = new StandardKeyboardEvent(e);
|
||||
|
||||
// Stop tab navigation of menus
|
||||
if (event.equals(KeyCode.Tab)) {
|
||||
EventHelper.stop(e, true);
|
||||
}
|
||||
});
|
||||
|
||||
if (options.enableMnemonics) {
|
||||
this.menuDisposables.push(addDisposableListener(menuElement, EventType.KEY_DOWN, (e) => {
|
||||
const key = e.key.toLocaleLowerCase();
|
||||
|
||||
@@ -240,8 +240,8 @@ class TreeRenderer<T, TFilterData, TTemplateData> implements IListRenderer<ITree
|
||||
return { container, twistie, templateData };
|
||||
}
|
||||
|
||||
renderElement(node: ITreeNode<T, TFilterData>, index: number, templateData: ITreeListTemplateData<TTemplateData>, dynamicHeightProbing?: boolean): void {
|
||||
if (!dynamicHeightProbing) {
|
||||
renderElement(node: ITreeNode<T, TFilterData>, index: number, templateData: ITreeListTemplateData<TTemplateData>, height: number | undefined): void {
|
||||
if (typeof height === 'number') {
|
||||
this.renderedNodes.set(node, templateData);
|
||||
this.renderedElements.set(node.element, node);
|
||||
}
|
||||
@@ -250,15 +250,15 @@ class TreeRenderer<T, TFilterData, TTemplateData> implements IListRenderer<ITree
|
||||
templateData.twistie.style.marginLeft = `${indent}px`;
|
||||
this.update(node, templateData);
|
||||
|
||||
this.renderer.renderElement(node, index, templateData.templateData, dynamicHeightProbing);
|
||||
this.renderer.renderElement(node, index, templateData.templateData, height);
|
||||
}
|
||||
|
||||
disposeElement(node: ITreeNode<T, TFilterData>, index: number, templateData: ITreeListTemplateData<TTemplateData>, dynamicHeightProbing?: boolean): void {
|
||||
disposeElement(node: ITreeNode<T, TFilterData>, index: number, templateData: ITreeListTemplateData<TTemplateData>, height: number | undefined): void {
|
||||
if (this.renderer.disposeElement) {
|
||||
this.renderer.disposeElement(node, index, templateData.templateData, dynamicHeightProbing);
|
||||
this.renderer.disposeElement(node, index, templateData.templateData, height);
|
||||
}
|
||||
|
||||
if (!dynamicHeightProbing) {
|
||||
if (typeof height === 'number') {
|
||||
this.renderedNodes.delete(node);
|
||||
this.renderedElements.delete(node.element);
|
||||
}
|
||||
|
||||
@@ -98,8 +98,8 @@ class DataTreeRenderer<TInput, T, TFilterData, TTemplateData> implements ITreeRe
|
||||
return { templateData };
|
||||
}
|
||||
|
||||
renderElement(node: ITreeNode<IAsyncDataTreeNode<TInput, T>, TFilterData>, index: number, templateData: IDataTreeListTemplateData<TTemplateData>, dynamicHeightProbing?: boolean): void {
|
||||
this.renderer.renderElement(new AsyncDataTreeNodeWrapper(node), index, templateData.templateData, dynamicHeightProbing);
|
||||
renderElement(node: ITreeNode<IAsyncDataTreeNode<TInput, T>, TFilterData>, index: number, templateData: IDataTreeListTemplateData<TTemplateData>, height: number | undefined): void {
|
||||
this.renderer.renderElement(new AsyncDataTreeNodeWrapper(node), index, templateData.templateData, height);
|
||||
}
|
||||
|
||||
renderTwistie(element: IAsyncDataTreeNode<TInput, T>, twistieElement: HTMLElement): boolean {
|
||||
@@ -107,9 +107,9 @@ class DataTreeRenderer<TInput, T, TFilterData, TTemplateData> implements ITreeRe
|
||||
return false;
|
||||
}
|
||||
|
||||
disposeElement(node: ITreeNode<IAsyncDataTreeNode<TInput, T>, TFilterData>, index: number, templateData: IDataTreeListTemplateData<TTemplateData>, dynamicHeightProbing?: boolean): void {
|
||||
disposeElement(node: ITreeNode<IAsyncDataTreeNode<TInput, T>, TFilterData>, index: number, templateData: IDataTreeListTemplateData<TTemplateData>, height: number | undefined): void {
|
||||
if (this.renderer.disposeElement) {
|
||||
this.renderer.disposeElement(new AsyncDataTreeNodeWrapper(node), index, templateData.templateData, dynamicHeightProbing);
|
||||
this.renderer.disposeElement(new AsyncDataTreeNodeWrapper(node), index, templateData.templateData, height);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,12 @@ export function createCancelablePromise<T>(callback: (token: CancellationToken)
|
||||
};
|
||||
}
|
||||
|
||||
export function raceCancellation<T>(promise: Promise<T>, token: CancellationToken): Promise<T | undefined>;
|
||||
export function raceCancellation<T>(promise: Promise<T>, token: CancellationToken, defaultValue: T): Promise<T>;
|
||||
export function raceCancellation<T>(promise: Promise<T>, token: CancellationToken, defaultValue?: T): Promise<T> {
|
||||
return Promise.race([promise, new Promise<T>(resolve => token.onCancellationRequested(() => resolve(defaultValue)))]);
|
||||
}
|
||||
|
||||
export function asPromise<T>(callback: () => T | Thenable<T>): Promise<T> {
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
const item = callback();
|
||||
@@ -767,4 +773,4 @@ export async function retry<T>(task: ITask<Promise<T>>, delay: number, retries:
|
||||
}
|
||||
|
||||
return Promise.reject(lastError);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -362,25 +362,6 @@ export function matchesFuzzy2(pattern: string, word: string): IMatch[] | null {
|
||||
return score ? createMatches(score) : null;
|
||||
}
|
||||
|
||||
export function anyScore(pattern: string, lowPattern: string, _patternPos: number, word: string, lowWord: string, _wordPos: number): FuzzyScore {
|
||||
const result = fuzzyScore(pattern, lowPattern, 0, word, lowWord, 0, true);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
let matches = 0;
|
||||
let score = 0;
|
||||
let idx = _wordPos;
|
||||
for (let patternPos = 0; patternPos < lowPattern.length && patternPos < _maxLen; ++patternPos) {
|
||||
const wordPos = lowWord.indexOf(lowPattern.charAt(patternPos), idx);
|
||||
if (wordPos >= 0) {
|
||||
score += 1;
|
||||
matches += 2 ** wordPos;
|
||||
idx = wordPos + 1;
|
||||
}
|
||||
}
|
||||
return [score, matches, _wordPos];
|
||||
}
|
||||
|
||||
//#region --- fuzzyScore ---
|
||||
|
||||
export function createMatches(score: undefined | FuzzyScore): IMatch[] {
|
||||
|
||||
@@ -11,5 +11,11 @@
|
||||
</body>
|
||||
|
||||
<!-- Startup via workbench.js -->
|
||||
<script src="../../../../../out/vs/code/browser/workbench/workbench.js"></script>
|
||||
<script>
|
||||
self.CONNECTION_AUTH_TOKEN = '{{CONNECTION_AUTH_TOKEN}}';
|
||||
self.USER_HOME_DIR = '{{USER_HOME_DIR}}';
|
||||
</script>
|
||||
|
||||
<!-- Startup via workbench.js -->
|
||||
<script src="./out/vs/code/browser/workbench/workbench.js"></script>
|
||||
</html>
|
||||
@@ -17,7 +17,7 @@
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
|
||||
loadScript('../../../../../out/vs/loader.js', function () {
|
||||
loadScript('./out/vs/loader.js', function () {
|
||||
|
||||
// @ts-ignore
|
||||
require.config({
|
||||
|
||||
@@ -33,7 +33,7 @@ import { EnvironmentService } from 'vs/platform/environment/node/environmentServ
|
||||
import { IssueReporterModel, IssueReporterData as IssueReporterModelData } from 'vs/code/electron-browser/issue/issueReporterModel';
|
||||
import { IssueReporterData, IssueReporterStyles, IssueType, ISettingsSearchIssueReporterData, IssueReporterFeatures, IssueReporterExtensionData } from 'vs/platform/issue/common/issue';
|
||||
import BaseHtml from 'vs/code/electron-browser/issue/issueReporterPage';
|
||||
import { createSpdLogService } from 'vs/platform/log/node/spdlogService';
|
||||
import { createBufferSpdLogService } from 'vs/platform/log/node/spdlogService';
|
||||
import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/node/logIpc';
|
||||
import { ILogService, getLogLevel } from 'vs/platform/log/common/log';
|
||||
import { OcticonLabel } from 'vs/base/browser/ui/octiconLabel/octiconLabel';
|
||||
@@ -300,7 +300,7 @@ export class IssueReporter extends Disposable {
|
||||
serviceCollection.set(IWindowsService, new WindowsService(mainProcessService));
|
||||
this.environmentService = new EnvironmentService(configuration, configuration.execPath);
|
||||
|
||||
const logService = createSpdLogService(`issuereporter${configuration.windowId}`, getLogLevel(this.environmentService), this.environmentService.logsPath);
|
||||
const logService = createBufferSpdLogService(`issuereporter${configuration.windowId}`, getLogLevel(this.environmentService), this.environmentService.logsPath);
|
||||
const logLevelClient = new LogLevelSetterChannelClient(mainProcessService.getChannel('loglevel'));
|
||||
this.logService = new FollowerLogService(logLevelClient, logService);
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppen
|
||||
import { IWindowsService, ActiveWindowManager } from 'vs/platform/windows/common/windows';
|
||||
import { WindowsService } from 'vs/platform/windows/electron-browser/windowsService';
|
||||
import { ipcRenderer } from 'electron';
|
||||
import { createSpdLogService } from 'vs/platform/log/node/spdlogService';
|
||||
import { createBufferSpdLogService } from 'vs/platform/log/node/spdlogService';
|
||||
import { ILogService, LogLevel } from 'vs/platform/log/common/log';
|
||||
import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/node/logIpc';
|
||||
import { LocalizationsService } from 'vs/platform/localizations/node/localizations';
|
||||
@@ -94,7 +94,7 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I
|
||||
|
||||
const mainRouter = new StaticRouter(ctx => ctx === 'main');
|
||||
const logLevelClient = new LogLevelSetterChannelClient(server.getChannel('loglevel', mainRouter));
|
||||
const logService = new FollowerLogService(logLevelClient, createSpdLogService('sharedprocess', initData.logLevel, environmentService.logsPath));
|
||||
const logService = new FollowerLogService(logLevelClient, createBufferSpdLogService('sharedprocess', initData.logLevel, environmentService.logsPath));
|
||||
disposables.push(logService);
|
||||
|
||||
logService.info('main', JSON.stringify(configuration));
|
||||
@@ -122,7 +122,7 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I
|
||||
const services = new ServiceCollection();
|
||||
const environmentService = accessor.get(IEnvironmentService);
|
||||
const { appRoot, extensionsPath, extensionDevelopmentLocationURI: extensionDevelopmentLocationURI, isBuilt, installSourcePath } = environmentService;
|
||||
const telemetryLogService = new FollowerLogService(logLevelClient, createSpdLogService('telemetry', initData.logLevel, environmentService.logsPath));
|
||||
const telemetryLogService = new FollowerLogService(logLevelClient, createBufferSpdLogService('telemetry', initData.logLevel, environmentService.logsPath));
|
||||
telemetryLogService.info('The below are logs for every telemetry event sent from VS Code once the log level is set to trace.');
|
||||
telemetryLogService.info('===========================================================');
|
||||
|
||||
|
||||
@@ -288,7 +288,7 @@ function startup(args: ParsedArgs): void {
|
||||
return Promise.reject(error);
|
||||
})
|
||||
.then(mainIpcServer => {
|
||||
bufferLogService.logger = createSpdLogService('main', bufferLogService.getLevel(), environmentService.logsPath);
|
||||
createSpdLogService('main', bufferLogService.getLevel(), environmentService.logsPath).then(logger => bufferLogService.logger = logger);
|
||||
|
||||
return instantiationService.createInstance(CodeApplication, mainIpcServer, instanceEnvironment).startup();
|
||||
});
|
||||
|
||||
@@ -31,7 +31,7 @@ import { mkdirp, writeFile } from 'vs/base/node/pfs';
|
||||
import { getBaseLabel } from 'vs/base/common/labels';
|
||||
import { IStateService } from 'vs/platform/state/common/state';
|
||||
import { StateService } from 'vs/platform/state/node/stateService';
|
||||
import { createSpdLogService } from 'vs/platform/log/node/spdlogService';
|
||||
import { createBufferSpdLogService } from 'vs/platform/log/node/spdlogService';
|
||||
import { ILogService, getLogLevel } from 'vs/platform/log/common/log';
|
||||
import { isPromiseCanceledError } from 'vs/base/common/errors';
|
||||
import { areSameExtensions, adoptToGalleryExtensionId, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
@@ -291,7 +291,7 @@ export function main(argv: ParsedArgs): Promise<void> {
|
||||
const services = new ServiceCollection();
|
||||
|
||||
const environmentService = new EnvironmentService(argv, process.execPath);
|
||||
const logService = createSpdLogService('cli', getLogLevel(environmentService), environmentService.logsPath);
|
||||
const logService = createBufferSpdLogService('cli', getLogLevel(environmentService), environmentService.logsPath);
|
||||
process.once('exit', () => logService.dispose());
|
||||
|
||||
logService.info('main', argv);
|
||||
|
||||
@@ -10,6 +10,7 @@ import { Range } from 'vs/editor/common/core/range';
|
||||
import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { EditorKeybindingCancellationTokenSource } from 'vs/editor/browser/core/keybindingCancellation';
|
||||
|
||||
export const enum CodeEditorStateFlag {
|
||||
Value = 1,
|
||||
@@ -78,12 +79,12 @@ export class EditorState {
|
||||
* A cancellation token source that cancels when the editor changes as expressed
|
||||
* by the provided flags
|
||||
*/
|
||||
export class EditorStateCancellationTokenSource extends CancellationTokenSource {
|
||||
export class EditorStateCancellationTokenSource extends EditorKeybindingCancellationTokenSource {
|
||||
|
||||
private readonly _listener: IDisposable[] = [];
|
||||
|
||||
constructor(readonly editor: IActiveCodeEditor, flags: CodeEditorStateFlag, parent?: CancellationToken) {
|
||||
super(parent);
|
||||
super(editor, parent);
|
||||
|
||||
if (flags & CodeEditorStateFlag.Position) {
|
||||
this._listener.push(editor.onDidChangeCursorPosition(_ => this.cancel()));
|
||||
|
||||
105
src/vs/editor/browser/core/keybindingCancellation.ts
Normal file
105
src/vs/editor/browser/core/keybindingCancellation.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { EditorCommand, registerEditorCommand } from 'vs/editor/browser/editorExtensions';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { LinkedList } from 'vs/base/common/linkedList';
|
||||
import { createDecorator, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
|
||||
|
||||
const IEditorCancellationTokens = createDecorator<IEditorCancellationTokens>('IEditorCancelService');
|
||||
|
||||
interface IEditorCancellationTokens {
|
||||
_serviceBrand: any;
|
||||
add(editor: ICodeEditor, cts: CancellationTokenSource): () => void;
|
||||
cancel(editor: ICodeEditor): void;
|
||||
}
|
||||
|
||||
const ctxCancellableOperation = new RawContextKey('cancellableOperation', false);
|
||||
|
||||
registerSingleton(IEditorCancellationTokens, class implements IEditorCancellationTokens {
|
||||
|
||||
_serviceBrand: any;
|
||||
|
||||
private readonly _tokens = new WeakMap<ICodeEditor, { key: IContextKey<boolean>, tokens: LinkedList<CancellationTokenSource> }>();
|
||||
|
||||
add(editor: ICodeEditor, cts: CancellationTokenSource): () => void {
|
||||
let data = this._tokens.get(editor);
|
||||
if (!data) {
|
||||
data = editor.invokeWithinContext(accessor => {
|
||||
const key = ctxCancellableOperation.bindTo(accessor.get(IContextKeyService));
|
||||
const tokens = new LinkedList<CancellationTokenSource>();
|
||||
return { key, tokens };
|
||||
});
|
||||
this._tokens.set(editor, data);
|
||||
}
|
||||
|
||||
let removeFn: Function | undefined;
|
||||
|
||||
data.key.set(true);
|
||||
removeFn = data.tokens.push(cts);
|
||||
|
||||
return () => {
|
||||
// remove w/o cancellation
|
||||
if (removeFn) {
|
||||
removeFn();
|
||||
data!.key.set(!data!.tokens.isEmpty());
|
||||
removeFn = undefined;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
cancel(editor: ICodeEditor): void {
|
||||
const data = this._tokens.get(editor);
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
// remove with cancellation
|
||||
const cts = data.tokens.pop();
|
||||
if (cts) {
|
||||
cts.cancel();
|
||||
data.key.set(!data.tokens.isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
}, true);
|
||||
|
||||
export class EditorKeybindingCancellationTokenSource extends CancellationTokenSource {
|
||||
|
||||
private readonly _unregister: Function;
|
||||
|
||||
constructor(readonly editor: ICodeEditor, parent?: CancellationToken) {
|
||||
super(parent);
|
||||
this._unregister = editor.invokeWithinContext(accessor => accessor.get(IEditorCancellationTokens).add(editor, this));
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._unregister();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
registerEditorCommand(new class extends EditorCommand {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: 'editor.cancelOperation',
|
||||
kbOpts: {
|
||||
weight: KeybindingWeight.EditorContrib,
|
||||
primary: KeyCode.Escape
|
||||
},
|
||||
precondition: ctxCancellableOperation
|
||||
});
|
||||
}
|
||||
|
||||
runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor): void {
|
||||
accessor.get(IEditorCancellationTokens).cancel(editor);
|
||||
}
|
||||
});
|
||||
@@ -323,9 +323,9 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
|
||||
}
|
||||
|
||||
if (this._renderSideBySide) {
|
||||
this._setStrategy(new DiffEdtorWidgetSideBySide(this._createDataSource(), this._enableSplitViewResizing));
|
||||
this._setStrategy(new DiffEditorWidgetSideBySide(this._createDataSource(), this._enableSplitViewResizing));
|
||||
} else {
|
||||
this._setStrategy(new DiffEdtorWidgetInline(this._createDataSource(), this._enableSplitViewResizing));
|
||||
this._setStrategy(new DiffEditorWidgetInline(this._createDataSource(), this._enableSplitViewResizing));
|
||||
}
|
||||
|
||||
this._register(themeService.onThemeChange(t => {
|
||||
@@ -597,9 +597,9 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
|
||||
// renderSideBySide
|
||||
if (renderSideBySideChanged) {
|
||||
if (this._renderSideBySide) {
|
||||
this._setStrategy(new DiffEdtorWidgetSideBySide(this._createDataSource(), this._enableSplitViewResizing));
|
||||
this._setStrategy(new DiffEditorWidgetSideBySide(this._createDataSource(), this._enableSplitViewResizing));
|
||||
} else {
|
||||
this._setStrategy(new DiffEdtorWidgetInline(this._createDataSource(), this._enableSplitViewResizing));
|
||||
this._setStrategy(new DiffEditorWidgetInline(this._createDataSource(), this._enableSplitViewResizing));
|
||||
}
|
||||
// Update class name
|
||||
this._containerDomElement.className = DiffEditorWidget._getClassName(this._themeService.getTheme(), this._renderSideBySide);
|
||||
@@ -1547,7 +1547,7 @@ const DECORATIONS = {
|
||||
|
||||
};
|
||||
|
||||
class DiffEdtorWidgetSideBySide extends DiffEditorWidgetStyle implements IDiffEditorWidgetStyle, IVerticalSashLayoutProvider {
|
||||
class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements IDiffEditorWidgetStyle, IVerticalSashLayoutProvider {
|
||||
|
||||
static MINIMUM_EDITOR_WIDTH = 100;
|
||||
|
||||
@@ -1592,13 +1592,13 @@ class DiffEdtorWidgetSideBySide extends DiffEditorWidgetStyle implements IDiffEd
|
||||
|
||||
sashPosition = this._disableSash ? midPoint : sashPosition || midPoint;
|
||||
|
||||
if (contentWidth > DiffEdtorWidgetSideBySide.MINIMUM_EDITOR_WIDTH * 2) {
|
||||
if (sashPosition < DiffEdtorWidgetSideBySide.MINIMUM_EDITOR_WIDTH) {
|
||||
sashPosition = DiffEdtorWidgetSideBySide.MINIMUM_EDITOR_WIDTH;
|
||||
if (contentWidth > DiffEditorWidgetSideBySide.MINIMUM_EDITOR_WIDTH * 2) {
|
||||
if (sashPosition < DiffEditorWidgetSideBySide.MINIMUM_EDITOR_WIDTH) {
|
||||
sashPosition = DiffEditorWidgetSideBySide.MINIMUM_EDITOR_WIDTH;
|
||||
}
|
||||
|
||||
if (sashPosition > contentWidth - DiffEdtorWidgetSideBySide.MINIMUM_EDITOR_WIDTH) {
|
||||
sashPosition = contentWidth - DiffEdtorWidgetSideBySide.MINIMUM_EDITOR_WIDTH;
|
||||
if (sashPosition > contentWidth - DiffEditorWidgetSideBySide.MINIMUM_EDITOR_WIDTH) {
|
||||
sashPosition = contentWidth - DiffEditorWidgetSideBySide.MINIMUM_EDITOR_WIDTH;
|
||||
}
|
||||
} else {
|
||||
sashPosition = midPoint;
|
||||
@@ -1807,7 +1807,7 @@ class SideBySideViewZonesComputer extends ViewZonesComputer {
|
||||
}
|
||||
}
|
||||
|
||||
class DiffEdtorWidgetInline extends DiffEditorWidgetStyle implements IDiffEditorWidgetStyle {
|
||||
class DiffEditorWidgetInline extends DiffEditorWidgetStyle implements IDiffEditorWidgetStyle {
|
||||
|
||||
private decorationsLeft: number;
|
||||
|
||||
|
||||
@@ -248,7 +248,7 @@ class EditorModelManager extends Disposable {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
public esureSyncedResources(resources: URI[]): void {
|
||||
public ensureSyncedResources(resources: URI[]): void {
|
||||
for (const resource of resources) {
|
||||
let resourceStr = resource.toString();
|
||||
|
||||
@@ -387,7 +387,7 @@ export class EditorWorkerClient extends Disposable {
|
||||
|
||||
protected _withSyncedResources(resources: URI[]): Promise<EditorSimpleWorkerImpl> {
|
||||
return this._getProxy().then((proxy) => {
|
||||
this._getOrCreateModelManager(proxy).esureSyncedResources(resources);
|
||||
this._getOrCreateModelManager(proxy).ensureSyncedResources(resources);
|
||||
return proxy;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import { isNonEmptyArray } from 'vs/base/common/arrays';
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { illegalArgument, onUnexpectedExternalError } from 'vs/base/common/errors';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { CodeEditorStateFlag, EditorState, EditorStateCancellationTokenSource, TextModelCancellationTokenSource } from 'vs/editor/browser/core/editorState';
|
||||
import { CodeEditorStateFlag, EditorStateCancellationTokenSource, TextModelCancellationTokenSource } from 'vs/editor/browser/core/editorState';
|
||||
import { IActiveCodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { registerLanguageCommand, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
@@ -143,27 +143,25 @@ export async function formatDocumentRangeWithProvider(
|
||||
const workerService = accessor.get(IEditorWorkerService);
|
||||
|
||||
let model: ITextModel;
|
||||
let validate: () => boolean;
|
||||
let cts: CancellationTokenSource;
|
||||
if (isCodeEditor(editorOrModel)) {
|
||||
model = editorOrModel.getModel();
|
||||
const state = new EditorState(editorOrModel, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position);
|
||||
validate = () => state.validate(editorOrModel);
|
||||
cts = new EditorStateCancellationTokenSource(editorOrModel, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position, token);
|
||||
} else {
|
||||
model = editorOrModel;
|
||||
const versionNow = editorOrModel.getVersionId();
|
||||
validate = () => versionNow === editorOrModel.getVersionId();
|
||||
cts = new TextModelCancellationTokenSource(editorOrModel, token);
|
||||
}
|
||||
|
||||
const rawEdits = await provider.provideDocumentRangeFormattingEdits(
|
||||
model,
|
||||
range,
|
||||
model.getFormattingOptions(),
|
||||
token
|
||||
cts.token
|
||||
);
|
||||
|
||||
const edits = await workerService.computeMoreMinimalEdits(model.uri, rawEdits);
|
||||
|
||||
if (!validate()) {
|
||||
if (cts.token.isCancellationRequested) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { alert } from 'vs/base/browser/ui/aria/aria';
|
||||
import { createCancelablePromise } from 'vs/base/common/async';
|
||||
import { createCancelablePromise, raceCancellation } from 'vs/base/common/async';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
@@ -64,9 +64,9 @@ export class DefinitionAction extends EditorAction {
|
||||
|
||||
const cts = new EditorStateCancellationTokenSource(editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position);
|
||||
|
||||
const definitionPromise = this._getTargetLocationForPosition(model, pos, cts.token).then(async references => {
|
||||
const definitionPromise = raceCancellation(this._getTargetLocationForPosition(model, pos, cts.token), cts.token).then(async references => {
|
||||
|
||||
if (cts.token.isCancellationRequested || model.isDisposed() || editor.getModel() !== model) {
|
||||
if (!references || model.isDisposed()) {
|
||||
// new model, no more model
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { fuzzyScore, fuzzyScoreGracefulAggressive, anyScore, FuzzyScorer, FuzzyScore } from 'vs/base/common/filters';
|
||||
import { fuzzyScore, fuzzyScoreGracefulAggressive, FuzzyScorer, FuzzyScore } from 'vs/base/common/filters';
|
||||
import { isDisposable } from 'vs/base/common/lifecycle';
|
||||
import { CompletionList, CompletionItemProvider, CompletionItemKind } from 'vs/editor/common/modes';
|
||||
import { CompletionItem } from './suggest';
|
||||
import { InternalSuggestOptions, EDITOR_DEFAULTS } from 'vs/editor/common/config/editorOptions';
|
||||
import { WordDistance } from 'vs/editor/contrib/suggest/wordDistance';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
import { compareIgnoreCase } from 'vs/base/common/strings';
|
||||
|
||||
type StrictCompletionItem = Required<CompletionItem>;
|
||||
|
||||
@@ -215,8 +216,15 @@ export class CompletionModel {
|
||||
if (!match) {
|
||||
continue; // NO match
|
||||
}
|
||||
item.score = anyScore(word, wordLow, 0, item.completion.label, item.labelLow, 0);
|
||||
item.score[0] = match[0]; // use score from filterText
|
||||
if (compareIgnoreCase(item.completion.filterText, item.completion.label) === 0) {
|
||||
// filterText and label are actually the same -> use good highlights
|
||||
item.score = match;
|
||||
} else {
|
||||
// re-run the scorer on the label in the hope of a result BUT use the rank
|
||||
// of the filterText-match
|
||||
item.score = scoreFn(word, wordLow, wordPos, item.completion.label, item.labelLow, 0, false) || FuzzyScore.Default;
|
||||
item.score[0] = match[0]; // use score from filterText
|
||||
}
|
||||
|
||||
} else {
|
||||
// by default match `word` against the `label`
|
||||
|
||||
59
src/vs/editor/contrib/suggest/suggestCommitCharacters.ts
Normal file
59
src/vs/editor/contrib/suggest/suggestCommitCharacters.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { isNonEmptyArray } from 'vs/base/common/arrays';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { ISelectedSuggestion, SuggestWidget } from './suggestWidget';
|
||||
import { CharacterSet } from 'vs/editor/common/core/characterClassifier';
|
||||
|
||||
export class CommitCharacterController {
|
||||
|
||||
private _disposables: IDisposable[] = [];
|
||||
|
||||
private _active?: {
|
||||
readonly acceptCharacters: CharacterSet;
|
||||
readonly item: ISelectedSuggestion;
|
||||
};
|
||||
|
||||
constructor(editor: ICodeEditor, widget: SuggestWidget, accept: (selected: ISelectedSuggestion) => any) {
|
||||
|
||||
this._disposables.push(widget.onDidShow(() => this._onItem(widget.getFocusedItem())));
|
||||
this._disposables.push(widget.onDidFocus(this._onItem, this));
|
||||
this._disposables.push(widget.onDidHide(this.reset, this));
|
||||
|
||||
this._disposables.push(editor.onWillType(text => {
|
||||
if (this._active) {
|
||||
const ch = text.charCodeAt(text.length - 1);
|
||||
if (this._active.acceptCharacters.has(ch) && editor.getConfiguration().contribInfo.acceptSuggestionOnCommitCharacter) {
|
||||
accept(this._active.item);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private _onItem(selected: ISelectedSuggestion | undefined): void {
|
||||
if (!selected || !isNonEmptyArray(selected.item.completion.commitCharacters)) {
|
||||
this.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
const acceptCharacters = new CharacterSet();
|
||||
for (const ch of selected.item.completion.commitCharacters) {
|
||||
if (ch.length > 0) {
|
||||
acceptCharacters.add(ch.charCodeAt(0));
|
||||
}
|
||||
}
|
||||
this._active = { acceptCharacters, item: selected };
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
this._active = undefined;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
dispose(this._disposables);
|
||||
}
|
||||
}
|
||||
@@ -31,57 +31,8 @@ import { WordContextKey } from 'vs/editor/contrib/suggest/wordContextKey';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
|
||||
import { IdleValue } from 'vs/base/common/async';
|
||||
import { CharacterSet } from 'vs/editor/common/core/characterClassifier';
|
||||
import { isObject } from 'vs/base/common/types';
|
||||
|
||||
class AcceptOnCharacterOracle {
|
||||
|
||||
private _disposables: IDisposable[] = [];
|
||||
|
||||
private _active?: {
|
||||
readonly acceptCharacters: CharacterSet;
|
||||
readonly item: ISelectedSuggestion;
|
||||
};
|
||||
|
||||
constructor(editor: ICodeEditor, widget: SuggestWidget, accept: (selected: ISelectedSuggestion) => any) {
|
||||
|
||||
this._disposables.push(widget.onDidShow(() => this._onItem(widget.getFocusedItem())));
|
||||
this._disposables.push(widget.onDidFocus(this._onItem, this));
|
||||
this._disposables.push(widget.onDidHide(this.reset, this));
|
||||
|
||||
this._disposables.push(editor.onWillType(text => {
|
||||
if (this._active) {
|
||||
const ch = text.charCodeAt(text.length - 1);
|
||||
if (this._active.acceptCharacters.has(ch) && editor.getConfiguration().contribInfo.acceptSuggestionOnCommitCharacter) {
|
||||
accept(this._active.item);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private _onItem(selected: ISelectedSuggestion | undefined): void {
|
||||
if (!selected || !isNonEmptyArray(selected.item.completion.commitCharacters)) {
|
||||
this.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
const acceptCharacters = new CharacterSet();
|
||||
for (const ch of selected.item.completion.commitCharacters) {
|
||||
if (ch.length > 0) {
|
||||
acceptCharacters.add(ch.charCodeAt(0));
|
||||
}
|
||||
}
|
||||
this._active = { acceptCharacters, item: selected };
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
this._active = undefined;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
dispose(this._disposables);
|
||||
}
|
||||
}
|
||||
import { CommitCharacterController } from './suggestCommitCharacters';
|
||||
|
||||
export class SuggestController implements IEditorContribution {
|
||||
|
||||
@@ -113,15 +64,15 @@ export class SuggestController implements IEditorContribution {
|
||||
const widget = this._instantiationService.createInstance(SuggestWidget, this._editor);
|
||||
|
||||
this._toDispose.push(widget);
|
||||
this._toDispose.push(widget.onDidSelect(item => this._onDidSelectItem(item, false, true), this));
|
||||
this._toDispose.push(widget.onDidSelect(item => this._insertSuggestion(item, false, true), this));
|
||||
|
||||
// Wire up logic to accept a suggestion on certain characters
|
||||
const autoAcceptOracle = new AcceptOnCharacterOracle(this._editor, widget, item => this._onDidSelectItem(item, false, true));
|
||||
const commitCharacterController = new CommitCharacterController(this._editor, widget, item => this._insertSuggestion(item, false, true));
|
||||
this._toDispose.push(
|
||||
autoAcceptOracle,
|
||||
commitCharacterController,
|
||||
this._model.onDidSuggest(e => {
|
||||
if (e.completionModel.items.length === 0) {
|
||||
autoAcceptOracle.reset();
|
||||
commitCharacterController.reset();
|
||||
}
|
||||
})
|
||||
);
|
||||
@@ -210,7 +161,7 @@ export class SuggestController implements IEditorContribution {
|
||||
}
|
||||
}
|
||||
|
||||
protected _onDidSelectItem(event: ISelectedSuggestion | undefined, keepAlternativeSuggestions: boolean, undoStops: boolean): void {
|
||||
protected _insertSuggestion(event: ISelectedSuggestion | undefined, keepAlternativeSuggestions: boolean, undoStops: boolean): void {
|
||||
if (!event || !event.item) {
|
||||
this._alternatives.getValue().reset();
|
||||
this._model.cancel();
|
||||
@@ -282,7 +233,7 @@ export class SuggestController implements IEditorContribution {
|
||||
if (modelVersionNow !== model.getAlternativeVersionId()) {
|
||||
model.undo();
|
||||
}
|
||||
this._onDidSelectItem(next, false, false);
|
||||
this._insertSuggestion(next, false, false);
|
||||
break;
|
||||
}
|
||||
});
|
||||
@@ -364,7 +315,7 @@ export class SuggestController implements IEditorContribution {
|
||||
return;
|
||||
}
|
||||
this._editor.pushUndoStop();
|
||||
this._onDidSelectItem({ index, item, model: completionModel }, true, false);
|
||||
this._insertSuggestion({ index, item, model: completionModel }, true, false);
|
||||
|
||||
}, undefined, listener);
|
||||
});
|
||||
@@ -375,10 +326,8 @@ export class SuggestController implements IEditorContribution {
|
||||
}
|
||||
|
||||
acceptSelectedSuggestion(keepAlternativeSuggestions?: boolean): void {
|
||||
if (this._widget) {
|
||||
const item = this._widget.getValue().getFocusedItem();
|
||||
this._onDidSelectItem(item, !!keepAlternativeSuggestions, true);
|
||||
}
|
||||
const item = this._widget.getValue().getFocusedItem();
|
||||
this._insertSuggestion(item, !!keepAlternativeSuggestions, true);
|
||||
}
|
||||
|
||||
acceptNextSuggestion() {
|
||||
@@ -390,58 +339,40 @@ export class SuggestController implements IEditorContribution {
|
||||
}
|
||||
|
||||
cancelSuggestWidget(): void {
|
||||
if (this._widget) {
|
||||
this._model.cancel();
|
||||
this._widget.getValue().hideWidget();
|
||||
}
|
||||
this._model.cancel();
|
||||
this._widget.getValue().hideWidget();
|
||||
}
|
||||
|
||||
selectNextSuggestion(): void {
|
||||
if (this._widget) {
|
||||
this._widget.getValue().selectNext();
|
||||
}
|
||||
this._widget.getValue().selectNext();
|
||||
}
|
||||
|
||||
selectNextPageSuggestion(): void {
|
||||
if (this._widget) {
|
||||
this._widget.getValue().selectNextPage();
|
||||
}
|
||||
this._widget.getValue().selectNextPage();
|
||||
}
|
||||
|
||||
selectLastSuggestion(): void {
|
||||
if (this._widget) {
|
||||
this._widget.getValue().selectLast();
|
||||
}
|
||||
this._widget.getValue().selectLast();
|
||||
}
|
||||
|
||||
selectPrevSuggestion(): void {
|
||||
if (this._widget) {
|
||||
this._widget.getValue().selectPrevious();
|
||||
}
|
||||
this._widget.getValue().selectPrevious();
|
||||
}
|
||||
|
||||
selectPrevPageSuggestion(): void {
|
||||
if (this._widget) {
|
||||
this._widget.getValue().selectPreviousPage();
|
||||
}
|
||||
this._widget.getValue().selectPreviousPage();
|
||||
}
|
||||
|
||||
selectFirstSuggestion(): void {
|
||||
if (this._widget) {
|
||||
this._widget.getValue().selectFirst();
|
||||
}
|
||||
this._widget.getValue().selectFirst();
|
||||
}
|
||||
|
||||
toggleSuggestionDetails(): void {
|
||||
if (this._widget) {
|
||||
this._widget.getValue().toggleDetails();
|
||||
}
|
||||
this._widget.getValue().toggleDetails();
|
||||
}
|
||||
|
||||
toggleSuggestionFocus(): void {
|
||||
if (this._widget) {
|
||||
this._widget.getValue().toggleDetailsFocus();
|
||||
}
|
||||
this._widget.getValue().toggleDetailsFocus();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -95,7 +95,6 @@ export class SuggestModel implements IDisposable {
|
||||
private _quickSuggestDelay: number;
|
||||
private _triggerCharacterListener: IDisposable;
|
||||
private readonly _triggerQuickSuggest = new TimeoutTimer();
|
||||
private readonly _triggerRefilter = new TimeoutTimer();
|
||||
private _state: State = State.Idle;
|
||||
|
||||
private _requestToken?: CancellationTokenSource;
|
||||
@@ -161,7 +160,7 @@ export class SuggestModel implements IDisposable {
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
dispose([this._onDidCancel, this._onDidSuggest, this._onDidTrigger, this._triggerCharacterListener, this._triggerQuickSuggest, this._triggerRefilter]);
|
||||
dispose([this._onDidCancel, this._onDidSuggest, this._onDidTrigger, this._triggerCharacterListener, this._triggerQuickSuggest]);
|
||||
this._toDispose = dispose(this._toDispose);
|
||||
dispose(this._completionModel);
|
||||
this.cancel();
|
||||
@@ -221,7 +220,6 @@ export class SuggestModel implements IDisposable {
|
||||
|
||||
cancel(retrigger: boolean = false): void {
|
||||
if (this._state !== State.Idle) {
|
||||
this._triggerRefilter.cancel();
|
||||
this._triggerQuickSuggest.cancel();
|
||||
if (this._requestToken) {
|
||||
this._requestToken.cancel();
|
||||
@@ -328,14 +326,15 @@ export class SuggestModel implements IDisposable {
|
||||
}
|
||||
|
||||
private _refilterCompletionItems(): void {
|
||||
if (this._state === State.Idle) {
|
||||
return;
|
||||
}
|
||||
if (!this._editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
// refine active suggestion
|
||||
this._triggerRefilter.cancelAndSet(() => {
|
||||
// Re-filter suggestions. This MUST run async because filtering/scoring
|
||||
// uses the model content AND the cursor position. The latter is NOT
|
||||
// updated when the document has changed (the event which drives this method)
|
||||
// and therefore a little pause (next mirco task) is needed. See:
|
||||
// https://stackoverflow.com/questions/25915634/difference-between-microtask-and-macrotask-within-an-event-loop-context#25933985
|
||||
Promise.resolve().then(() => {
|
||||
if (this._state === State.Idle) {
|
||||
return;
|
||||
}
|
||||
if (!this._editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
@@ -343,7 +342,7 @@ export class SuggestModel implements IDisposable {
|
||||
const position = this._editor.getPosition();
|
||||
const ctx = new LineContext(model, position, this._state === State.Auto, false);
|
||||
this._onNewContext(ctx);
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
|
||||
trigger(context: SuggestTriggerContext, retrigger: boolean = false, onlyFrom?: Set<CompletionItemProvider>, existingItems?: CompletionItem[]): void {
|
||||
|
||||
@@ -30,7 +30,6 @@ import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { TimeoutTimer, CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { CompletionItemKind, completionKindToCssClass } from 'vs/editor/common/modes';
|
||||
import { IconLabel, IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
|
||||
import { getIconClasses } from 'vs/editor/common/services/getIconClasses';
|
||||
@@ -545,10 +544,8 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
||||
return;
|
||||
}
|
||||
|
||||
item.resolve(CancellationToken.None).then(() => {
|
||||
this.onDidSelectEmitter.fire({ item, index, model: completionModel });
|
||||
this.editor.focus();
|
||||
});
|
||||
this.onDidSelectEmitter.fire({ item, index, model: completionModel });
|
||||
this.editor.focus();
|
||||
}
|
||||
|
||||
private _getSuggestionAriaAlertLabel(item: CompletionItem): string {
|
||||
|
||||
@@ -671,8 +671,8 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
||||
|
||||
return withOracle(async (sugget, editor) => {
|
||||
class TestCtrl extends SuggestController {
|
||||
_onDidSelectItem(item: ISelectedSuggestion) {
|
||||
super._onDidSelectItem(item, false, true);
|
||||
_insertSuggestion(item: ISelectedSuggestion) {
|
||||
super._insertSuggestion(item, false, true);
|
||||
}
|
||||
}
|
||||
const ctrl = <TestCtrl>editor.registerAndInstantiateContribution(TestCtrl);
|
||||
@@ -687,7 +687,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
||||
const [first] = event.completionModel.items;
|
||||
assert.equal(first.completion.label, 'bar');
|
||||
|
||||
ctrl._onDidSelectItem({ item: first, index: 0, model: event.completionModel });
|
||||
ctrl._insertSuggestion({ item: first, index: 0, model: event.completionModel });
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
|
||||
@@ -162,7 +162,6 @@ export interface IExtensionGalleryService {
|
||||
getChangelog(extension: IGalleryExtension, token: CancellationToken): Promise<string>;
|
||||
getCoreTranslation(extension: IGalleryExtension, languageId: string): Promise<ITranslation | null>;
|
||||
getAllVersions(extension: IGalleryExtension, compatible: boolean): Promise<IGalleryExtensionVersion[]>;
|
||||
loadAllDependencies(dependencies: IExtensionIdentifier[], token: CancellationToken): Promise<IGalleryExtension[]>;
|
||||
getExtensionsReport(): Promise<IReportedExtension[]>;
|
||||
getCompatibleExtension(extension: IGalleryExtension): Promise<IGalleryExtension | null>;
|
||||
getCompatibleExtension(id: IExtensionIdentifier, version?: string): Promise<IGalleryExtension | null>;
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
import { tmpdir } from 'os';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import { distinct } from 'vs/base/common/arrays';
|
||||
import { getErrorMessage, isPromiseCanceledError, canceled } from 'vs/base/common/errors';
|
||||
import { StatisticType, IGalleryExtension, IExtensionGalleryService, IGalleryExtensionAsset, IQueryOptions, SortBy, SortOrder, IExtensionIdentifier, IReportedExtension, InstallOperation, ITranslation, IGalleryExtensionVersion, IGalleryExtensionAssets, isIExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { getGalleryExtensionId, getGalleryExtensionTelemetryData, adoptToGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
@@ -750,10 +749,6 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
return Promise.resolve('');
|
||||
}
|
||||
|
||||
loadAllDependencies(extensions: IExtensionIdentifier[], token: CancellationToken): Promise<IGalleryExtension[]> {
|
||||
return this.getDependenciesRecursively(extensions.map(e => e.id), [], token);
|
||||
}
|
||||
|
||||
getAllVersions(extension: IGalleryExtension, compatible: boolean): Promise<IGalleryExtensionVersion[]> {
|
||||
let query = new Query()
|
||||
.withFlags(Flags.IncludeVersions, Flags.IncludeFiles, Flags.IncludeVersionProperties, Flags.ExcludeNonValidated)
|
||||
@@ -782,58 +777,6 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
});
|
||||
}
|
||||
|
||||
private loadDependencies(extensionNames: string[], token: CancellationToken): Promise<IGalleryExtension[]> {
|
||||
if (!extensionNames || extensionNames.length === 0) {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
let query = new Query()
|
||||
.withFlags(Flags.IncludeLatestVersionOnly, Flags.IncludeAssetUri, Flags.IncludeStatistics, Flags.IncludeFiles, Flags.IncludeVersionProperties)
|
||||
.withPage(1, extensionNames.length)
|
||||
.withFilter(FilterType.Target, 'Microsoft.VisualStudio.Code')
|
||||
.withFilter(FilterType.ExcludeWithFlags, flagsToString(Flags.Unpublished))
|
||||
.withAssetTypes(AssetType.Icon, AssetType.License, AssetType.Details, AssetType.Manifest, AssetType.VSIX)
|
||||
.withFilter(FilterType.ExtensionName, ...extensionNames);
|
||||
|
||||
return this.queryGallery(query, token).then(result => {
|
||||
const dependencies: IGalleryExtension[] = [];
|
||||
const ids: string[] = [];
|
||||
|
||||
for (let index = 0; index < result.galleryExtensions.length; index++) {
|
||||
const rawExtension = result.galleryExtensions[index];
|
||||
if (ids.indexOf(rawExtension.extensionId) === -1) {
|
||||
dependencies.push(toExtension(rawExtension, rawExtension.versions[0], index, query, 'dependencies'));
|
||||
ids.push(rawExtension.extensionId);
|
||||
}
|
||||
}
|
||||
return dependencies;
|
||||
});
|
||||
}
|
||||
|
||||
private getDependenciesRecursively(toGet: string[], result: IGalleryExtension[], token: CancellationToken): Promise<IGalleryExtension[]> {
|
||||
if (!toGet || !toGet.length) {
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
toGet = result.length ? toGet.filter(e => !ExtensionGalleryService.hasExtensionByName(result, e)) : toGet;
|
||||
if (!toGet.length) {
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
return this.loadDependencies(toGet, token)
|
||||
.then(loadedDependencies => {
|
||||
const dependenciesSet = new Set<string>();
|
||||
for (const dep of loadedDependencies) {
|
||||
if (dep.properties.dependencies) {
|
||||
dep.properties.dependencies.forEach(d => dependenciesSet.add(d));
|
||||
}
|
||||
}
|
||||
result = distinct(result.concat(loadedDependencies), d => d.identifier.uuid);
|
||||
const dependencies: string[] = [];
|
||||
dependenciesSet.forEach(d => !ExtensionGalleryService.hasExtensionByName(result, d) && dependencies.push(d));
|
||||
return this.getDependenciesRecursively(dependencies, result, token);
|
||||
});
|
||||
}
|
||||
|
||||
private getAsset(asset: IGalleryExtensionAsset, options: IRequestOptions = {}, token: CancellationToken = CancellationToken.None): Promise<IRequestContext> {
|
||||
return this.commonHeadersPromise.then(commonHeaders => {
|
||||
const baseOptions = { type: 'GET' };
|
||||
@@ -952,15 +895,6 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
});
|
||||
}
|
||||
|
||||
private static hasExtensionByName(extensions: IGalleryExtension[], name: string): boolean {
|
||||
for (const extension of extensions) {
|
||||
if (`${extension.publisher}.${extension.name}` === name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
getExtensionsReport(): Promise<IReportedExtension[]> {
|
||||
if (!this.isEnabled()) {
|
||||
return Promise.reject(new Error('No extension gallery service configured.'));
|
||||
|
||||
@@ -16,18 +16,24 @@ export function isUIExtension(manifest: IExtensionManifest, uiContributions: str
|
||||
case 'ui': return true;
|
||||
case 'workspace': return false;
|
||||
default: {
|
||||
// Tagged as UI extension in product
|
||||
if (isNonEmptyArray(product.uiExtensions) && product.uiExtensions.some(id => areSameExtensions({ id }, { id: extensionId }))) {
|
||||
return true;
|
||||
}
|
||||
// Not an UI extension if it has main
|
||||
if (manifest.main) {
|
||||
return false;
|
||||
}
|
||||
// Not an UI extension if it has dependencies or an extension pack
|
||||
if (isNonEmptyArray(manifest.extensionDependencies) || isNonEmptyArray(manifest.extensionPack)) {
|
||||
return false;
|
||||
}
|
||||
if (manifest.contributes) {
|
||||
// Not an UI extension if it has no ui contributions
|
||||
if (!uiContributions.length || Object.keys(manifest.contributes).some(contribution => uiContributions.indexOf(contribution) === -1)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Default is UI Extension
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,14 +6,15 @@
|
||||
import * as path from 'vs/base/common/path';
|
||||
import { ILogService, LogLevel, NullLogService, AbstractLogService } from 'vs/platform/log/common/log';
|
||||
import * as spdlog from 'spdlog';
|
||||
import { BufferLogService } from 'vs/platform/log/common/bufferLog';
|
||||
|
||||
export function createSpdLogService(processName: string, logLevel: LogLevel, logsFolder: string): ILogService {
|
||||
export async function createSpdLogService(processName: string, logLevel: LogLevel, logsFolder: string): Promise<ILogService> {
|
||||
// Do not crash if spdlog cannot be loaded
|
||||
try {
|
||||
const _spdlog: typeof spdlog = require.__$__nodeRequire('spdlog');
|
||||
_spdlog.setAsyncMode(8192, 500);
|
||||
const logfilePath = path.join(logsFolder, `${processName}.log`);
|
||||
const logger = new _spdlog.RotatingLogger(processName, logfilePath, 1024 * 1024 * 5, 6);
|
||||
const logger = await _spdlog.createRotatingLoggerAsync(processName, logfilePath, 1024 * 1024 * 5, 6);
|
||||
logger.setLevel(0);
|
||||
|
||||
return new SpdLogService(logger, logLevel);
|
||||
@@ -28,6 +29,12 @@ export function createRotatingLogger(name: string, filename: string, filesize: n
|
||||
return _spdlog.createRotatingLogger(name, filename, filesize, filecount);
|
||||
}
|
||||
|
||||
export function createBufferSpdLogService(processName: string, logLevel: LogLevel, logsFolder: string): ILogService {
|
||||
const bufferLogService = new BufferLogService();
|
||||
createSpdLogService(processName, logLevel, logsFolder).then(logger => bufferLogService.logger = logger);
|
||||
return bufferLogService;
|
||||
}
|
||||
|
||||
class SpdLogService extends AbstractLogService implements ILogService {
|
||||
|
||||
_serviceBrand: any;
|
||||
|
||||
@@ -67,6 +67,7 @@ export interface IProductConfiguration {
|
||||
keyboardShortcutsUrlWin: string;
|
||||
introductoryVideosUrl: string;
|
||||
tipsAndTricksUrl: string;
|
||||
newsletterSignupUrl: string;
|
||||
twitterUrl: string;
|
||||
requestFeatureUrl: string;
|
||||
reportIssueUrl: string;
|
||||
|
||||
89
src/vs/platform/remote/browser/browserWebSocketFactory.ts
Normal file
89
src/vs/platform/remote/browser/browserWebSocketFactory.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IWebSocketFactory, IConnectCallback } from 'vs/platform/remote/common/remoteAgentConnection';
|
||||
import { ISocket } from 'vs/base/parts/ipc/common/ipc.net';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
|
||||
class BrowserSocket implements ISocket {
|
||||
public readonly socket: WebSocket;
|
||||
|
||||
constructor(socket: WebSocket) {
|
||||
this.socket = socket;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.socket.close();
|
||||
}
|
||||
|
||||
public onData(_listener: (e: VSBuffer) => void): IDisposable {
|
||||
const fileReader = new FileReader();
|
||||
const queue: Blob[] = [];
|
||||
let isReading = false;
|
||||
fileReader.onload = function (event) {
|
||||
isReading = false;
|
||||
const buff = <ArrayBuffer>(<any>event.target).result;
|
||||
|
||||
try {
|
||||
_listener(VSBuffer.wrap(new Uint8Array(buff)));
|
||||
} catch (err) {
|
||||
onUnexpectedError(err);
|
||||
}
|
||||
|
||||
if (queue.length > 0) {
|
||||
enqueue(queue.shift()!);
|
||||
}
|
||||
};
|
||||
const enqueue = (blob: Blob) => {
|
||||
if (isReading) {
|
||||
queue.push(blob);
|
||||
return;
|
||||
}
|
||||
isReading = true;
|
||||
fileReader.readAsArrayBuffer(blob);
|
||||
};
|
||||
const listener = (e: MessageEvent) => {
|
||||
enqueue(<Blob>e.data);
|
||||
};
|
||||
this.socket.addEventListener('message', listener);
|
||||
return {
|
||||
dispose: () => this.socket.removeEventListener('message', listener)
|
||||
};
|
||||
}
|
||||
|
||||
public onClose(listener: () => void): IDisposable {
|
||||
this.socket.addEventListener('close', listener);
|
||||
return {
|
||||
dispose: () => this.socket.removeEventListener('close', listener)
|
||||
};
|
||||
}
|
||||
|
||||
public onEnd(listener: () => void): IDisposable {
|
||||
return Disposable.None;
|
||||
}
|
||||
|
||||
public write(buffer: VSBuffer): void {
|
||||
this.socket.send(buffer.buffer);
|
||||
}
|
||||
|
||||
public end(): void {
|
||||
this.socket.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const browserWebSocketFactory = new class implements IWebSocketFactory {
|
||||
connect(host: string, port: number, query: string, callback: IConnectCallback): void {
|
||||
const errorListener = (err: any) => callback(err, undefined);
|
||||
const socket = new WebSocket(`ws://${host}:${port}/?${query}&skipWebSocketFrames=false`);
|
||||
socket.onopen = function (event) {
|
||||
socket.removeEventListener('error', errorListener);
|
||||
callback(undefined, new BrowserSocket(socket));
|
||||
};
|
||||
socket.addEventListener('error', errorListener);
|
||||
}
|
||||
};
|
||||
@@ -3,11 +3,15 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Client, PersistentProtocol, ISocket } from 'vs/base/parts/ipc/common/ipc.net';
|
||||
import { Client, PersistentProtocol, ISocket, ProtocolConstants } from 'vs/base/parts/ipc/common/ipc.net';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { RemoteAgentConnectionContext } from 'vs/platform/remote/common/remoteAgentEnvironment';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { RemoteAuthorityResolverError } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import { isPromiseCanceledError } from 'vs/base/common/errors';
|
||||
|
||||
export const enum ConnectionType {
|
||||
Management = 1,
|
||||
@@ -15,6 +19,37 @@ export const enum ConnectionType {
|
||||
Tunnel = 3,
|
||||
}
|
||||
|
||||
export interface AuthRequest {
|
||||
type: 'auth';
|
||||
auth: string;
|
||||
}
|
||||
|
||||
export interface SignRequest {
|
||||
type: 'sign';
|
||||
data: string;
|
||||
}
|
||||
|
||||
export interface ConnectionTypeRequest {
|
||||
type: 'connectionType';
|
||||
commit?: string;
|
||||
signedData?: string;
|
||||
desiredConnectionType?: ConnectionType;
|
||||
args?: any;
|
||||
isBuilt: boolean;
|
||||
}
|
||||
|
||||
export interface ErrorMessage {
|
||||
type: 'error';
|
||||
reason: string;
|
||||
}
|
||||
|
||||
export interface OKMessage {
|
||||
type: 'ok';
|
||||
}
|
||||
|
||||
export type HandshakeMessage = AuthRequest | SignRequest | ConnectionTypeRequest | ErrorMessage | OKMessage;
|
||||
|
||||
|
||||
interface ISimpleConnectionOptions {
|
||||
isBuilt: boolean;
|
||||
commit: string | undefined;
|
||||
@@ -34,7 +69,84 @@ export interface IWebSocketFactory {
|
||||
}
|
||||
|
||||
async function connectToRemoteExtensionHostAgent(options: ISimpleConnectionOptions, connectionType: ConnectionType, args: any | undefined): Promise<PersistentProtocol> {
|
||||
throw new Error(`Not implemented`);
|
||||
const protocol = await new Promise<PersistentProtocol>((c, e) => {
|
||||
options.webSocketFactory.connect(
|
||||
options.host,
|
||||
options.port,
|
||||
`reconnectionToken=${options.reconnectionToken}&reconnection=${options.reconnectionProtocol ? 'true' : 'false'}`,
|
||||
(err: any, socket: ISocket) => {
|
||||
if (err) {
|
||||
e(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.reconnectionProtocol) {
|
||||
options.reconnectionProtocol.beginAcceptReconnection(socket, null);
|
||||
c(options.reconnectionProtocol);
|
||||
} else {
|
||||
c(new PersistentProtocol(socket, null));
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return new Promise<PersistentProtocol>((c, e) => {
|
||||
|
||||
const messageRegistration = protocol.onControlMessage(raw => {
|
||||
const msg = <HandshakeMessage>JSON.parse(raw.toString());
|
||||
// Stop listening for further events
|
||||
messageRegistration.dispose();
|
||||
|
||||
const error = getErrorFromMessage(msg);
|
||||
if (error) {
|
||||
return e(error);
|
||||
}
|
||||
|
||||
if (msg.type === 'sign') {
|
||||
|
||||
let signed = msg.data;
|
||||
if (platform.isNative) {
|
||||
try {
|
||||
const vsda = <any>require.__$__nodeRequire('vsda');
|
||||
const signer = new vsda.signer();
|
||||
if (signer) {
|
||||
signed = signer.sign(msg.data);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('signer.sign: ' + e);
|
||||
}
|
||||
} else {
|
||||
signed = (<any>self).CONNECTION_AUTH_TOKEN;
|
||||
}
|
||||
|
||||
const connTypeRequest: ConnectionTypeRequest = {
|
||||
type: 'connectionType',
|
||||
commit: options.commit,
|
||||
signedData: signed,
|
||||
desiredConnectionType: connectionType,
|
||||
isBuilt: options.isBuilt
|
||||
};
|
||||
if (args) {
|
||||
connTypeRequest.args = args;
|
||||
}
|
||||
protocol.sendControl(VSBuffer.fromString(JSON.stringify(connTypeRequest)));
|
||||
c(protocol);
|
||||
} else {
|
||||
e(new Error('handshake error'));
|
||||
}
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
e(new Error('handshake timeout'));
|
||||
}, 2000);
|
||||
|
||||
// TODO@vs-remote: use real nonce here
|
||||
const authRequest: AuthRequest = {
|
||||
type: 'auth',
|
||||
auth: '00000000000000000000'
|
||||
};
|
||||
protocol.sendControl(VSBuffer.fromString(JSON.stringify(authRequest)));
|
||||
});
|
||||
}
|
||||
|
||||
interface IManagementConnectionResult {
|
||||
@@ -148,6 +260,12 @@ export async function connectRemoteAgentTunnel(options: IConnectionOptions, tunn
|
||||
return protocol;
|
||||
}
|
||||
|
||||
function sleep(seconds: number): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
setTimeout(resolve, seconds * 1000);
|
||||
});
|
||||
}
|
||||
|
||||
export const enum PersistenConnectionEventType {
|
||||
ConnectionLost,
|
||||
ReconnectionWait,
|
||||
@@ -184,11 +302,99 @@ abstract class PersistentConnection extends Disposable {
|
||||
public readonly reconnectionToken: string;
|
||||
public readonly protocol: PersistentProtocol;
|
||||
|
||||
private _isReconnecting: boolean;
|
||||
private _permanentFailure: boolean;
|
||||
|
||||
constructor(options: IConnectionOptions, reconnectionToken: string, protocol: PersistentProtocol) {
|
||||
super();
|
||||
this._options = options;
|
||||
this.reconnectionToken = reconnectionToken;
|
||||
this.protocol = protocol;
|
||||
this._isReconnecting = false;
|
||||
this._permanentFailure = false;
|
||||
|
||||
this._register(protocol.onSocketClose(() => this._beginReconnecting()));
|
||||
this._register(protocol.onSocketTimeout(() => this._beginReconnecting()));
|
||||
}
|
||||
|
||||
private async _beginReconnecting(): Promise<void> {
|
||||
// Only have one reconnection loop active at a time.
|
||||
if (this._isReconnecting) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this._isReconnecting = true;
|
||||
await this._runReconnectingLoop();
|
||||
} finally {
|
||||
this._isReconnecting = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async _runReconnectingLoop(): Promise<void> {
|
||||
if (this._permanentFailure) {
|
||||
// no more attempts!
|
||||
return;
|
||||
}
|
||||
this._onDidStateChange.fire(new ConnectionLostEvent());
|
||||
const TIMES = [5, 5, 10, 10, 10, 10, 10, 30];
|
||||
const disconnectStartTime = Date.now();
|
||||
let attempt = -1;
|
||||
do {
|
||||
attempt++;
|
||||
const waitTime = (attempt < TIMES.length ? TIMES[attempt] : TIMES[TIMES.length - 1]);
|
||||
try {
|
||||
this._onDidStateChange.fire(new ReconnectionWaitEvent(waitTime));
|
||||
await sleep(waitTime);
|
||||
|
||||
// connection was lost, let's try to re-establish it
|
||||
this._onDidStateChange.fire(new ReconnectionRunningEvent());
|
||||
const simpleOptions = await resolveConnectionOptions(this._options, this.reconnectionToken, this.protocol);
|
||||
await connectWithTimeLimit(this._reconnect(simpleOptions), 30 * 1000 /*30s*/);
|
||||
this._onDidStateChange.fire(new ConnectionGainEvent());
|
||||
|
||||
break;
|
||||
} catch (err) {
|
||||
if (err.code === 'VSCODE_CONNECTION_ERROR') {
|
||||
console.error(`A permanent connection error occurred`);
|
||||
console.error(err);
|
||||
this._permanentFailure = true;
|
||||
this._onDidStateChange.fire(new ReconnectionPermanentFailureEvent());
|
||||
this.protocol.acceptDisconnect();
|
||||
break;
|
||||
}
|
||||
if (Date.now() - disconnectStartTime > ProtocolConstants.ReconnectionGraceTime) {
|
||||
console.error(`Giving up after reconnection grace time has expired!`);
|
||||
this._permanentFailure = true;
|
||||
this._onDidStateChange.fire(new ReconnectionPermanentFailureEvent());
|
||||
this.protocol.acceptDisconnect();
|
||||
break;
|
||||
}
|
||||
if (RemoteAuthorityResolverError.isTemporarilyNotAvailable(err)) {
|
||||
console.warn(`A temporarily not available error occured while trying to reconnect:`);
|
||||
console.warn(err);
|
||||
// try again!
|
||||
continue;
|
||||
}
|
||||
if ((err.code === 'ETIMEDOUT' || err.code === 'ENETUNREACH' || err.code === 'ECONNREFUSED' || err.code === 'ECONNRESET') && err.syscall === 'connect') {
|
||||
console.warn(`A connect error occured while trying to reconnect:`);
|
||||
console.warn(err);
|
||||
// try again!
|
||||
continue;
|
||||
}
|
||||
if (isPromiseCanceledError(err)) {
|
||||
console.warn(`A cancel error occured while trying to reconnect:`);
|
||||
console.warn(err);
|
||||
// try again!
|
||||
continue;
|
||||
}
|
||||
console.error(`An error occured while trying to reconnect:`);
|
||||
console.error(err);
|
||||
this._permanentFailure = true;
|
||||
this._onDidStateChange.fire(new ReconnectionPermanentFailureEvent());
|
||||
this.protocol.acceptDisconnect();
|
||||
break;
|
||||
}
|
||||
} while (!this._permanentFailure);
|
||||
}
|
||||
|
||||
protected abstract _reconnect(options: ISimpleConnectionOptions): Promise<void>;
|
||||
@@ -227,6 +433,24 @@ export class ExtensionHostPersistentConnection extends PersistentConnection {
|
||||
}
|
||||
}
|
||||
|
||||
function connectWithTimeLimit(p: Promise<void>, timeLimit: number): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
let timeout = setTimeout(() => {
|
||||
const err: any = new Error('Time limit reached');
|
||||
err.code = 'ETIMEDOUT';
|
||||
err.syscall = 'connect';
|
||||
reject(err);
|
||||
}, timeLimit);
|
||||
p.then(() => {
|
||||
clearTimeout(timeout);
|
||||
resolve();
|
||||
}, (err) => {
|
||||
clearTimeout(timeout);
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getErrorFromMessage(msg: any): Error | null {
|
||||
if (msg && msg.type === 'error') {
|
||||
const error = new Error(`Connection error: ${msg.reason}`);
|
||||
|
||||
@@ -3,11 +3,13 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { onUnexpectedError, setUnexpectedErrorHandler } from 'vs/base/common/errors';
|
||||
import BaseErrorTelemetry from '../common/errorTelemetry';
|
||||
|
||||
export default class ErrorTelemetry extends BaseErrorTelemetry {
|
||||
protected installErrorListeners(): void {
|
||||
setUnexpectedErrorHandler(err => console.error(err));
|
||||
|
||||
// Print a console message when rejection isn't handled within N seconds. For details:
|
||||
// see https://nodejs.org/api/process.html#process_event_unhandledrejection
|
||||
// and https://nodejs.org/api/process.html#process_event_rejectionhandled
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { URI as uri } from 'vs/base/common/uri';
|
||||
import { IDebugService, IConfig, IDebugConfigurationProvider, IBreakpoint, IFunctionBreakpoint, IBreakpointData, ITerminalSettings, IDebugAdapter, IDebugAdapterDescriptorFactory, IDebugSession, IDebugAdapterFactory, IDebugAdapterTrackerFactory } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IDebugService, IConfig, IDebugConfigurationProvider, IBreakpoint, IFunctionBreakpoint, IBreakpointData, ITerminalSettings, IDebugAdapter, IDebugAdapterDescriptorFactory, IDebugSession, IDebugAdapterFactory } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import {
|
||||
ExtHostContext, ExtHostDebugServiceShape, MainThreadDebugServiceShape, DebugSessionUUID, MainContext,
|
||||
IExtHostContext, IBreakpointsDeltaDto, ISourceMultiBreakpointDto, ISourceBreakpointDto, IFunctionBreakpointDto, IDebugSessionDto
|
||||
@@ -26,7 +26,6 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
|
||||
private _debugAdaptersHandleCounter = 1;
|
||||
private readonly _debugConfigurationProviders: Map<number, IDebugConfigurationProvider>;
|
||||
private readonly _debugAdapterDescriptorFactories: Map<number, IDebugAdapterDescriptorFactory>;
|
||||
private readonly _debugAdapterTrackerFactories: Map<number, IDebugAdapterTrackerFactory>;
|
||||
private readonly _sessions: Set<DebugSessionUUID>;
|
||||
|
||||
constructor(
|
||||
@@ -53,7 +52,6 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
|
||||
this._debugAdapters = new Map();
|
||||
this._debugConfigurationProviders = new Map();
|
||||
this._debugAdapterDescriptorFactories = new Map();
|
||||
this._debugAdapterTrackerFactories = new Map();
|
||||
this._sessions = new Set();
|
||||
}
|
||||
|
||||
@@ -207,24 +205,6 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
|
||||
}
|
||||
}
|
||||
|
||||
public $registerDebugAdapterTrackerFactory(debugType: string, handle: number) {
|
||||
const factory = <IDebugAdapterTrackerFactory>{
|
||||
type: debugType,
|
||||
};
|
||||
this._debugAdapterTrackerFactories.set(handle, factory);
|
||||
this._toDispose.push(this.debugService.getConfigurationManager().registerDebugAdapterTrackerFactory(factory));
|
||||
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
public $unregisterDebugAdapterTrackerFactory(handle: number) {
|
||||
const factory = this._debugAdapterTrackerFactories.get(handle);
|
||||
if (factory) {
|
||||
this._debugAdapterTrackerFactories.delete(handle);
|
||||
this.debugService.getConfigurationManager().unregisterDebugAdapterTrackerFactory(factory);
|
||||
}
|
||||
}
|
||||
|
||||
private getSession(sessionId: DebugSessionUUID | undefined): IDebugSession | undefined {
|
||||
if (sessionId) {
|
||||
return this.debugService.getModel().getSession(sessionId, true);
|
||||
|
||||
@@ -686,10 +686,8 @@ export interface MainThreadDebugServiceShape extends IDisposable {
|
||||
$acceptDAExit(handle: number, code: number | undefined, signal: string | undefined): void;
|
||||
$registerDebugConfigurationProvider(type: string, hasProvideMethod: boolean, hasResolveMethod: boolean, hasProvideDaMethod: boolean, handle: number): Promise<void>;
|
||||
$registerDebugAdapterDescriptorFactory(type: string, handle: number): Promise<void>;
|
||||
$registerDebugAdapterTrackerFactory(type: string, handle: number): Promise<any>;
|
||||
$unregisterDebugConfigurationProvider(handle: number): void;
|
||||
$unregisterDebugAdapterDescriptorFactory(handle: number): void;
|
||||
$unregisterDebugAdapterTrackerFactory(handle: number): void;
|
||||
$startDebugging(folder: UriComponents | undefined, nameOrConfig: string | IDebugConfiguration, parentSessionID: string | undefined): Promise<boolean>;
|
||||
$customDebugAdapterRequest(id: DebugSessionUUID, command: string, args: any): Promise<any>;
|
||||
$appendDebugConsole(value: string): void;
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace schema {
|
||||
export interface IUserFriendlyMenuItem {
|
||||
command: string;
|
||||
alt?: string;
|
||||
precondition?: string;
|
||||
when?: string;
|
||||
group?: string;
|
||||
}
|
||||
@@ -79,6 +80,10 @@ namespace schema {
|
||||
collector.error(localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'alt'));
|
||||
return false;
|
||||
}
|
||||
if (item.precondition && typeof item.precondition !== 'string') {
|
||||
collector.error(localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'precondition'));
|
||||
return false;
|
||||
}
|
||||
if (item.when && typeof item.when !== 'string') {
|
||||
collector.error(localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'when'));
|
||||
return false;
|
||||
@@ -103,6 +108,10 @@ namespace schema {
|
||||
description: localize('vscode.extension.contributes.menuItem.alt', 'Identifier of an alternative command to execute. The command must be declared in the \'commands\'-section'),
|
||||
type: 'string'
|
||||
},
|
||||
precondition: {
|
||||
description: localize('vscode.extension.contributes.menuItem.precondition', 'Condition which must be true to enable this item'),
|
||||
type: 'string'
|
||||
},
|
||||
when: {
|
||||
description: localize('vscode.extension.contributes.menuItem.when', 'Condition which must be true to show this item'),
|
||||
type: 'string'
|
||||
@@ -114,7 +123,7 @@ namespace schema {
|
||||
}
|
||||
};
|
||||
|
||||
export const menusContribtion: IJSONSchema = {
|
||||
export const menusContribution: IJSONSchema = {
|
||||
description: localize('vscode.extension.contributes.menus', "Contributes menu items to the editor"),
|
||||
type: 'object',
|
||||
properties: {
|
||||
@@ -354,7 +363,7 @@ let _menuRegistrations: IDisposable[] = [];
|
||||
|
||||
ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: schema.IUserFriendlyMenuItem[] }>({
|
||||
extensionPoint: 'menus',
|
||||
jsonSchema: schema.menusContribtion
|
||||
jsonSchema: schema.menusContribution
|
||||
}).setHandler(extensions => {
|
||||
|
||||
// remove all previous menu registrations
|
||||
@@ -406,6 +415,14 @@ ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: schema.IUserFriendlyM
|
||||
}
|
||||
}
|
||||
|
||||
if (item.precondition) {
|
||||
command.precondition = ContextKeyExpr.deserialize(item.precondition);
|
||||
}
|
||||
|
||||
if (alt && item.precondition) {
|
||||
alt.precondition = command.precondition;
|
||||
}
|
||||
|
||||
const registration = MenuRegistry.appendMenuItem(menu, {
|
||||
command,
|
||||
alt,
|
||||
|
||||
@@ -210,11 +210,10 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
|
||||
}
|
||||
}
|
||||
|
||||
public $postMessage(handle: WebviewPanelHandle | WebviewInsetHandle, message: any): Promise<boolean> {
|
||||
public async $postMessage(handle: WebviewPanelHandle | WebviewInsetHandle, message: any): Promise<boolean> {
|
||||
if (typeof handle === 'number') {
|
||||
this.getWebviewElement(handle).sendMessage(message);
|
||||
return Promise.resolve(true);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
const webview = this.getWebview(handle);
|
||||
const editors = this._editorService.visibleControls
|
||||
@@ -222,11 +221,17 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
|
||||
.map(e => e as WebviewEditor)
|
||||
.filter(e => e.input!.matches(webview));
|
||||
|
||||
for (const editor of editors) {
|
||||
editor.sendMessage(message);
|
||||
if (editors.length > 0) {
|
||||
editors[0].sendMessage(message);
|
||||
return true;
|
||||
}
|
||||
|
||||
return Promise.resolve(editors.length > 0);
|
||||
if (webview.webview) {
|
||||
webview.webview.sendMessage(message);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -307,11 +307,8 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
|
||||
const handle = this._trackerFactoryHandleCounter++;
|
||||
this._trackerFactories.push({ type, handle, factory });
|
||||
|
||||
this._debugServiceProxy.$registerDebugAdapterTrackerFactory(type, handle);
|
||||
|
||||
return new Disposable(() => {
|
||||
this._trackerFactories = this._trackerFactories.filter(p => p.factory !== factory); // remove
|
||||
this._debugServiceProxy.$unregisterDebugAdapterTrackerFactory(handle);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -333,11 +333,6 @@ export class SimpleExtensionGalleryService implements IExtensionGalleryService {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
loadAllDependencies(dependencies: IExtensionIdentifier[], token: CancellationToken): Promise<IGalleryExtension[]> {
|
||||
// @ts-ignore
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
getExtensionsReport(): Promise<IReportedExtension[]> {
|
||||
// @ts-ignore
|
||||
return Promise.resolve(undefined);
|
||||
|
||||
@@ -24,7 +24,7 @@ import { IViewContainersRegistry, ViewContainer, Extensions as ViewContainerExte
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { TaskIdentifier } from 'vs/workbench/contrib/tasks/common/tasks';
|
||||
import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService';
|
||||
import { IOutputService } from 'vs/workbench/contrib/output/common/output';
|
||||
import { ITerminalConfiguration } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
|
||||
export const VIEWLET_ID = 'workbench.view.debug';
|
||||
export const VIEW_CONTAINER: ViewContainer = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer(VIEWLET_ID);
|
||||
@@ -111,7 +111,7 @@ export interface IExpression extends IReplElement, IExpressionContainer {
|
||||
}
|
||||
|
||||
export interface IDebugger {
|
||||
createDebugAdapter(session: IDebugSession, outputService: IOutputService): Promise<IDebugAdapter>;
|
||||
createDebugAdapter(session: IDebugSession): Promise<IDebugAdapter>;
|
||||
runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments): Promise<number | undefined>;
|
||||
getCustomTelemetryService(): Promise<TelemetryService | undefined>;
|
||||
}
|
||||
@@ -573,13 +573,7 @@ export interface ITerminalSettings {
|
||||
osxExec: string,
|
||||
linuxExec: string
|
||||
};
|
||||
integrated: {
|
||||
shell: {
|
||||
osx: string,
|
||||
windows: string,
|
||||
linux: string
|
||||
}
|
||||
};
|
||||
integrated: ITerminalConfiguration;
|
||||
}
|
||||
|
||||
export interface IConfigurationManager {
|
||||
@@ -609,7 +603,6 @@ export interface IConfigurationManager {
|
||||
|
||||
activateDebuggers(activationEvent: string, debugType?: string): Promise<void>;
|
||||
|
||||
needsToRunInExtHost(debugType: string): boolean;
|
||||
hasDebugConfigurationProvider(debugType: string): boolean;
|
||||
|
||||
registerDebugConfigurationProvider(debugConfigurationProvider: IDebugConfigurationProvider): IDisposable;
|
||||
@@ -618,9 +611,6 @@ export interface IConfigurationManager {
|
||||
registerDebugAdapterDescriptorFactory(debugAdapterDescriptorFactory: IDebugAdapterDescriptorFactory): IDisposable;
|
||||
unregisterDebugAdapterDescriptorFactory(debugAdapterDescriptorFactory: IDebugAdapterDescriptorFactory): void;
|
||||
|
||||
registerDebugAdapterTrackerFactory(debugAdapterTrackerFactory: IDebugAdapterTrackerFactory): IDisposable;
|
||||
unregisterDebugAdapterTrackerFactory(debugAdapterTrackerFactory: IDebugAdapterTrackerFactory): void;
|
||||
|
||||
resolveConfigurationByProviders(folderUri: uri | undefined, type: string | undefined, debugConfiguration: any): Promise<any>;
|
||||
getDebugAdapterDescriptor(session: IDebugSession): Promise<IAdapterDescriptor | undefined>;
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IDebugConfigurationProvider, ICompound, IDebugConfiguration, IConfig, IGlobalConfig, IConfigurationManager, ILaunch, IDebugAdapterDescriptorFactory, IDebugAdapter, ITerminalSettings, ITerminalLauncher, IDebugSession, IAdapterDescriptor, CONTEXT_DEBUG_CONFIGURATION_TYPE, IDebugAdapterFactory, IDebugAdapterTrackerFactory, IDebugService } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IDebugConfigurationProvider, ICompound, IDebugConfiguration, IConfig, IGlobalConfig, IConfigurationManager, ILaunch, IDebugAdapterDescriptorFactory, IDebugAdapter, ITerminalSettings, ITerminalLauncher, IDebugSession, IAdapterDescriptor, CONTEXT_DEBUG_CONFIGURATION_TYPE, IDebugAdapterFactory, IDebugService } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { Debugger } from 'vs/workbench/contrib/debug/node/debugger';
|
||||
import { IEditorService, ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
@@ -52,7 +52,6 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
private _onDidSelectConfigurationName = new Emitter<void>();
|
||||
private configProviders: IDebugConfigurationProvider[];
|
||||
private adapterDescriptorFactories: IDebugAdapterDescriptorFactory[];
|
||||
private adapterTrackerFactories: IDebugAdapterTrackerFactory[];
|
||||
private debugAdapterFactories: Map<string, IDebugAdapterFactory>;
|
||||
private terminalLauncher: ITerminalLauncher;
|
||||
private debugConfigurationTypeContext: IContextKey<string>;
|
||||
@@ -72,7 +71,6 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
) {
|
||||
this.configProviders = [];
|
||||
this.adapterDescriptorFactories = [];
|
||||
this.adapterTrackerFactories = [];
|
||||
this.debuggers = [];
|
||||
this.toDispose = [];
|
||||
this.registerListeners(lifecycleService);
|
||||
@@ -164,24 +162,6 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
// debug adapter trackers
|
||||
|
||||
registerDebugAdapterTrackerFactory(debugAdapterTrackerFactory: IDebugAdapterTrackerFactory): IDisposable {
|
||||
this.adapterTrackerFactories.push(debugAdapterTrackerFactory);
|
||||
return {
|
||||
dispose: () => {
|
||||
this.unregisterDebugAdapterTrackerFactory(debugAdapterTrackerFactory);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
unregisterDebugAdapterTrackerFactory(debugAdapterTrackerFactory: IDebugAdapterTrackerFactory): void {
|
||||
const ix = this.adapterTrackerFactories.indexOf(debugAdapterTrackerFactory);
|
||||
if (ix >= 0) {
|
||||
this.adapterTrackerFactories.splice(ix, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// debug configurations
|
||||
|
||||
registerDebugConfigurationProvider(debugConfigurationProvider: IDebugConfigurationProvider): IDisposable {
|
||||
@@ -206,13 +186,6 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
return providers.length > 0;
|
||||
}
|
||||
|
||||
needsToRunInExtHost(debugType: string): boolean {
|
||||
|
||||
// if the given debugType matches any registered tracker factory we need to run the DA in the EH
|
||||
const providers = this.adapterTrackerFactories.filter(p => p.type === debugType || p.type === '*');
|
||||
return providers.length > 0;
|
||||
}
|
||||
|
||||
resolveConfigurationByProviders(folderUri: uri | undefined, type: string | undefined, debugConfiguration: IConfig): Promise<IConfig | null | undefined> {
|
||||
return this.activateDebuggers('onDebugResolve', type).then(() => {
|
||||
// pipe the config through the promises sequentially. Append at the end the '*' types
|
||||
|
||||
@@ -25,7 +25,6 @@ import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { normalizeDriveLetter } from 'vs/base/common/labels';
|
||||
import { IOutputService } from 'vs/workbench/contrib/output/common/output';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
@@ -62,7 +61,6 @@ export class DebugSession implements IDebugSession {
|
||||
private _parentSession: IDebugSession | undefined,
|
||||
@IDebugService private readonly debugService: IDebugService,
|
||||
@ITelemetryService private readonly telemetryService: ITelemetryService,
|
||||
@IOutputService private readonly outputService: IOutputService,
|
||||
@IWindowService private readonly windowService: IWindowService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IViewletService private readonly viewletService: IViewletService,
|
||||
@@ -167,7 +165,7 @@ export class DebugSession implements IDebugSession {
|
||||
|
||||
return dbgr.getCustomTelemetryService().then(customTelemetryService => {
|
||||
|
||||
return dbgr.createDebugAdapter(this, this.outputService).then(debugAdapter => {
|
||||
return dbgr.createDebugAdapter(this).then(debugAdapter => {
|
||||
|
||||
this.raw = new RawDebugSession(debugAdapter, dbgr, this.telemetryService, customTelemetryService, this.environmentService);
|
||||
|
||||
|
||||
@@ -11,11 +11,8 @@ import { isObject } from 'vs/base/common/types';
|
||||
import { TelemetryAppenderClient } from 'vs/platform/telemetry/node/telemetryIpc';
|
||||
import { IJSONSchema, IJSONSchemaSnippet } from 'vs/base/common/jsonSchema';
|
||||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { IConfig, IDebuggerContribution, IDebugAdapterExecutable, INTERNAL_CONSOLE_OPTIONS_SCHEMA, IConfigurationManager, IDebugAdapter, ITerminalSettings, IDebugger, IDebugSession, IAdapterDescriptor, IDebugAdapterServer } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IConfig, IDebuggerContribution, INTERNAL_CONSOLE_OPTIONS_SCHEMA, IConfigurationManager, IDebugAdapter, ITerminalSettings, IDebugger, IDebugSession } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IOutputService } from 'vs/workbench/contrib/output/common/output';
|
||||
import { ExecutableDebugAdapter, SocketDebugAdapter } from 'vs/workbench/contrib/debug/node/debugAdapter';
|
||||
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
|
||||
import * as ConfigurationResolverUtils from 'vs/workbench/services/configurationResolver/common/configurationResolverUtils';
|
||||
import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService';
|
||||
@@ -38,7 +35,6 @@ export class Debugger implements IDebugger {
|
||||
constructor(private configurationManager: IConfigurationManager, dbgContribution: IDebuggerContribution, extensionDescription: IExtensionDescription,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@ITextResourcePropertiesService private readonly resourcePropertiesService: ITextResourcePropertiesService,
|
||||
@ICommandService private readonly commandService: ICommandService,
|
||||
@IConfigurationResolverService private readonly configurationResolverService: IConfigurationResolverService,
|
||||
@ITelemetryService private readonly telemetryService: ITelemetryService,
|
||||
) {
|
||||
@@ -97,97 +93,25 @@ export class Debugger implements IDebugger {
|
||||
}
|
||||
}
|
||||
|
||||
public createDebugAdapter(session: IDebugSession, outputService: IOutputService): Promise<IDebugAdapter> {
|
||||
public createDebugAdapter(session: IDebugSession): Promise<IDebugAdapter> {
|
||||
return this.configurationManager.activateDebuggers('onDebugAdapterProtocolTracker', this.type).then(_ => {
|
||||
if (this.inExtHost()) {
|
||||
const da = this.configurationManager.createDebugAdapter(session);
|
||||
if (da) {
|
||||
return Promise.resolve(da);
|
||||
}
|
||||
throw new Error(nls.localize('cannot.find.da', "Cannot find debug adapter for type '{0}'.", this.type));
|
||||
} else {
|
||||
return this.getAdapterDescriptor(session).then(adapterDescriptor => {
|
||||
switch (adapterDescriptor.type) {
|
||||
case 'executable':
|
||||
return new ExecutableDebugAdapter(adapterDescriptor, this.type, outputService);
|
||||
case 'server':
|
||||
return new SocketDebugAdapter(adapterDescriptor);
|
||||
case 'implementation':
|
||||
// TODO@AW: this.inExtHost() should now return true
|
||||
return Promise.resolve(this.configurationManager.createDebugAdapter(session));
|
||||
default:
|
||||
throw new Error('unknown descriptor type');
|
||||
}
|
||||
}).catch(err => {
|
||||
if (err && err.message) {
|
||||
throw new Error(nls.localize('cannot.create.da.with.err', "Cannot create debug adapter ({0}).", err.message));
|
||||
} else {
|
||||
throw new Error(nls.localize('cannot.create.da', "Cannot create debug adapter."));
|
||||
}
|
||||
});
|
||||
const da = this.configurationManager.createDebugAdapter(session);
|
||||
if (da) {
|
||||
return Promise.resolve(da);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private getAdapterDescriptor(session: IDebugSession): Promise<IAdapterDescriptor> {
|
||||
|
||||
// a "debugServer" attribute in the launch config takes precedence
|
||||
if (typeof session.configuration.debugServer === 'number') {
|
||||
return Promise.resolve(<IDebugAdapterServer>{
|
||||
type: 'server',
|
||||
port: session.configuration.debugServer
|
||||
});
|
||||
}
|
||||
|
||||
// try the new "createDebugAdapterDescriptor" and the deprecated "provideDebugAdapter" API
|
||||
return this.configurationManager.getDebugAdapterDescriptor(session).then(adapter => {
|
||||
|
||||
if (adapter) {
|
||||
return adapter;
|
||||
}
|
||||
|
||||
// try deprecated command based extension API "adapterExecutableCommand" to determine the executable
|
||||
if (this.debuggerContribution.adapterExecutableCommand) {
|
||||
console.info('debugAdapterExecutable attribute in package.json is deprecated and support for it will be removed soon; please use DebugAdapterDescriptorFactory.createDebugAdapterDescriptor instead.');
|
||||
const rootFolder = session.root ? session.root.uri.toString() : undefined;
|
||||
return this.commandService.executeCommand<IDebugAdapterExecutable>(this.debuggerContribution.adapterExecutableCommand, rootFolder).then(ae => {
|
||||
if (ae) {
|
||||
return <IAdapterDescriptor>{
|
||||
type: 'executable',
|
||||
command: ae.command,
|
||||
args: ae.args || []
|
||||
};
|
||||
}
|
||||
throw new Error('command adapterExecutableCommand did not return proper command.');
|
||||
});
|
||||
}
|
||||
|
||||
// fallback: use executable information from package.json
|
||||
const ae = ExecutableDebugAdapter.platformAdapterExecutable(this.mergedExtensionDescriptions, this.type);
|
||||
if (ae === undefined) {
|
||||
throw new Error('no executable specified in package.json');
|
||||
}
|
||||
return ae;
|
||||
throw new Error(nls.localize('cannot.find.da', "Cannot find debug adapter for type '{0}'.", this.type));
|
||||
});
|
||||
}
|
||||
|
||||
substituteVariables(folder: IWorkspaceFolder | undefined, config: IConfig): Promise<IConfig> {
|
||||
if (this.inExtHost()) {
|
||||
return this.configurationManager.substituteVariables(this.type, folder, config).then(config => {
|
||||
return this.configurationResolverService.resolveWithInteractionReplace(folder, config, 'launch', this.variables);
|
||||
});
|
||||
} else {
|
||||
return this.configurationManager.substituteVariables(this.type, folder, config).then(config => {
|
||||
return this.configurationResolverService.resolveWithInteractionReplace(folder, config, 'launch', this.variables);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments): Promise<number | undefined> {
|
||||
const config = this.configurationService.getValue<ITerminalSettings>('terminal');
|
||||
return this.configurationManager.runInTerminal(this.inExtHost() ? this.type : '*', args, config);
|
||||
}
|
||||
|
||||
private inExtHost(): boolean {
|
||||
return true;
|
||||
return this.configurationManager.runInTerminal(this.type, args, config);
|
||||
}
|
||||
|
||||
get label(): string {
|
||||
|
||||
@@ -10,6 +10,7 @@ import * as pfs from 'vs/base/node/pfs';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { ITerminalLauncher, ITerminalSettings } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { getPathFromAmdModule } from 'vs/base/common/amd';
|
||||
import { getDefaultShell } from 'vs/workbench/contrib/terminal/node/terminal';
|
||||
|
||||
const TERMINAL_TITLE = nls.localize('console.title', "VS Code Console");
|
||||
|
||||
@@ -314,13 +315,13 @@ export function prepareCommand(args: DebugProtocol.RunInTerminalRequestArguments
|
||||
let shell: string;
|
||||
const shell_config = config.integrated.shell;
|
||||
if (env.isWindows) {
|
||||
shell = shell_config.windows;
|
||||
shell = shell_config.windows || getDefaultShell(env.Platform.Windows);
|
||||
shellType = ShellType.cmd;
|
||||
} else if (env.isLinux) {
|
||||
shell = shell_config.linux;
|
||||
shell = shell_config.linux || getDefaultShell(env.Platform.Linux);
|
||||
shellType = ShellType.bash;
|
||||
} else if (env.isMacintosh) {
|
||||
shell = shell_config.osx;
|
||||
shell = shell_config.osx || getDefaultShell(env.Platform.Mac);
|
||||
shellType = ShellType.bash;
|
||||
} else {
|
||||
throw new Error('Unknown platform');
|
||||
|
||||
@@ -14,7 +14,7 @@ import { DebugSession } from 'vs/workbench/contrib/debug/electron-browser/debugS
|
||||
import { ReplModel } from 'vs/workbench/contrib/debug/common/replModel';
|
||||
|
||||
function createMockSession(model: DebugModel, name = 'mockSession', parentSession?: DebugSession | undefined): DebugSession {
|
||||
return new DebugSession({ resolved: { name, type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, parentSession, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!);
|
||||
return new DebugSession({ resolved: { name, type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, parentSession, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!);
|
||||
}
|
||||
|
||||
suite('Debug - Model', () => {
|
||||
@@ -436,7 +436,7 @@ suite('Debug - Model', () => {
|
||||
// Repl output
|
||||
|
||||
test('repl output', () => {
|
||||
const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, undefined, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!);
|
||||
const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, undefined, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!);
|
||||
const repl = new ReplModel(session);
|
||||
repl.appendToRepl('first line\n', severity.Error);
|
||||
repl.appendToRepl('second line ', severity.Error);
|
||||
|
||||
@@ -130,7 +130,7 @@ suite('Debug - Debugger', () => {
|
||||
const testResourcePropertiesService = new TestTextResourcePropertiesService(configurationService);
|
||||
|
||||
setup(() => {
|
||||
_debugger = new Debugger(configurationManager, debuggerContribution, extensionDescriptor0, configurationService, testResourcePropertiesService, undefined!, undefined!, undefined!);
|
||||
_debugger = new Debugger(configurationManager, debuggerContribution, extensionDescriptor0, configurationService, testResourcePropertiesService, undefined!, undefined!);
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
|
||||
@@ -19,6 +19,8 @@ import { IAccessibilityService } from 'vs/platform/accessibility/common/accessib
|
||||
import { IAsyncDataSource, ITreeNode } from 'vs/base/browser/ui/tree/tree';
|
||||
import { IListVirtualDelegate, IListRenderer } from 'vs/base/browser/ui/list/list';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { isNonEmptyArray } from 'vs/base/common/arrays';
|
||||
|
||||
export interface IExtensionTemplateData {
|
||||
icon: HTMLImageElement;
|
||||
@@ -217,4 +219,46 @@ export class ExtensionsTree extends WorkbenchAsyncDataTree<IExtensionData, IExte
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtensionData implements IExtensionData {
|
||||
|
||||
readonly extension: IExtension;
|
||||
readonly parent: IExtensionData | null;
|
||||
private readonly getChildrenExtensionIds: (extension: IExtension) => string[];
|
||||
private readonly childrenExtensionIds: string[];
|
||||
private readonly extensionsWorkbenchService: IExtensionsWorkbenchService;
|
||||
|
||||
constructor(extension: IExtension, parent: IExtensionData | null, getChildrenExtensionIds: (extension: IExtension) => string[], extensionsWorkbenchService: IExtensionsWorkbenchService) {
|
||||
this.extension = extension;
|
||||
this.parent = parent;
|
||||
this.getChildrenExtensionIds = getChildrenExtensionIds;
|
||||
this.extensionsWorkbenchService = extensionsWorkbenchService;
|
||||
this.childrenExtensionIds = this.getChildrenExtensionIds(extension);
|
||||
}
|
||||
|
||||
get hasChildren(): boolean {
|
||||
return isNonEmptyArray(this.childrenExtensionIds);
|
||||
}
|
||||
|
||||
async getChildren(): Promise<IExtensionData[] | null> {
|
||||
if (this.hasChildren) {
|
||||
const localById = this.extensionsWorkbenchService.local.reduce((result, e) => { result.set(e.identifier.id.toLowerCase(), e); return result; }, new Map<string, IExtension>());
|
||||
const result: IExtension[] = [];
|
||||
const toQuery: string[] = [];
|
||||
for (const extensionId of this.childrenExtensionIds) {
|
||||
const id = extensionId.toLowerCase();
|
||||
const local = localById.get(id);
|
||||
if (local) {
|
||||
result.push(local);
|
||||
} else {
|
||||
toQuery.push(id);
|
||||
}
|
||||
}
|
||||
const galleryResult = await this.extensionsWorkbenchService.queryGallery({ names: this.childrenExtensionIds, pageSize: this.childrenExtensionIds.length }, CancellationToken.None);
|
||||
result.push(...galleryResult.firstPage);
|
||||
return result.map(extension => new ExtensionData(extension, this, this.getChildrenExtensionIds, this.extensionsWorkbenchService));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -69,14 +69,6 @@ export interface IExtension {
|
||||
readonly isMalicious: boolean;
|
||||
}
|
||||
|
||||
export interface IExtensionDependencies {
|
||||
dependencies: IExtensionDependencies[];
|
||||
hasDependencies: boolean;
|
||||
identifier: string;
|
||||
extension: IExtension;
|
||||
dependent: IExtensionDependencies | null;
|
||||
}
|
||||
|
||||
export const SERVICE_ID = 'extensionsWorkbenchService';
|
||||
|
||||
export const IExtensionsWorkbenchService = createDecorator<IExtensionsWorkbenchService>(SERVICE_ID);
|
||||
@@ -97,7 +89,6 @@ export interface IExtensionsWorkbenchService {
|
||||
installVersion(extension: IExtension, version: string): Promise<IExtension>;
|
||||
reinstall(extension: IExtension): Promise<IExtension>;
|
||||
setEnablement(extensions: IExtension | IExtension[], enablementState: EnablementState): Promise<void>;
|
||||
loadDependencies(extension: IExtension, token: CancellationToken): Promise<IExtensionDependencies | null>;
|
||||
open(extension: IExtension, sideByside?: boolean): Promise<any>;
|
||||
checkForUpdates(): Promise<void>;
|
||||
allowedBadgeProviders: string[];
|
||||
|
||||
@@ -24,7 +24,7 @@ import { IExtensionTipsService } from 'vs/platform/extensionManagement/common/ex
|
||||
import { IExtensionManifest, IKeyBinding, IView, IViewContainer, ExtensionType } from 'vs/platform/extensions/common/extensions';
|
||||
import { ResolvedKeybinding, KeyMod, KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput';
|
||||
import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, IExtension, IExtensionDependencies, ExtensionContainers } from 'vs/workbench/contrib/extensions/common/extensions';
|
||||
import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, IExtension, ExtensionContainers } from 'vs/workbench/contrib/extensions/common/extensions';
|
||||
import { RatingsWidget, InstallCountWidget, RemoteBadgeWidget } from 'vs/workbench/contrib/extensions/electron-browser/extensionsWidgets';
|
||||
import { EditorOptions } from 'vs/workbench/common/editor';
|
||||
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
@@ -43,13 +43,13 @@ import { Color } from 'vs/base/common/color';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { ExtensionsTree, IExtensionData } from 'vs/workbench/contrib/extensions/browser/extensionsViewer';
|
||||
import { ExtensionsTree, ExtensionData } from 'vs/workbench/contrib/extensions/browser/extensionsViewer';
|
||||
import { ShowCurrentReleaseNotesAction } from 'vs/workbench/contrib/update/electron-browser/update';
|
||||
import { KeybindingParser } from 'vs/base/common/keybindingParser';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { getDefaultValue } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { isUndefined, withUndefinedAsNull } from 'vs/base/common/types';
|
||||
import { isUndefined } from 'vs/base/common/types';
|
||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
@@ -181,7 +181,6 @@ export class ExtensionEditor extends BaseEditor {
|
||||
private extensionReadme: Cache<string> | null;
|
||||
private extensionChangelog: Cache<string> | null;
|
||||
private extensionManifest: Cache<IExtensionManifest | null> | null;
|
||||
private extensionDependencies: Cache<IExtensionDependencies | null> | null;
|
||||
|
||||
private layoutParticipants: ILayoutParticipant[] = [];
|
||||
private contentDisposables: IDisposable[] = [];
|
||||
@@ -210,7 +209,6 @@ export class ExtensionEditor extends BaseEditor {
|
||||
this.extensionReadme = null;
|
||||
this.extensionChangelog = null;
|
||||
this.extensionManifest = null;
|
||||
this.extensionDependencies = null;
|
||||
}
|
||||
|
||||
createEditor(parent: HTMLElement): void {
|
||||
@@ -298,7 +296,6 @@ export class ExtensionEditor extends BaseEditor {
|
||||
this.extensionReadme = new Cache(() => createCancelablePromise(token => extension.getReadme(token)));
|
||||
this.extensionChangelog = new Cache(() => createCancelablePromise(token => extension.getChangelog(token)));
|
||||
this.extensionManifest = new Cache(() => createCancelablePromise(token => extension.getManifest(token)));
|
||||
this.extensionDependencies = new Cache(() => createCancelablePromise(token => this.extensionsWorkbenchService.loadDependencies(extension, token)));
|
||||
|
||||
const remoteBadge = this.instantiationService.createInstance(RemoteBadgeWidget, this.iconContainer, true);
|
||||
const onError = Event.once(domEvent(this.icon, 'error'));
|
||||
@@ -643,69 +640,28 @@ export class ExtensionEditor extends BaseEditor {
|
||||
}
|
||||
|
||||
private openDependencies(extension: IExtension): Promise<IActiveElement> {
|
||||
if (extension.dependencies.length === 0) {
|
||||
if (arrays.isFalsyOrEmpty(extension.dependencies)) {
|
||||
append(this.content, $('p.nocontent')).textContent = localize('noDependencies', "No Dependencies");
|
||||
return Promise.resolve(this.content);
|
||||
}
|
||||
|
||||
return this.loadContents(() => this.extensionDependencies!.get())
|
||||
.then<IActiveElement, IActiveElement>(extensionDependencies => {
|
||||
if (extensionDependencies) {
|
||||
const content = $('div', { class: 'subcontent' });
|
||||
const scrollableContent = new DomScrollableElement(content, {});
|
||||
append(this.content, scrollableContent.getDomNode());
|
||||
this.contentDisposables.push(scrollableContent);
|
||||
const content = $('div', { class: 'subcontent' });
|
||||
const scrollableContent = new DomScrollableElement(content, {});
|
||||
append(this.content, scrollableContent.getDomNode());
|
||||
this.contentDisposables.push(scrollableContent);
|
||||
|
||||
const dependenciesTree = this.renderDependencies(content, extensionDependencies);
|
||||
const layout = () => {
|
||||
scrollableContent.scanDomNode();
|
||||
const scrollDimensions = scrollableContent.getScrollDimensions();
|
||||
dependenciesTree.layout(scrollDimensions.height);
|
||||
};
|
||||
const removeLayoutParticipant = arrays.insert(this.layoutParticipants, { layout });
|
||||
this.contentDisposables.push(toDisposable(removeLayoutParticipant));
|
||||
const dependenciesTree = this.instantiationService.createInstance(ExtensionsTree, new ExtensionData(extension, null, extension => extension.dependencies || [], this.extensionsWorkbenchService), content);
|
||||
const layout = () => {
|
||||
scrollableContent.scanDomNode();
|
||||
const scrollDimensions = scrollableContent.getScrollDimensions();
|
||||
dependenciesTree.layout(scrollDimensions.height);
|
||||
};
|
||||
const removeLayoutParticipant = arrays.insert(this.layoutParticipants, { layout });
|
||||
this.contentDisposables.push(toDisposable(removeLayoutParticipant));
|
||||
|
||||
this.contentDisposables.push(dependenciesTree);
|
||||
scrollableContent.scanDomNode();
|
||||
return { focus() { dependenciesTree.domFocus(); } };
|
||||
} else {
|
||||
append(this.content, $('p.nocontent')).textContent = localize('noDependencies', "No Dependencies");
|
||||
return Promise.resolve(this.content);
|
||||
}
|
||||
}, error => {
|
||||
append(this.content, $('p.nocontent')).textContent = error;
|
||||
this.notificationService.error(error);
|
||||
return this.content;
|
||||
});
|
||||
}
|
||||
|
||||
private renderDependencies(container: HTMLElement, extensionDependencies: IExtensionDependencies): ExtensionsTree {
|
||||
class ExtensionData implements IExtensionData {
|
||||
|
||||
private readonly extensionDependencies: IExtensionDependencies;
|
||||
|
||||
constructor(extensionDependencies: IExtensionDependencies) {
|
||||
this.extensionDependencies = extensionDependencies;
|
||||
}
|
||||
|
||||
get extension(): IExtension {
|
||||
return this.extensionDependencies.extension;
|
||||
}
|
||||
|
||||
get parent(): IExtensionData | null {
|
||||
return this.extensionDependencies.dependent ? new ExtensionData(this.extensionDependencies.dependent) : null;
|
||||
}
|
||||
|
||||
get hasChildren(): boolean {
|
||||
return this.extensionDependencies.hasDependencies;
|
||||
}
|
||||
|
||||
getChildren(): Promise<IExtensionData[] | null> {
|
||||
return this.extensionDependencies.dependencies ? Promise.resolve(this.extensionDependencies.dependencies.map(d => new ExtensionData(d))) : Promise.resolve(null);
|
||||
}
|
||||
}
|
||||
|
||||
return this.instantiationService.createInstance(ExtensionsTree, new ExtensionData(extensionDependencies), container);
|
||||
this.contentDisposables.push(dependenciesTree);
|
||||
scrollableContent.scanDomNode();
|
||||
return Promise.resolve({ focus() { dependenciesTree.domFocus(); } });
|
||||
}
|
||||
|
||||
private openExtensionPack(extension: IExtension): Promise<IActiveElement> {
|
||||
@@ -714,7 +670,7 @@ export class ExtensionEditor extends BaseEditor {
|
||||
append(this.content, scrollableContent.getDomNode());
|
||||
this.contentDisposables.push(scrollableContent);
|
||||
|
||||
const extensionsPackTree = this.renderExtensionPack(content, extension);
|
||||
const extensionsPackTree = this.instantiationService.createInstance(ExtensionsTree, new ExtensionData(extension, null, extension => extension.extensionPack || [], this.extensionsWorkbenchService), content);
|
||||
const layout = () => {
|
||||
scrollableContent.scanDomNode();
|
||||
const scrollDimensions = scrollableContent.getScrollDimensions();
|
||||
@@ -728,35 +684,6 @@ export class ExtensionEditor extends BaseEditor {
|
||||
return Promise.resolve({ focus() { extensionsPackTree.domFocus(); } });
|
||||
}
|
||||
|
||||
private renderExtensionPack(container: HTMLElement, extension: IExtension): ExtensionsTree {
|
||||
const extensionsWorkbenchService = this.extensionsWorkbenchService;
|
||||
class ExtensionData implements IExtensionData {
|
||||
|
||||
readonly extension: IExtension;
|
||||
readonly parent: IExtensionData | null;
|
||||
|
||||
constructor(extension: IExtension, parent?: IExtensionData) {
|
||||
this.extension = extension;
|
||||
this.parent = withUndefinedAsNull(parent);
|
||||
}
|
||||
|
||||
get hasChildren(): boolean {
|
||||
return this.extension.extensionPack.length > 0;
|
||||
}
|
||||
|
||||
getChildren(): Promise<IExtensionData[] | null> {
|
||||
if (this.hasChildren) {
|
||||
const names = arrays.distinct(this.extension.extensionPack, e => e.toLowerCase());
|
||||
return extensionsWorkbenchService.queryGallery({ names, pageSize: names.length }, CancellationToken.None)
|
||||
.then(result => result.firstPage.map(extension => new ExtensionData(extension, this)));
|
||||
}
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
}
|
||||
|
||||
return this.instantiationService.createInstance(ExtensionsTree, new ExtensionData(extension), container);
|
||||
}
|
||||
|
||||
private renderSettings(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean {
|
||||
const contributes = manifest.contributes;
|
||||
const configuration = contributes && contributes.configuration;
|
||||
|
||||
@@ -1558,7 +1558,7 @@ export class InstallExtensionsAction extends OpenExtensionsViewletAction {
|
||||
export class ShowEnabledExtensionsAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.extensions.action.showEnabledExtensions';
|
||||
static LABEL = localize('showEnabledExtensions', 'Show Enabled Extensions');
|
||||
static LABEL = localize('showEnabledExtensions', "Show Enabled Extensions");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
|
||||
@@ -23,8 +23,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
|
||||
import { IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
// {{SQL CARBON EDIT}}
|
||||
import { IExtension, IExtensionDependencies, ExtensionState, IExtensionsWorkbenchService, AutoUpdateConfigurationKey, AutoCheckUpdatesConfigurationKey, ExtensionsPolicyKey, ExtensionsPolicy } from 'vs/workbench/contrib/extensions/common/extensions';
|
||||
import { IExtension, ExtensionState, IExtensionsWorkbenchService, AutoUpdateConfigurationKey, AutoCheckUpdatesConfigurationKey, ExtensionsPolicyKey, ExtensionsPolicy } from 'vs/workbench/contrib/extensions/common/extensions';
|
||||
import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IURLService, IURLHandler } from 'vs/platform/url/common/url';
|
||||
import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput';
|
||||
@@ -323,53 +322,6 @@ ${this.description}
|
||||
}
|
||||
}
|
||||
|
||||
class ExtensionDependencies implements IExtensionDependencies {
|
||||
|
||||
private _hasDependencies: boolean | null = null;
|
||||
|
||||
constructor(private _extension: IExtension, private _identifier: string, private _map: Map<string, IExtension>, private _dependent: IExtensionDependencies | null = null) { }
|
||||
|
||||
get hasDependencies(): boolean {
|
||||
if (this._hasDependencies === null) {
|
||||
this._hasDependencies = this.computeHasDependencies();
|
||||
}
|
||||
return this._hasDependencies;
|
||||
}
|
||||
|
||||
get extension(): IExtension {
|
||||
return this._extension;
|
||||
}
|
||||
|
||||
get identifier(): string {
|
||||
return this._identifier;
|
||||
}
|
||||
|
||||
get dependent(): IExtensionDependencies | null {
|
||||
return this._dependent;
|
||||
}
|
||||
|
||||
get dependencies(): IExtensionDependencies[] {
|
||||
if (!this.hasDependencies) {
|
||||
return [];
|
||||
}
|
||||
return this._extension.dependencies.map(id => new ExtensionDependencies(this._map.get(id)!, id, this._map, this));
|
||||
}
|
||||
|
||||
private computeHasDependencies(): boolean {
|
||||
if (this._extension && this._extension.dependencies.length > 0) {
|
||||
let dependent = this._dependent;
|
||||
while (dependent !== null) {
|
||||
if (dependent.identifier === this.identifier) {
|
||||
return false;
|
||||
}
|
||||
dependent = dependent.dependent;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class Extensions extends Disposable {
|
||||
|
||||
private readonly _onChange: Emitter<Extension | undefined> = new Emitter<Extension | undefined>();
|
||||
@@ -666,27 +618,6 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
|
||||
});
|
||||
}
|
||||
|
||||
loadDependencies(extension: IExtension, token: CancellationToken): Promise<IExtensionDependencies | null> {
|
||||
if (!extension.dependencies.length) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
return this.extensionService.getExtensionsReport()
|
||||
.then(report => {
|
||||
const maliciousSet = getMaliciousExtensionsSet(report);
|
||||
|
||||
return this.galleryService.loadAllDependencies((<Extension>extension).dependencies.map(id => ({ id })), token)
|
||||
.then(galleryExtensions => {
|
||||
const extensions: IExtension[] = [...this.local, ...galleryExtensions.map(galleryExtension => this.fromGallery(galleryExtension, maliciousSet))];
|
||||
const map = new Map<string, IExtension>();
|
||||
for (const extension of extensions) {
|
||||
map.set(extension.identifier.id, extension);
|
||||
}
|
||||
return new ExtensionDependencies(extension, extension.identifier.id, map);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
open(extension: IExtension, sideByside: boolean = false): Promise<any> {
|
||||
return Promise.resolve(this.editorService.openEditor(this.instantiationService.createInstance(ExtensionsInput, extension), undefined, sideByside ? SIDE_GROUP : ACTIVE_GROUP));
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user