Merge from vscode 8aa90d444f5d051984e8055f547c4252d53479b3 (#5587)

* Merge from vscode 8aa90d444f5d051984e8055f547c4252d53479b3

* pipeline errors

* fix build
This commit is contained in:
Anthony Dresser
2019-05-23 11:16:03 -07:00
committed by GitHub
parent ca36f20c6b
commit cf8f8907ee
141 changed files with 6450 additions and 1228 deletions

4
.gitignore vendored
View File

@@ -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/

View File

@@ -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'

View File

@@ -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');

View File

@@ -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

View File

@@ -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"

View File

@@ -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

View File

@@ -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"

View File

@@ -0,0 +1,2 @@
node_modules/
*.js

View 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;
}

View 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;
}

View 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

View 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;
}

View 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;
}

View File

@@ -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" }

View File

@@ -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
View 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);

View File

@@ -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"

View File

@@ -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 {

View File

@@ -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"
}
]
},

View File

@@ -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"
}
]
}

View File

@@ -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;

View File

@@ -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);
}
}
}

View File

@@ -35,6 +35,10 @@ const previewStrings = {
'Content Disabled Security Warning')
};
function escapeAttribute(value: string): string {
return value.replace(/"/g, '&quot;');
}
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, '&quot;')}"
data-strings="${JSON.stringify(previewStrings).replace(/"/g, '&quot;')}"
data-state="${JSON.stringify(state || {}).replace(/"/g, '&quot;')}">
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, '&quot;')}" href="${this.fixHref(resource, style).replace(/"/g, '&quot;')}" 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');
}

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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"

View File

@@ -0,0 +1,2 @@
out
node_modules

View 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"
]
}
]
}

View File

@@ -0,0 +1,6 @@
.vscode/**
typings/**
**/*.ts
**/*.map
.gitignore
tsconfig.json

View 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"
}
]
}
}
}

View 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);
}

View 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();
}
}

View 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'/>

View File

@@ -0,0 +1,9 @@
{
"extends": "../shared.tsconfig.json",
"compilerOptions": {
"outDir": "./out"
},
"include": [
"src/**/*"
]
}

File diff suppressed because it is too large Load Diff

View File

@@ -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==

View File

@@ -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
View File

@@ -0,0 +1,3 @@
disturl "http://nodejs.org/dist"
target "10.2.1"
runtime "node"

20
remote/installDevModules.sh Executable file
View 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

View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

View File

@@ -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);
}
});

View File

@@ -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;
}
}

View File

@@ -1025,9 +1025,11 @@ declare module 'vscode-xterm' {
charMeasure?: { height: number, width: number };
renderer: {
_renderLayers: any[];
onIntersectionChange: any;
_renderCoordinator: {
_renderer: {
_renderLayers: any[];
};
_onIntersectionChange: any;
};
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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 {

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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();

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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[] {

View File

@@ -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>

View File

@@ -17,7 +17,7 @@
document.head.appendChild(script);
}
loadScript('../../../../../out/vs/loader.js', function () {
loadScript('./out/vs/loader.js', function () {
// @ts-ignore
require.config({

View File

@@ -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);

View File

@@ -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('===========================================================');

View File

@@ -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();
});

View File

@@ -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);

View File

@@ -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()));

View 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);
}
});

View File

@@ -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;

View File

@@ -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;
});
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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`

View 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);
}
}

View File

@@ -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();
}
}

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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(

View File

@@ -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>;

View File

@@ -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.'));

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -67,6 +67,7 @@ export interface IProductConfiguration {
keyboardShortcutsUrlWin: string;
introductoryVideosUrl: string;
tipsAndTricksUrl: string;
newsletterSignupUrl: string;
twitterUrl: string;
requestFeatureUrl: string;
reportIssueUrl: string;

View 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);
}
};

View File

@@ -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}`);

View File

@@ -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

View File

@@ -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);

View File

@@ -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;

View File

@@ -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,

View File

@@ -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;
}
}

View File

@@ -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);
});
}

View File

@@ -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);

View File

@@ -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>;

View File

@@ -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

View File

@@ -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);

View File

@@ -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 {

View File

@@ -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');

View File

@@ -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);

View File

@@ -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(() => {

View File

@@ -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;
}
}

View File

@@ -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[];

View File

@@ -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;

View File

@@ -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,

View File

@@ -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