Merge from vscode 31e03b8ffbb218a87e3941f2b63a249f061fe0e4 (#4986)

This commit is contained in:
Anthony Dresser
2019-04-10 16:29:23 -07:00
committed by GitHub
parent 18c54f41bd
commit 8315dacda4
320 changed files with 5540 additions and 3822 deletions

View File

@@ -65,8 +65,7 @@ interface Asset {
platform: string;
type: string;
url: string;
// {{SQL CARBON EDIT}}
mooncakeUrl: string | undefined;
mooncakeUrl?: string;
hash: string;
sha256hash: string;
size: number;
@@ -189,56 +188,18 @@ async function publish(commit: string, quality: string, platform: string, type:
const blobService = azure.createBlobService(storageAccount, process.env['AZURE_STORAGE_ACCESS_KEY_2']!)
.withFilter(new azure.ExponentialRetryPolicyFilter(20));
// {{SQL CARBON EDIT}}
await assertContainer(blobService, quality);
const blobExists = await doesAssetExist(blobService, quality, blobName);
const promises = [];
if (!blobExists) {
promises.push(uploadBlob(blobService, quality, blobName, file));
}
// {{SQL CARBON EDIT}}
if (process.env['MOONCAKE_STORAGE_ACCESS_KEY']) {
const mooncakeBlobService = azure.createBlobService(storageAccount, process.env['MOONCAKE_STORAGE_ACCESS_KEY']!, `${storageAccount}.blob.core.chinacloudapi.cn`)
.withFilter(new azure.ExponentialRetryPolicyFilter(20));
// mooncake is fussy and far away, this is needed!
mooncakeBlobService.defaultClientRequestTimeoutInMs = 10 * 60 * 1000;
await Promise.all([
assertContainer(blobService, quality),
assertContainer(mooncakeBlobService, quality)
]);
const [blobExists, moooncakeBlobExists] = await Promise.all([
doesAssetExist(blobService, quality, blobName),
doesAssetExist(mooncakeBlobService, quality, blobName)
]);
const promises: Array<Promise<void>> = [];
if (!blobExists) {
promises.push(uploadBlob(blobService, quality, blobName, file));
}
if (!moooncakeBlobExists) {
promises.push(uploadBlob(mooncakeBlobService, quality, blobName, file));
}
} else {
console.log('Skipping Mooncake publishing.');
}
if (promises.length === 0) {
if (blobExists) {
console.log(`Blob ${quality}, ${blobName} already exists, not publishing again.`);
return;
}
console.log('Uploading blobs to Azure storage...');
await Promise.all(promises);
await uploadBlob(blobService, quality, blobName, file);
console.log('Blobs successfully uploaded.');
@@ -250,8 +211,6 @@ async function publish(commit: string, quality: string, platform: string, type:
platform: platform,
type: type,
url: `${process.env['AZURE_CDN_URL']}/${quality}/${blobName}`,
// {{SQL CARBON EDIT}}
mooncakeUrl: process.env['MOONCAKE_CDN_URL'] ? `${process.env['MOONCAKE_CDN_URL']}/${quality}/${blobName}` : undefined,
hash: sha1hash,
sha256hash,
size

View File

@@ -0,0 +1,176 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as url from 'url';
import * as azure from 'azure-storage';
import * as mime from 'mime';
import { DocumentClient, RetrievedDocument } from 'documentdb';
function log(...args: any[]) {
console.log(...[`[${new Date().toISOString()}]`, ...args]);
}
function error(...args: any[]) {
console.error(...[`[${new Date().toISOString()}]`, ...args]);
}
if (process.argv.length < 3) {
error('Usage: node sync-mooncake.js <quality>');
process.exit(-1);
}
interface Build extends RetrievedDocument {
assets: Asset[];
}
interface Asset {
platform: string;
type: string;
url: string;
mooncakeUrl: string;
hash: string;
sha256hash: string;
size: number;
supportsFastUpdate?: boolean;
}
function updateBuild(commit: string, quality: string, platform: string, type: string, asset: Asset): Promise<void> {
const client = new DocumentClient(process.env['AZURE_DOCUMENTDB_ENDPOINT']!, { masterKey: process.env['AZURE_DOCUMENTDB_MASTERKEY'] });
const collection = 'dbs/builds/colls/' + quality;
const updateQuery = {
query: 'SELECT TOP 1 * FROM c WHERE c.id = @id',
parameters: [{ name: '@id', value: commit }]
};
let updateTries = 0;
function _update(): Promise<void> {
updateTries++;
return new Promise<void>((c, e) => {
client.queryDocuments(collection, updateQuery).toArray((err, results) => {
if (err) { return e(err); }
if (results.length !== 1) { return e(new Error('No documents')); }
const release = results[0];
release.assets = [
...release.assets.filter((a: any) => !(a.platform === platform && a.type === type)),
asset
];
client.replaceDocument(release._self, release, err => {
if (err && err.code === 409 && updateTries < 5) { return c(_update()); }
if (err) { return e(err); }
log('Build successfully updated.');
c();
});
});
});
}
return _update();
}
async function sync(commit: string, quality: string): Promise<void> {
log(`Synchronizing Mooncake assets for ${quality}, ${commit}...`);
const cosmosdb = new DocumentClient(process.env['AZURE_DOCUMENTDB_ENDPOINT']!, { masterKey: process.env['AZURE_DOCUMENTDB_MASTERKEY'] });
const collection = `dbs/builds/colls/${quality}`;
const query = {
query: 'SELECT TOP 1 * FROM c WHERE c.id = @id',
parameters: [{ name: '@id', value: commit }]
};
const build = await new Promise<Build>((c, e) => {
cosmosdb.queryDocuments(collection, query).toArray((err, results) => {
if (err) { return e(err); }
if (results.length !== 1) { return e(new Error('No documents')); }
c(results[0] as Build);
});
});
log(`Found build for ${commit}, with ${build.assets.length} assets`);
const storageAccount = process.env['AZURE_STORAGE_ACCOUNT_2']!;
const blobService = azure.createBlobService(storageAccount, process.env['AZURE_STORAGE_ACCESS_KEY_2']!)
.withFilter(new azure.ExponentialRetryPolicyFilter(20));
const mooncakeBlobService = azure.createBlobService(storageAccount, process.env['MOONCAKE_STORAGE_ACCESS_KEY']!, `${storageAccount}.blob.core.chinacloudapi.cn`)
.withFilter(new azure.ExponentialRetryPolicyFilter(20));
// mooncake is fussy and far away, this is needed!
blobService.defaultClientRequestTimeoutInMs = 10 * 60 * 1000;
mooncakeBlobService.defaultClientRequestTimeoutInMs = 10 * 60 * 1000;
for (const asset of build.assets) {
try {
const blobPath = url.parse(asset.url).path;
if (!blobPath) {
throw new Error(`Failed to parse URL: ${asset.url}`);
}
const blobName = blobPath.replace(/^\/\w+\//, '');
log(`Found ${blobName}`);
if (asset.mooncakeUrl) {
log(` Already in Mooncake ✔️`);
continue;
}
const readStream = blobService.createReadStream(quality, blobName, undefined!);
const blobOptions: azure.BlobService.CreateBlockBlobRequestOptions = {
contentSettings: {
contentType: mime.lookup(blobPath),
cacheControl: 'max-age=31536000, public'
}
};
const writeStream = mooncakeBlobService.createWriteStreamToBlockBlob(quality, blobName, blobOptions, undefined);
log(` Uploading to Mooncake...`);
await new Promise((c, e) => readStream.pipe(writeStream).on('finish', c).on('error', e));
log(` Updating build in DB...`);
asset.mooncakeUrl = `${process.env['MOONCAKE_CDN_URL']}${blobPath}`;
await updateBuild(commit, quality, asset.platform, asset.type, asset);
log(` Done ✔️`);
} catch (err) {
error(err);
}
}
log(`All done ✔️`);
}
function main(): void {
if (process.env['VSCODE_BUILD_SKIP_PUBLISH']) {
error('Skipping publish due to VSCODE_BUILD_SKIP_PUBLISH');
return;
}
const commit = process.env['BUILD_SOURCEVERSION'];
if (!commit) {
error('Skipping publish due to missing BUILD_SOURCEVERSION');
return;
}
const quality = process.argv[2];
sync(commit, quality).catch(err => {
error(err);
process.exit(1);
});
}
main();

View File

@@ -78,7 +78,6 @@ steps:
VERSION=`node -p "require(\"$PACKAGEJSON\").version"`
AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \
AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \
MOONCAKE_STORAGE_ACCESS_KEY="$(MOONCAKE_STORAGE_ACCESS_KEY)" \
node build/azure-pipelines/common/publish.js \
"$(VSCODE_QUALITY)" \
darwin \

View File

@@ -0,0 +1,29 @@
trigger:
branches:
include: ['master', 'release/*']
pr:
branches:
include: ['master', 'release/*']
steps:
- task: NodeTool@0
inputs:
versionSpec: "10.15.1"
- script: |
set -e
cat << EOF > ~/.netrc
machine github.com
login vscode
password $(VSCODE_MIXIN_PASSWORD)
EOF
git config user.email "vscode@microsoft.com"
git config user.name "VSCode"
git remote add distro "https://github.com/$VSCODE_MIXIN_REPO.git"
git fetch distro
git merge $(node -p "require('./package.json').distro")
displayName: Merge Distro

View File

@@ -68,7 +68,6 @@ steps:
AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \
AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \
MOONCAKE_STORAGE_ACCESS_KEY="$(MOONCAKE_STORAGE_ACCESS_KEY)" \
node build/azure-pipelines/common/publish.js "$VSCODE_QUALITY" "$PLATFORM_LINUX" archive-unsigned "$TARBALL_FILENAME" "$VERSION" true "$TARBALL_PATH"
# Publish hockeyapp symbols
@@ -83,7 +82,6 @@ steps:
AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \
AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \
MOONCAKE_STORAGE_ACCESS_KEY="$(MOONCAKE_STORAGE_ACCESS_KEY)" \
node build/azure-pipelines/common/publish.js "$VSCODE_QUALITY" "$PLATFORM_DEB" package "$DEB_FILENAME" "$VERSION" true "$DEB_PATH"
# Publish RPM
@@ -95,7 +93,6 @@ steps:
AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \
AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \
MOONCAKE_STORAGE_ACCESS_KEY="$(MOONCAKE_STORAGE_ACCESS_KEY)" \
node build/azure-pipelines/common/publish.js "$VSCODE_QUALITY" "$PLATFORM_RPM" package "$RPM_FILENAME" "$VERSION" true "$RPM_PATH"
# Publish Snap

View File

@@ -46,5 +46,4 @@ steps:
# Publish snap package
AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \
AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \
MOONCAKE_STORAGE_ACCESS_KEY="$(MOONCAKE_STORAGE_ACCESS_KEY)" \
node build/azure-pipelines/common/publish.js "$VSCODE_QUALITY" "linux-snap-$ARCH" package "$SNAP_FILENAME" "$VERSION" true "$SNAP_PATH"

View File

@@ -62,4 +62,18 @@ jobs:
pool:
vmImage: macOS 10.13
steps:
- template: darwin/product-build-darwin.yml
- template: darwin/product-build-darwin.yml
- job: Mooncake
pool:
vmImage: 'Ubuntu-16.04'
condition: true
dependsOn:
- Windows
- Windows32
- Linux
- LinuxSnap
- Linux32
- macOS
steps:
- template: sync-mooncake.yml

View File

@@ -0,0 +1,18 @@
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"
- script: |
set -e
(cd build ; yarn)
AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \
AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \
MOONCAKE_STORAGE_ACCESS_KEY="$(MOONCAKE_STORAGE_ACCESS_KEY)" \
node build/azure-pipelines/common/sync-mooncake.js "$VSCODE_QUALITY"

View File

@@ -133,7 +133,6 @@ steps:
$Version = $PackageJson.version
$Quality = "$env:VSCODE_QUALITY"
$env:AZURE_STORAGE_ACCESS_KEY_2 = "$(AZURE_STORAGE_ACCESS_KEY_2)"
$env:MOONCAKE_STORAGE_ACCESS_KEY = "$(MOONCAKE_STORAGE_ACCESS_KEY)"
$env:AZURE_DOCUMENTDB_MASTERKEY = "$(AZURE_DOCUMENTDB_MASTERKEY)"
$assetPlatform = if ("$(VSCODE_ARCH)" -eq "ia32") { "win32" } else { "win32-x64" }

View File

@@ -87,6 +87,7 @@ const indentationFilter = [
'!build/azure-pipelines/**/*.js',
'!build/azure-pipelines/**/*.config',
'!**/Dockerfile',
'!**/Dockerfile.*',
'!**/*.Dockerfile',
'!**/*.dockerfile',
'!extensions/markdown-language-features/media/*.js'

View File

@@ -89,6 +89,8 @@ const vscodeResources = [
'out-build/vs/workbench/contrib/welcome/walkThrough/**/*.md',
'out-build/vs/workbench/services/files/**/*.exe',
'out-build/vs/workbench/services/files/**/*.md',
'out-build/vs/workbench/services/files2/**/*.exe',
'out-build/vs/workbench/services/files2/**/*.md',
'out-build/vs/code/electron-browser/workbench/**',
'out-build/vs/code/electron-browser/sharedProcess/sharedProcess.js',
'out-build/vs/code/electron-browser/issue/issueReporter.js',
@@ -425,6 +427,8 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op
result = es.merge(result, gulp.src('resources/win32/bin/code.sh', { base: 'resources/win32' })
.pipe(replace('@@NAME@@', product.nameShort))
.pipe(replace('@@PRODNAME@@', product.nameLong))
.pipe(replace('@@VERSION@@', version))
.pipe(replace('@@COMMIT@@', commit))
.pipe(replace('@@APPNAME@@', product.applicationName))
.pipe(rename(function (f) { f.basename = product.applicationName; f.extname = ''; })));
@@ -433,6 +437,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op
.pipe(rename(product.nameShort + '.VisualElementsManifest.xml')));
} else if (platform === 'linux') {
result = es.merge(result, gulp.src('resources/linux/bin/code.sh', { base: '.' })
.pipe(replace('@@PRODNAME@@', product.nameLong))
.pipe(replace('@@NAME@@', product.applicationName))
.pipe(rename('bin/' + product.applicationName)));
}

View File

@@ -24,7 +24,7 @@ function log(message: any, ...rest: any[]): void {
}
export interface Language {
id: string; // laguage id, e.g. zh-tw, de
id: string; // language id, e.g. zh-tw, de
translationId?: string; // language id used in translation tools, e.g zh-hant, de (optional, if not set, the id is used)
folderName?: string; // language specific folder name, e.g. cht, deu (optional, if not set, the id is used)
}
@@ -1378,4 +1378,4 @@ function decodeEntities(value: string): string {
function pseudify(message: string) {
return '\uFF3B' + message.replace(/[aouei]/g, '$&$&') + '\uFF3D';
}
}

View File

@@ -32,7 +32,7 @@ async function _doExecute(task) {
// Always invoke as if it were a callback task
return new Promise((resolve, reject) => {
if (task.length === 1) {
// this is a calback task
// this is a callback task
task((err) => {
if (err) {
return reject(err);

View File

@@ -54,7 +54,7 @@ async function _doExecute(task: Task): Promise<void> {
// Always invoke as if it were a callback task
return new Promise((resolve, reject) => {
if (task.length === 1) {
// this is a calback task
// this is a callback task
task((err) => {
if (err) {
return reject(err);

View File

@@ -74,7 +74,7 @@ function update(options) {
let translationPaths = [];
i18n.pullI18nPackFiles(server, userName, apiToken, { id: languageId }, translationPaths)
.on('error', (error) => {
console.log(`Error occured while importing translations:`);
console.log(`Error occurred while importing translations:`);
translationPaths = undefined;
if (Array.isArray(error)) {
error.forEach(console.log);
@@ -100,7 +100,7 @@ function update(options) {
gulp.src(path.join(location, languageId, '**', '*.xlf'))
.pipe(i18n.prepareI18nPackFiles(i18n.externalExtensionsWithTranslations, translationPaths, languageId === 'ps'))
.on('error', (error) => {
console.log(`Error occured while importing translations:`);
console.log(`Error occurred while importing translations:`);
translationPaths = undefined;
if (Array.isArray(error)) {
error.forEach(console.log);

View File

@@ -8,7 +8,7 @@
"resolveJsonModule": true,
"experimentalDecorators": true,
// enable JavaScript type checking for the language service
// use the tsconfig.build.json for compiling wich disable JavaScript
// use the tsconfig.build.json for compiling which disable JavaScript
// type checking so that JavaScript file are not transpiled
"allowJs": true,
"checkJs": true,
@@ -22,4 +22,4 @@
"exclude": [
"node_modules/**"
]
}
}

View File

@@ -57,6 +57,10 @@
"fileMatch": "%APP_SETTINGS_HOME%/settings.json",
"url": "vscode://schemas/settings/user"
},
{
"fileMatch": "%MACHINE_SETTINGS_HOME%/settings.json",
"url": "vscode://schemas/settings/machine"
},
{
"fileMatch": "%APP_WORKSPACES_HOME%/*/workspace.json",
"url": "vscode://schemas/workspaceConfig"

View File

@@ -265,6 +265,7 @@ function getSchemaAssociation(_context: ExtensionContext): ISchemaAssociations {
}
if (fileMatch[0] === '%') {
fileMatch = fileMatch.replace(/%APP_SETTINGS_HOME%/, '/User');
fileMatch = fileMatch.replace(/%MACHINE_SETTINGS_HOME%/, '/Machine');
fileMatch = fileMatch.replace(/%APP_WORKSPACES_HOME%/, '/Workspaces');
} else if (fileMatch.charAt(0) !== '/' && !fileMatch.match(/\w+:\/\//)) {
fileMatch = '/' + fileMatch;

View File

@@ -101,21 +101,19 @@ export class MarkdownContentProvider {
return href;
}
// Use href if it is already an URL
const hrefUri = vscode.Uri.parse(href);
if (['http', 'https'].indexOf(hrefUri.scheme) >= 0) {
return hrefUri.toString(true);
if (href.startsWith('http:') || href.startsWith('https:') || href.startsWith('file:')) {
return href;
}
// Use href as file URI if it is absolute
if (path.isAbsolute(href) || hrefUri.scheme === 'file') {
// Assume it must be a local file
if (path.isAbsolute(href)) {
return vscode.Uri.file(href)
.with({ scheme: 'vscode-resource' })
.toString();
}
// Use a workspace relative path if there is a workspace
let root = vscode.workspace.getWorkspaceFolder(resource);
const root = vscode.workspace.getWorkspaceFolder(resource);
if (root) {
return vscode.Uri.file(path.join(root.uri.fsPath, href))
.with({ scheme: 'vscode-resource' })

View File

@@ -3,7 +3,7 @@
"version": "0.0.1",
"description": "Dependencies shared by all extensions",
"dependencies": {
"typescript": "3.4.1"
"typescript": "3.4.3-insiders.20190408"
},
"scripts": {
"postinstall": "node ./postinstall"

View File

@@ -2,7 +2,7 @@
# yarn lockfile v1
typescript@3.4.1:
version "3.4.1"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.4.1.tgz#b6691be11a881ffa9a05765a205cb7383f3b63c6"
integrity sha512-3NSMb2VzDQm8oBTLH6Nj55VVtUEpe/rgkIzMir0qVoLyjDZlnMBva0U6vDiV3IH+sl/Yu6oP5QwsAQtHPmDd2Q==
typescript@3.4.3-insiders.20190408:
version "3.4.3-insiders.20190408"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.4.3-insiders.20190408.tgz#18d98336c693a13dc8b2d5f39b70268c018c650b"
integrity sha512-5SI6EA+2u0ea/Uy0qCEczh8vBR0ByVaCFCyU0RdROROw8V5O4OIQHMFcnIdyg+nnfRGYp39PxvllGMDpsTFOOQ==

View File

@@ -11,18 +11,18 @@ set VSCODEUSERDATADIR=%TMP%\vscodeuserfolder-%RANDOM%-%TIME:~6,5%
:: if %errorlevel% neq 0 exit /b %errorlevel%
:: Tests in the extension host
call .\scripts\code.bat %~dp0\..\extensions\vscode-api-tests\testWorkspace --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\singlefolder-tests --disableExtensions --user-data-dir=%VSCODEUSERDATADIR%
call .\scripts\code.bat %~dp0\..\extensions\vscode-api-tests\testWorkspace --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\singlefolder-tests --disable-extensions --user-data-dir=%VSCODEUSERDATADIR%
if %errorlevel% neq 0 exit /b %errorlevel%
call .\scripts\code.bat %~dp0\..\extensions\vscode-api-tests\testworkspace.code-workspace --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\workspace-tests --disableExtensions --user-data-dir=%VSCODEUSERDATADIR%
call .\scripts\code.bat %~dp0\..\extensions\vscode-api-tests\testworkspace.code-workspace --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\workspace-tests --disable-extensions --user-data-dir=%VSCODEUSERDATADIR%
if %errorlevel% neq 0 exit /b %errorlevel%
call .\scripts\code.bat %~dp0\..\extensions\vscode-colorize-tests\test --extensionDevelopmentPath=%~dp0\..\extensions\vscode-colorize-tests --extensionTestsPath=%~dp0\..\extensions\vscode-colorize-tests\out --disableExtensions --user-data-dir=%VSCODEUSERDATADIR%
call .\scripts\code.bat %~dp0\..\extensions\vscode-colorize-tests\test --extensionDevelopmentPath=%~dp0\..\extensions\vscode-colorize-tests --extensionTestsPath=%~dp0\..\extensions\vscode-colorize-tests\out --disable-extensions --user-data-dir=%VSCODEUSERDATADIR%
call .\scripts\code.bat %~dp0\..\extensions\markdown-language-features\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\markdown-language-features --extensionTestsPath=%~dp0\..\extensions\markdown-language-features\out\test --disableExtensions --user-data-dir=%VSCODEUSERDATADIR%
call .\scripts\code.bat %~dp0\..\extensions\azurecore\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\azurecore --extensionTestsPath=%~dp0\..\extensions\azurecore\out\test --disableExtensions --user-data-dir=%VSCODEUSERDATADIR%
if %errorlevel% neq 0 exit /b %errorlevel%
:: call .\scripts\code.bat $%~dp0\..\extensions\emmet\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\emmet --extensionTestsPath=%~dp0\..\extensions\emmet\out\test --disableExtensions --user-data-dir=%VSCODEUSERDATADIR% .
:: call .\scripts\code.bat $%~dp0\..\extensions\emmet\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\emmet --extensionTestsPath=%~dp0\..\extensions\emmet\out\test --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% .
:: if %errorlevel% neq 0 exit /b %errorlevel%
:: Integration & performance tests in AMD

View File

@@ -17,14 +17,15 @@ cd $ROOT
# Tests in the extension host
# TODO port over an re-enable API tests
# ./scripts/code.sh $ROOT/extensions/vscode-api-tests/testWorkspace --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out --disableExtensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started
./scripts/code.sh $ROOT/extensions/vscode-colorize-tests/test --extensionDevelopmentPath=$ROOT/extensions/vscode-colorize-tests --extensionTestsPath=$ROOT/extensions/vscode-colorize-tests/out --disableExtensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started
./scripts/code.sh $ROOT/extensions/markdown-language-features/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/markdown-language-features --extensionTestsPath=$ROOT/extensions/markdown-language-features/out/test --disableExtensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started
#./scripts/code.sh $ROOT/extensions/vscode-api-tests/testWorkspace --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/singlefolder-tests --disable-extensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started
#./scripts/code.sh $ROOT/extensions/vscode-api-tests/testworkspace.code-workspace --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/workspace-tests --disable-extensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started
./scripts/code.sh $ROOT/extensions/vscode-colorize-tests/test --extensionDevelopmentPath=$ROOT/extensions/vscode-colorize-tests --extensionTestsPath=$ROOT/extensions/vscode-colorize-tests/out --disable-extensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started
./scripts/code.sh $ROOT/extensions/markdown-language-features/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/markdown-language-features --extensionTestsPath=$ROOT/extensions/markdown-language-features/out/test --disable-extensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started
./scripts/code.sh $ROOT/extensions/azurecore/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/azurecore --extensionTestsPath=$ROOT/extensions/azurecore/out/test --disableExtensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started
mkdir $ROOT/extensions/emmet/test-fixtures
./scripts/code.sh $ROOT/extensions/emmet/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disableExtensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started .
rm -r $ROOT/extensions/emmet/test-fixtures
mkdir -p $ROOT/extensions/emmet/test-fixtures
./scripts/code.sh $ROOT/extensions/emmet/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disable-extensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started
rm -rf $ROOT/extensions/emmet/test-fixtures
# Tests in commonJS
cd $ROOT/extensions/css-language-features/server && $ROOT/scripts/node-electron.sh test/index.js

View File

@@ -146,6 +146,11 @@ function configureCommandlineSwitches(cliArgs, nodeCachedDataDir) {
if (jsFlags) {
app.commandLine.appendSwitch('--js-flags', jsFlags);
}
// Disable smooth scrolling for Webviews
if (cliArgs['disable-smooth-scrolling']) {
app.commandLine.appendSwitch('disable-smooth-scrolling');
}
}
/**

View File

@@ -5,7 +5,7 @@
'use strict';
import { nb, IConnectionProfile } from 'azdata';
import * as vsExtTypes from 'vs/workbench/api/node/extHostTypes';
import * as vsExtTypes from 'vs/workbench/api/common/extHostTypes';
// SQL added extension host types
export enum ServiceOptionType {

View File

@@ -6,7 +6,7 @@
'use strict';
import * as azdata from 'azdata';
import { Disposable } from 'vs/workbench/api/node/extHostTypes';
import { Disposable } from 'vs/workbench/api/common/extHostTypes';
import {
ExtHostAccountManagementShape,
MainThreadAccountManagementShape,

View File

@@ -8,7 +8,7 @@ import { IMainContext } from 'vs/workbench/api/common/extHost.protocol';
import { SqlMainContext, MainThreadCredentialManagementShape, ExtHostCredentialManagementShape } from 'sql/workbench/api/node/sqlExtHost.protocol';
import * as vscode from 'vscode';
import * as azdata from 'azdata';
import { Disposable } from 'vs/workbench/api/node/extHostTypes';
import { Disposable } from 'vs/workbench/api/common/extHostTypes';
class CredentialAdapter {
public provider: azdata.CredentialProvider;

View File

@@ -8,7 +8,7 @@ import * as vscode from 'vscode';
import * as azdata from 'azdata';
import { Event, Emitter } from 'vs/base/common/event';
import { IMainContext } from 'vs/workbench/api/common/extHost.protocol';
import { Disposable } from 'vs/workbench/api/node/extHostTypes';
import { Disposable } from 'vs/workbench/api/common/extHostTypes';
import { SqlMainContext, MainThreadDataProtocolShape, ExtHostDataProtocolShape } from 'sql/workbench/api/node/sqlExtHost.protocol';
import { DataProviderType } from 'sql/workbench/api/common/sqlExtHostTypes';

View File

@@ -8,10 +8,10 @@ import { localize } from 'vs/nls';
import * as vscode from 'vscode';
import { SqlMainContext, ExtHostModelViewTreeViewsShape, MainThreadModelViewShape } from 'sql/workbench/api/node/sqlExtHost.protocol';
import { ITreeComponentItem } from 'sql/workbench/common/views';
import { CommandsConverter } from 'vs/workbench/api/node/extHostCommands';
import { CommandsConverter } from 'vs/workbench/api/common/extHostCommands';
import { IMainContext } from 'vs/workbench/api/common/extHost.protocol';
import * as azdata from 'azdata';
import * as vsTreeExt from 'vs/workbench/api/node/extHostTreeViews';
import * as vsTreeExt from 'vs/workbench/api/common/extHostTreeViews';
import { Emitter } from 'vs/base/common/event';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';

View File

@@ -8,7 +8,7 @@ import * as azdata from 'azdata';
import * as vscode from 'vscode';
import { IMainContext } from 'vs/workbench/api/common/extHost.protocol';
import { Disposable } from 'vs/workbench/api/node/extHostTypes';
import { Disposable } from 'vs/workbench/api/common/extHostTypes';
import { localize } from 'vs/nls';
import { URI, UriComponents } from 'vs/base/common/uri';

View File

@@ -10,8 +10,8 @@ import * as vscode from 'vscode';
import { Event, Emitter } from 'vs/base/common/event';
import { dispose } from 'vs/base/common/lifecycle';
import { URI, UriComponents } from 'vs/base/common/uri';
import { Disposable } from 'vs/workbench/api/node/extHostTypes';
import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters';
import { Disposable } from 'vs/workbench/api/common/extHostTypes';
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
import { IMainContext } from 'vs/workbench/api/common/extHost.protocol';
import { ok } from 'vs/base/common/assert';

View File

@@ -7,7 +7,7 @@
import * as azdata from 'azdata';
import { IMainContext } from 'vs/workbench/api/common/extHost.protocol';
import { Disposable } from 'vs/workbench/api/node/extHostTypes';
import { Disposable } from 'vs/workbench/api/common/extHostTypes';
import {
ExtHostResourceProviderShape,
MainThreadResourceProviderShape,

View File

@@ -8,7 +8,7 @@ import { IMainContext } from 'vs/workbench/api/common/extHost.protocol';
import { SqlMainContext, MainThreadSerializationProviderShape, ExtHostSerializationProviderShape } from 'sql/workbench/api/node/sqlExtHost.protocol';
import * as vscode from 'vscode';
import * as azdata from 'azdata';
import { Disposable } from 'vs/workbench/api/node/extHostTypes';
import { Disposable } from 'vs/workbench/api/common/extHostTypes';
class SerializationAdapter {
private _provider: azdata.SerializationProvider;

View File

@@ -7,7 +7,7 @@
import { validateConstraint } from 'vs/base/common/types';
import { ILogService } from 'vs/platform/log/common/log';
import { IMainContext } from 'vs/workbench/api/common/extHost.protocol';
import * as extHostTypes from 'vs/workbench/api/node/extHostTypes';
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
import * as azdata from 'azdata';

View File

@@ -7,7 +7,6 @@
import * as extHostApi from 'vs/workbench/api/node/extHost.api.impl';
import { IInitData, IMainContext } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService';
import * as extHostTypes from 'vs/workbench/api/node/extHostTypes';
import { URI } from 'vs/base/common/uri';
import * as azdata from 'azdata';
@@ -20,8 +19,6 @@ import { ExtHostDataProtocol } from 'sql/workbench/api/node/extHostDataProtocol'
import { ExtHostSerializationProvider } from 'sql/workbench/api/node/extHostSerializationProvider';
import { ExtHostResourceProvider } from 'sql/workbench/api/node/extHostResourceProvider';
import * as sqlExtHostTypes from 'sql/workbench/api/common/sqlExtHostTypes';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import { ExtHostConfiguration, ExtHostConfigProvider } from 'vs/workbench/api/node/extHostConfiguration';
import { ExtHostModalDialogs } from 'sql/workbench/api/node/extHostModalDialog';
import { ExtHostTasks } from 'sql/workbench/api/node/extHostTasks';
import { ExtHostDashboardWebviews } from 'sql/workbench/api/node/extHostDashboardWebview';
@@ -29,18 +26,21 @@ import { ExtHostModelView } from 'sql/workbench/api/node/extHostModelView';
import { ExtHostConnectionManagement } from 'sql/workbench/api/node/extHostConnectionManagement';
import { ExtHostDashboard } from 'sql/workbench/api/node/extHostDashboard';
import { ExtHostObjectExplorer } from 'sql/workbench/api/node/extHostObjectExplorer';
import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService';
import { ExtHostLogService } from 'vs/workbench/api/common/extHostLogService';
import { ExtHostModelViewDialog } from 'sql/workbench/api/node/extHostModelViewDialog';
import { ExtHostModelViewTreeViews } from 'sql/workbench/api/node/extHostModelViewTree';
import { ExtHostQueryEditor } from 'sql/workbench/api/node/extHostQueryEditor';
import { ExtHostBackgroundTaskManagement } from './extHostBackgroundTaskManagement';
import { ExtHostNotebook } from 'sql/workbench/api/node/extHostNotebook';
import { ExtHostNotebookDocumentsAndEditors } from 'sql/workbench/api/node/extHostNotebookDocumentsAndEditors';
import { ExtHostStorage } from 'vs/workbench/api/node/extHostStorage';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
import { ExtHostExtensionManagement } from 'sql/workbench/api/node/extHostExtensionManagement';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { TernarySearchTree } from 'vs/base/common/map';
import { ExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
import { ExtHostConfiguration, ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration';
import { ExtHostStorage } from 'vs/workbench/api/common/extHostStorage';
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
export interface ISqlExtensionApiFactory {
vsCodeFactory(extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode;

View File

@@ -3,11 +3,10 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { equal } from 'assert';
import { equal, fail } from 'assert';
import * as os from 'os';
import { resolveQueryFilePath } from 'sql/workbench/services/insights/common/insightsUtils';
import { TestWindowService } from 'sqltest/stubs/windowTestService';
import * as path from 'vs/base/common/path';
import * as pfs from 'vs/base/node/pfs';
@@ -16,69 +15,129 @@ import { getRandomTestPath } from 'vs/base/test/node/testUtils';
import { Workspace, toWorkspaceFolders } from 'vs/platform/workspace/common/workspace';
import { ConfigurationResolverService } from 'vs/workbench/services/configurationResolver/browser/configurationResolverService';
import { TestContextService } from 'vs/workbench/test/workbenchTestServices';
import { IExtensionHostDebugParams, IDebugParams, ParsedArgs } from 'vs/platform/environment/common/environment';
import { URI } from 'vs/base/common/uri';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IWindowConfiguration } from 'vs/platform/windows/common/windows';
class TestEnvironmentService implements IWorkbenchEnvironmentService {
machineSettingsHome: string;
machineSettingsPath: string;
extensionDevelopmentLocationURI?: URI[];
constructor(private userEnv: { [key: string]: any }) {
}
get configuration(): IWindowConfiguration {
return {
userEnv: this.userEnv
} as IWindowConfiguration;
}
_serviceBrand: any;
args: ParsedArgs;
execPath: string;
cliPath: string;
appRoot: string;
userHome: string;
userDataPath: string;
appNameLong: string;
appQuality?: string;
appSettingsHome: string;
appSettingsPath: string;
appKeybindingsPath: string;
settingsSearchBuildId?: number;
settingsSearchUrl?: string;
globalStorageHome: string;
workspaceStorageHome: string;
backupHome: string;
backupWorkspacesPath: string;
untitledWorkspacesHome: URI;
isExtensionDevelopment: boolean;
disableExtensions: boolean | string[];
builtinExtensionsPath: string;
extensionsPath: string;
extensionTestsLocationURI?: URI;
debugExtensionHost: IExtensionHostDebugParams;
debugSearch: IDebugParams;
logExtensionHostCommunication: boolean;
isBuilt: boolean;
wait: boolean;
status: boolean;
log?: string;
logsPath: string;
verbose: boolean;
skipGettingStarted: boolean;
skipReleaseNotes: boolean;
skipAddToRecentlyOpened: boolean;
mainIPCHandle: string;
sharedIPCHandle: string;
nodeCachedDataDir?: string;
installSourcePath: string;
disableUpdates: boolean;
disableCrashReporter: boolean;
driverHandle?: string;
driverVerbose: boolean;
}
suite('Insights Utils tests', function () {
let testRootPath: string;
let queryFileDir: string;
let queryFilePath: string;
suiteSetup(done => {
suiteSetup(async () => {
// Create test file - just needs to exist for verifying the path resolution worked correctly
testRootPath = path.join(os.tmpdir(), 'adstests');
queryFileDir = getRandomTestPath(testRootPath, 'insightsutils');
pfs.mkdirp(queryFileDir).then(() => {
queryFilePath = path.join(queryFileDir, 'test.sql');
pfs.writeFile(queryFilePath, '').then(done());
});
await pfs.mkdirp(queryFileDir);
queryFilePath = path.join(queryFileDir, 'test.sql');
await pfs.writeFile(queryFilePath, '');
});
test('resolveQueryFilePath resolves path correctly with fully qualified path', async () => {
let configurationResolverService = new ConfigurationResolverService(
new TestWindowService({}),
undefined,
const configurationResolverService = new ConfigurationResolverService(
undefined,
new TestEnvironmentService({}),
undefined,
undefined,
new TestContextService(),
undefined);
let resolvedPath = await resolveQueryFilePath(queryFilePath, new TestContextService(), configurationResolverService);
const resolvedPath = await resolveQueryFilePath(queryFilePath, new TestContextService(), configurationResolverService);
equal(resolvedPath, queryFilePath);
});
test('resolveQueryFilePath resolves path correctly with workspaceRoot var and non-empty workspace containing file', async () => {
// Create mock context service with our test folder added as a workspace folder for resolution
let contextService = new TestContextService(
const contextService = new TestContextService(
new Workspace(
'TestWorkspace',
toWorkspaceFolders([{ path: queryFileDir }])
));
let configurationResolverService = new ConfigurationResolverService(
new TestWindowService({}),
undefined,
const configurationResolverService = new ConfigurationResolverService(
undefined,
new TestEnvironmentService({}),
undefined,
undefined,
contextService,
undefined);
let resolvedPath = await resolveQueryFilePath(path.join('${workspaceRoot}', 'test.sql'), contextService, configurationResolverService);
const resolvedPath = await resolveQueryFilePath(path.join('${workspaceRoot}', 'test.sql'), contextService, configurationResolverService);
equal(resolvedPath, queryFilePath);
});
test('resolveQueryFilePath throws with workspaceRoot var and non-empty workspace not containing file', async (done) => {
let tokenizedPath = path.join('${workspaceRoot}', 'test.sql');
const tokenizedPath = path.join('${workspaceRoot}', 'test.sql');
// Create mock context service with a folder NOT containing our test file to verify it returns original path
let contextService = new TestContextService(
const contextService = new TestContextService(
new Workspace(
'TestWorkspace',
toWorkspaceFolders([{ path: os.tmpdir() }])
));
let configurationResolverService = new ConfigurationResolverService(
new TestWindowService({}),
undefined,
const configurationResolverService = new ConfigurationResolverService(
undefined,
new TestEnvironmentService({}),
undefined,
undefined,
contextService,
@@ -86,6 +145,7 @@ suite('Insights Utils tests', function () {
try {
await resolveQueryFilePath(tokenizedPath, contextService, configurationResolverService);
fail('Should have thrown');
}
catch (e) {
done();
@@ -93,15 +153,14 @@ suite('Insights Utils tests', function () {
});
test('resolveQueryFilePath throws with workspaceRoot var and empty workspace', async (done) => {
let tokenizedPath = path.join('${workspaceRoot}', 'test.sql');
const tokenizedPath = path.join('${workspaceRoot}', 'test.sql');
// Create mock context service with an empty workspace
let contextService = new TestContextService(
const contextService = new TestContextService(
new Workspace(
'TestWorkspace'));
let configurationResolverService = new ConfigurationResolverService(
new TestWindowService({}),
undefined,
const configurationResolverService = new ConfigurationResolverService(
undefined,
new TestEnvironmentService({}),
undefined,
undefined,
contextService,
@@ -109,6 +168,7 @@ suite('Insights Utils tests', function () {
try {
await resolveQueryFilePath(tokenizedPath, contextService, configurationResolverService);
fail('Should have thrown');
}
catch (e) {
done();
@@ -116,47 +176,44 @@ suite('Insights Utils tests', function () {
});
test('resolveQueryFilePath resolves path correctly with env var and empty workspace', async () => {
let contextService = new TestContextService(
const contextService = new TestContextService(
new Workspace('TestWorkspace'));
// Create mock window service with env variable containing test folder for resolution
let configurationResolverService = new ConfigurationResolverService(
new TestWindowService({ TEST_PATH: queryFileDir }),
undefined,
const configurationResolverService = new ConfigurationResolverService(
undefined,
new TestEnvironmentService({ TEST_PATH: queryFileDir }),
undefined,
undefined,
undefined,
undefined);
let resolvedPath = await resolveQueryFilePath(path.join('${env:TEST_PATH}', 'test.sql'), contextService, configurationResolverService);
const resolvedPath = await resolveQueryFilePath(path.join('${env:TEST_PATH}', 'test.sql'), contextService, configurationResolverService);
equal(resolvedPath, queryFilePath);
});
test('resolveQueryFilePath resolves path correctly with env var and non-empty workspace', async () => {
let contextService = new TestContextService(
const contextService = new TestContextService(
new Workspace('TestWorkspace', toWorkspaceFolders([{ path: os.tmpdir() }])));
// Create mock window service with env variable containing test folder for resolution
let configurationResolverService = new ConfigurationResolverService(
new TestWindowService({ TEST_PATH: queryFileDir }),
undefined,
const configurationResolverService = new ConfigurationResolverService(
undefined,
new TestEnvironmentService({ TEST_PATH: queryFileDir }),
undefined,
undefined,
undefined,
undefined);
let resolvedPath = await resolveQueryFilePath(path.join('${env:TEST_PATH}', 'test.sql'), contextService, configurationResolverService);
const resolvedPath = await resolveQueryFilePath(path.join('${env:TEST_PATH}', 'test.sql'), contextService, configurationResolverService);
equal(resolvedPath, queryFilePath);
});
test('resolveQueryFilePath throws if invalid param var specified', async (done) => {
let invalidPath = path.join('${INVALID}', 'test.sql');
let configurationResolverService = new ConfigurationResolverService(
new TestWindowService({}),
undefined,
const invalidPath = path.join('${INVALID}', 'test.sql');
const configurationResolverService = new ConfigurationResolverService(
undefined,
new TestEnvironmentService({}),
undefined,
undefined,
undefined,
@@ -164,8 +221,8 @@ suite('Insights Utils tests', function () {
try {
await resolveQueryFilePath(invalidPath, new TestContextService(), configurationResolverService);
}
catch (e) {
fail('Should have thrown');
} catch (e) {
done();
}

View File

@@ -15,6 +15,7 @@ import { IDimension } from 'vs/editor/common/editorCommon';
import { IDisposable } from 'vs/base/common/lifecycle';
export class EditorGroupTestService implements IEditorGroupsService {
willRestoreEditors: boolean;
dimension: IDimension;
whenRestored: Promise<void>;
centerLayout(active: boolean): void {

View File

@@ -115,6 +115,13 @@ export class DragMouseEvent extends StandardMouseEvent {
export interface IMouseWheelEvent extends MouseEvent {
readonly wheelDelta: number;
readonly wheelDeltaX: number;
readonly wheelDeltaY: number;
readonly deltaX: number;
readonly deltaY: number;
readonly deltaZ: number;
readonly deltaMode: number;
}
interface IWebKitMouseWheelEvent {

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
declare var Buffer: any;
const hasBuffer = (typeof Buffer !== 'undefined');
export const hasBuffer = (typeof Buffer !== 'undefined');
let textEncoder: TextEncoder | null;
let textDecoder: TextDecoder | null;
@@ -20,6 +20,11 @@ export class VSBuffer {
}
public static wrap(actual: Uint8Array): VSBuffer {
if (hasBuffer && !(Buffer.isBuffer(actual))) {
// https://nodejs.org/dist/latest-v10.x/docs/api/buffer.html#buffer_class_method_buffer_from_arraybuffer_byteoffset_length
// Create a zero-copy Buffer wrapper around the ArrayBuffer pointed to by the Uint8Array
actual = Buffer.from(actual.buffer, actual.byteOffset, actual.byteLength);
}
return new VSBuffer(actual);
}
@@ -124,3 +129,44 @@ function readUint8(source: Uint8Array, offset: number): number {
function writeUint8(destination: Uint8Array, value: number, offset: number): void {
destination[offset] = value;
}
export interface VSBufferReadable {
/**
* Read data from the underlying source. Will return
* null to indicate that no more data can be read.
*/
read(): VSBuffer | null;
}
/**
* Helper to fully read a VSBuffer readable into a single buffer.
*/
export function readableToBuffer(readable: VSBufferReadable): VSBuffer {
const chunks: VSBuffer[] = [];
let chunk: VSBuffer | null;
while (chunk = readable.read()) {
chunks.push(chunk);
}
return VSBuffer.concat(chunks);
}
/**
* Helper to convert a buffer into a readable buffer.
*/
export function bufferToReadable(buffer: VSBuffer): VSBufferReadable {
let done = false;
return {
read: () => {
if (done) {
return null;
}
done = true;
return buffer;
}
};
}

View File

@@ -265,33 +265,6 @@ export namespace Event {
return emitter.event;
}
/**
* Similar to `buffer` but it buffers indefinitely and repeats
* the buffered events to every new listener.
*/
export function echo<T>(event: Event<T>, nextTick = false, buffer: T[] = []): Event<T> {
buffer = buffer.slice();
event(e => {
buffer.push(e);
emitter.fire(e);
});
const flush = (listener: (e: T) => any, thisArgs?: any) => buffer.forEach(e => listener.call(thisArgs, e));
const emitter = new Emitter<T>({
onListenerDidAdd(emitter: Emitter<T>, listener: (e: T) => any, thisArgs?: any) {
if (nextTick) {
setTimeout(() => flush(listener, thisArgs));
} else {
flush(listener, thisArgs);
}
}
});
return emitter.event;
}
export interface IChainableEvent<T> {
event: Event<T>;
map<O>(fn: (i: T) => O): IChainableEvent<O>;

View File

@@ -44,4 +44,6 @@ export namespace Schemas {
export const data: string = 'data';
export const command: string = 'command';
export const vscodeRemote: string = 'vscode-remote';
}

View File

@@ -284,7 +284,6 @@ export namespace DataUri {
}
}
export class ResourceGlobMatcher {
private readonly globalExpression: ParsedExpression;
@@ -311,3 +310,16 @@ export class ResourceGlobMatcher {
return !!this.globalExpression(resource.path);
}
}
export function toLocalResource(resource: URI, authority: string | undefined): URI {
if (authority) {
let path = resource.path;
if (path && path[0] !== paths.posix.sep) {
path = paths.posix.sep + path;
}
return resource.with({ scheme: Schemas.vscodeRemote, authority, path });
}
return resource.with({ scheme: Schemas.file });
}

View File

@@ -0,0 +1,20 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { hasBuffer, VSBuffer } from 'vs/base/common/buffer';
suite('Buffer', () => {
if (hasBuffer) {
test('issue #71993 - VSBuffer#toString returns numbers', () => {
const data = new Uint8Array([1, 2, 3, 'h'.charCodeAt(0), 'i'.charCodeAt(0), 4, 5]).buffer;
const buffer = VSBuffer.wrap(new Uint8Array(data, 3, 2));
assert.deepEqual(buffer.toString(), 'hi');
});
}
});

View File

@@ -645,67 +645,6 @@ suite('Event utils', () => {
});
});
suite('echo', () => {
test('should echo events', () => {
const result: number[] = [];
const emitter = new Emitter<number>();
const event = emitter.event;
const echoEvent = Event.echo(event);
emitter.fire(1);
emitter.fire(2);
emitter.fire(3);
assert.deepEqual(result, []);
const listener = echoEvent(num => result.push(num));
assert.deepEqual(result, [1, 2, 3]);
emitter.fire(4);
assert.deepEqual(result, [1, 2, 3, 4]);
listener.dispose();
emitter.fire(5);
assert.deepEqual(result, [1, 2, 3, 4]);
});
test('should echo events for every listener', () => {
const result1: number[] = [];
const result2: number[] = [];
const emitter = new Emitter<number>();
const event = emitter.event;
const echoEvent = Event.echo(event);
emitter.fire(1);
emitter.fire(2);
emitter.fire(3);
assert.deepEqual(result1, []);
assert.deepEqual(result2, []);
const listener1 = echoEvent(num => result1.push(num));
assert.deepEqual(result1, [1, 2, 3]);
assert.deepEqual(result2, []);
emitter.fire(4);
assert.deepEqual(result1, [1, 2, 3, 4]);
assert.deepEqual(result2, []);
const listener2 = echoEvent(num => result2.push(num));
assert.deepEqual(result1, [1, 2, 3, 4]);
assert.deepEqual(result2, [1, 2, 3, 4]);
emitter.fire(5);
assert.deepEqual(result1, [1, 2, 3, 4, 5]);
assert.deepEqual(result2, [1, 2, 3, 4, 5]);
listener1.dispose();
listener2.dispose();
emitter.fire(6);
assert.deepEqual(result1, [1, 2, 3, 4, 5]);
assert.deepEqual(result2, [1, 2, 3, 4, 5]);
});
});
suite('EventMultiplexer', () => {
test('works', () => {

View File

@@ -24,6 +24,10 @@ td {
vertical-align: top;
}
tr td:first-child {
width: 30%;
}
label {
user-select: none;
}

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="#646465" d="M6 4v8l4-4-4-4zm1 2.414L8.586 8 7 9.586V6.414z"/></svg>

After

Width:  |  Height:  |  Size: 139 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="#646465" d="M11 10H5.344L11 4.414V10z"/></svg>

After

Width:  |  Height:  |  Size: 118 B

View File

@@ -85,6 +85,20 @@ td {
height: 22px;
}
.error {
padding-left: 20px;
white-space: nowrap;
}
tbody > tr:hover {
background-color: #2A2D2E;
}
.hidden {
display: none;
}
img {
width: 16px;
margin-right: 4px;
}

View File

@@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/processExplorer';
import { listProcesses } from 'vs/base/node/ps';
import { webFrame, ipcRenderer, clipboard } from 'electron';
import { repeat } from 'vs/base/common/strings';
import { totalmem } from 'os';
@@ -16,31 +15,46 @@ import * as platform from 'vs/base/common/platform';
import { IContextMenuItem } from 'vs/base/parts/contextmenu/common/contextmenu';
import { popup } from 'vs/base/parts/contextmenu/electron-browser/contextmenu';
import { ProcessItem } from 'vs/base/common/processes';
import { addDisposableListener } from 'vs/base/browser/dom';
import { IDisposable } from 'vs/base/common/lifecycle';
import { isRemoteDiagnosticError, IRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnosticsService';
let processList: any[];
let mapPidToWindowTitle = new Map<number, string>();
const DEBUG_FLAGS_PATTERN = /\s--(inspect|debug)(-brk|port)?=(\d+)?/;
const DEBUG_PORT_PATTERN = /\s--(inspect|debug)-port=(\d+)/;
const listeners: IDisposable[] = [];
const collapsedStateCache: Map<string, boolean> = new Map<string, boolean>();
let lastRequestTime: number;
function getProcessList(rootProcess: ProcessItem) {
const processes: any[] = [];
interface FormattedProcessItem {
cpu: string;
memory: string;
pid: string;
name: string;
formattedName: string;
cmd: string;
}
function getProcessList(rootProcess: ProcessItem, isLocal: boolean): FormattedProcessItem[] {
const processes: FormattedProcessItem[] = [];
if (rootProcess) {
getProcessItem(processes, rootProcess, 0);
getProcessItem(processes, rootProcess, 0, isLocal);
}
return processes;
}
function getProcessItem(processes: any[], item: ProcessItem, indent: number): void {
function getProcessItem(processes: FormattedProcessItem[], item: ProcessItem, indent: number, isLocal: boolean): void {
const isRoot = (indent === 0);
const MB = 1024 * 1024;
let name = item.name;
if (isRoot) {
name = `${product.applicationName} main`;
name = isLocal ? `${product.applicationName} main` : 'remote agent';
}
if (name === 'window') {
@@ -52,9 +66,9 @@ function getProcessItem(processes: any[], item: ProcessItem, indent: number): vo
const formattedName = isRoot ? name : `${repeat(' ', indent)} ${name}`;
const memory = process.platform === 'win32' ? item.mem : (totalmem() * (item.mem / 100));
processes.push({
cpu: Number(item.load.toFixed(0)),
memory: Number((memory / MB).toFixed(0)),
pid: Number((item.pid).toFixed(0)),
cpu: item.load.toFixed(0),
memory: (memory / MB).toFixed(0),
pid: item.pid.toFixed(0),
name,
formattedName,
cmd: item.cmd
@@ -62,7 +76,7 @@ function getProcessItem(processes: any[], item: ProcessItem, indent: number): vo
// Recurse into children if any
if (Array.isArray(item.children)) {
item.children.forEach(child => getProcessItem(processes, child, indent + 1));
item.children.forEach(child => getProcessItem(processes, child, indent + 1, isLocal));
}
}
@@ -71,7 +85,7 @@ function isDebuggable(cmd: string): boolean {
return (matches && matches.length >= 2) || cmd.indexOf('node ') >= 0 || cmd.indexOf('node.exe') >= 0;
}
function attachTo(item: ProcessItem) {
function attachTo(item: FormattedProcessItem) {
const config: any = {
type: 'node',
request: 'attach',
@@ -113,35 +127,82 @@ function getProcessIdWithHighestProperty(processList: any[], propertyName: strin
return maxProcessId;
}
function updateProcessInfo(processList: any[]): void {
function updateSectionCollapsedState(shouldExpand: boolean, body: HTMLElement, twistie: HTMLImageElement, sectionName: string) {
if (shouldExpand) {
body.classList.remove('hidden');
collapsedStateCache.set(sectionName, false);
twistie.src = './media/expanded.svg';
} else {
body.classList.add('hidden');
collapsedStateCache.set(sectionName, true);
twistie.src = './media/collapsed.svg';
}
}
function renderProcessFetchError(sectionName: string, errorMessage: string) {
const container = document.getElementById('process-list');
if (!container) {
return;
}
const body = document.createElement('tbody');
renderProcessGroupHeader(sectionName, body, container);
const errorRow = document.createElement('tr');
const data = document.createElement('td');
data.textContent = errorMessage;
data.className = 'error';
data.colSpan = 4;
errorRow.appendChild(data);
body.appendChild(errorRow);
container.appendChild(body);
}
function renderProcessGroupHeader(sectionName: string, body: HTMLElement, container: HTMLElement) {
const headerRow = document.createElement('tr');
const data = document.createElement('td');
data.textContent = sectionName;
data.colSpan = 4;
headerRow.appendChild(data);
const twistie = document.createElement('img');
updateSectionCollapsedState(!collapsedStateCache.get(sectionName), body, twistie, sectionName);
data.prepend(twistie);
listeners.push(addDisposableListener(data, 'click', (e) => {
const isHidden = body.classList.contains('hidden');
updateSectionCollapsedState(isHidden, body, twistie, sectionName);
}));
container.appendChild(headerRow);
}
function renderTableSection(sectionName: string, processList: FormattedProcessItem[], renderManySections: boolean, sectionIsLocal: boolean): void {
const container = document.getElementById('process-list');
if (!container) {
return;
}
container.innerHTML = '';
const highestCPUProcess = getProcessIdWithHighestProperty(processList, 'cpu');
const highestMemoryProcess = getProcessIdWithHighestProperty(processList, 'memory');
const tableHead = document.createElement('thead');
tableHead.innerHTML = `<tr>
<th scope="col" class="cpu">${localize('cpu', "CPU %")}</th>
<th scope="col" class="memory">${localize('memory', "Memory (MB)")}</th>
<th scope="col" class="pid">${localize('pid', "pid")}</th>
<th scope="col" class="nameLabel">${localize('name', "Name")}</th>
</tr>`;
const body = document.createElement('tbody');
const tableBody = document.createElement('tbody');
if (renderManySections) {
renderProcessGroupHeader(sectionName, body, container);
}
processList.forEach(p => {
const row = document.createElement('tr');
row.id = p.pid;
row.id = p.pid.toString();
const cpu = document.createElement('td');
p.pid === highestCPUProcess
? cpu.classList.add('centered', 'highest')
: cpu.classList.add('centered');
cpu.textContent = p.cpu;
cpu.textContent = p.cpu.toString();
const memory = document.createElement('td');
p.pid === highestMemoryProcess
@@ -160,10 +221,45 @@ function updateProcessInfo(processList: any[]): void {
name.textContent = p.formattedName;
row.append(cpu, memory, pid, name);
tableBody.appendChild(row);
listeners.push(addDisposableListener(row, 'contextmenu', (e) => {
showContextMenu(e, p, sectionIsLocal);
}));
body.appendChild(row);
});
container.append(tableHead, tableBody);
container.appendChild(body);
}
function updateProcessInfo(processLists: [{ name: string, rootProcess: ProcessItem | IRemoteDiagnosticError }]): void {
const container = document.getElementById('process-list');
if (!container) {
return;
}
container.innerHTML = '';
listeners.forEach(l => l.dispose());
const tableHead = document.createElement('thead');
tableHead.innerHTML = `<tr>
<th scope="col" class="cpu">${localize('cpu', "CPU %")}</th>
<th scope="col" class="memory">${localize('memory', "Memory (MB)")}</th>
<th scope="col" class="pid">${localize('pid', "pid")}</th>
<th scope="col" class="nameLabel">${localize('name', "Name")}</th>
</tr>`;
container.append(tableHead);
const hasMultipleMachines = Object.keys(processLists).length > 1;
processLists.forEach((remote, i) => {
const isLocal = i === 0;
if (isRemoteDiagnosticError(remote.rootProcess)) {
renderProcessFetchError(remote.name, remote.rootProcess.errorMessage);
} else {
renderTableSection(remote.name, getProcessList(remote.rootProcess, isLocal), hasMultipleMachines, isLocal);
}
});
}
function applyStyles(styles: ProcessExplorerStyles): void {
@@ -171,11 +267,11 @@ function applyStyles(styles: ProcessExplorerStyles): void {
const content: string[] = [];
if (styles.hoverBackground) {
content.push(`tbody > tr:hover { background-color: ${styles.hoverBackground}; }`);
content.push(`tbody > tr:hover, table > tr:hover { background-color: ${styles.hoverBackground}; }`);
}
if (styles.hoverForeground) {
content.push(`tbody > tr:hover{ color: ${styles.hoverForeground}; }`);
content.push(`tbody > tr:hover, table > tr:hover { color: ${styles.hoverForeground}; }`);
}
if (styles.highlightForeground) {
@@ -200,13 +296,13 @@ function applyZoom(zoomLevel: number): void {
browser.setZoomLevel(webFrame.getZoomLevel(), /*isTrusted*/false);
}
function showContextMenu(e: MouseEvent) {
function showContextMenu(e: MouseEvent, item: FormattedProcessItem, isLocal: boolean) {
e.preventDefault();
const items: IContextMenuItem[] = [];
const pid = Number(item.pid);
const pid = parseInt((e.currentTarget as HTMLElement).id);
if (pid && typeof pid === 'number') {
if (isLocal) {
items.push({
label: localize('killProcess', "Kill Process"),
click() {
@@ -224,48 +320,37 @@ function showContextMenu(e: MouseEvent) {
items.push({
type: 'separator'
});
}
items.push({
label: localize('copy', "Copy"),
click() {
const row = document.getElementById(pid.toString());
if (row) {
clipboard.writeText(row.innerText);
}
items.push({
label: localize('copy', "Copy"),
click() {
const row = document.getElementById(pid.toString());
if (row) {
clipboard.writeText(row.innerText);
}
});
items.push({
label: localize('copyAll', "Copy All"),
click() {
const processList = document.getElementById('process-list');
if (processList) {
clipboard.writeText(processList.innerText);
}
}
});
const item = processList.filter(process => process.pid === pid)[0];
if (item && isDebuggable(item.cmd)) {
items.push({
type: 'separator'
});
items.push({
label: localize('debug', "Debug"),
click() {
attachTo(item);
}
});
}
} else {
});
items.push({
label: localize('copyAll', "Copy All"),
click() {
const processList = document.getElementById('process-list');
if (processList) {
clipboard.writeText(processList.innerText);
}
}
});
if (item && isLocal && isDebuggable(item.cmd)) {
items.push({
label: localize('copyAll', "Copy All"),
type: 'separator'
});
items.push({
label: localize('debug', "Debug"),
click() {
const processList = document.getElementById('process-list');
if (processList) {
clipboard.writeText(processList.innerText);
}
attachTo(item);
}
});
}
@@ -273,6 +358,22 @@ function showContextMenu(e: MouseEvent) {
popup(items);
}
function requestProcessList(totalWaitTime: number): void {
setTimeout(() => {
const nextRequestTime = Date.now();
const waited = totalWaitTime + nextRequestTime - lastRequestTime;
lastRequestTime = nextRequestTime;
// Wait at least a second between requests.
if (waited > 1000) {
ipcRenderer.send('windowsInfoRequest');
ipcRenderer.send('vscode:listProcesses');
} else {
requestProcessList(waited);
}
}, 200);
}
export function startup(data: ProcessExplorerData): void {
applyStyles(data.styles);
applyZoom(data.zoomLevel);
@@ -283,23 +384,14 @@ export function startup(data: ProcessExplorerData): void {
windows.forEach(window => mapPidToWindowTitle.set(window.pid, window.title));
});
setInterval(() => {
ipcRenderer.send('windowsInfoRequest');
listProcesses(data.pid).then(processes => {
processList = getProcessList(processes);
updateProcessInfo(processList);
const tableRows = document.getElementsByTagName('tr');
for (let i = 0; i < tableRows.length; i++) {
const tableRow = tableRows[i];
tableRow.addEventListener('contextmenu', (e) => {
showContextMenu(e);
});
}
});
}, 1200);
ipcRenderer.on('vscode:listProcessesResponse', (_event: Event, processRoots: [{ name: string, rootProcess: ProcessItem | IRemoteDiagnosticError }]) => {
updateProcessInfo(processRoots);
requestProcessList(0);
});
lastRequestTime = Date.now();
ipcRenderer.send('windowsInfoRequest');
ipcRenderer.send('vscode:listProcesses');
document.onkeydown = (e: KeyboardEvent) => {
const cmdOrCtrlKey = platform.isMacintosh ? e.metaKey : e.ctrlKey;

View File

@@ -101,7 +101,7 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I
services.set(IEnvironmentService, environmentService);
services.set(ILogService, logService);
services.set(IConfigurationService, new SyncDescriptor(ConfigurationService));
services.set(IConfigurationService, new SyncDescriptor(ConfigurationService, [environmentService.appSettingsPath]));
services.set(IRequestService, new SyncDescriptor(RequestService));
services.set(IDownloadService, new SyncDescriptor(DownloadService));
@@ -121,7 +121,7 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I
instantiationService.invokeFunction(accessor => {
const services = new ServiceCollection();
const environmentService = accessor.get(IEnvironmentService);
const { appRoot, extensionsPath, extensionDevelopmentLocationURI, isBuilt, installSourcePath } = environmentService;
const { appRoot, extensionsPath, extensionDevelopmentLocationURI: extensionDevelopmentLocationURI, isBuilt, installSourcePath } = environmentService;
const telemetryLogService = new FollowerLogService(logLevelClient, createSpdLogService('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

@@ -67,7 +67,7 @@ import { storeBackgroundColor } from 'vs/code/electron-main/theme';
import { homedir } from 'os';
import { join, sep } from 'vs/base/common/path';
import { localize } from 'vs/nls';
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
import { Schemas } from 'vs/base/common/network';
import { REMOTE_FILE_SYSTEM_CHANNEL_NAME } from 'vs/platform/remote/common/remoteAgentFileSystemChannel';
import { ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { SnapUpdateService } from 'vs/platform/update/electron-main/updateService.snap';
@@ -748,7 +748,7 @@ export class CodeApplication extends Disposable {
}
};
protocol.registerBufferProtocol(REMOTE_HOST_SCHEME, async (request, callback) => {
protocol.registerBufferProtocol(Schemas.vscodeRemote, async (request, callback) => {
if (request.method !== 'GET') {
return callback(undefined);
}

View File

@@ -307,7 +307,7 @@ function createServices(args: ParsedArgs, bufferLogService: BufferLogService): I
services.set(ILogService, logService);
services.set(ILifecycleService, new SyncDescriptor(LifecycleService));
services.set(IStateService, new SyncDescriptor(StateService));
services.set(IConfigurationService, new SyncDescriptor(ConfigurationService));
services.set(IConfigurationService, new SyncDescriptor(ConfigurationService, [environmentService.appSettingsPath]));
services.set(IRequestService, new SyncDescriptor(RequestService));
services.set(IDiagnosticsService, new SyncDescriptor(DiagnosticsService));

View File

@@ -320,6 +320,9 @@ export class CodeWindow extends Disposable implements ICodeWindow {
this._win.webContents.session.webRequest.onBeforeSendHeaders({ urls }, (details, cb) => {
this.marketplaceHeadersPromise.then(headers => {
const requestHeaders = objects.assign(details.requestHeaders, headers);
if (!this.configurationService.getValue('extensions.disableExperimentalAzureSearch')) {
requestHeaders['Cookie'] = `${requestHeaders['Cookie'] ? requestHeaders['Cookie'] + ';' : ''}EnableExternalSearchForVSCode=true`;
}
cb({ cancel: false, requestHeaders });
});
});

View File

@@ -24,7 +24,8 @@ function shouldSpawnCliProcess(argv: ParsedArgs): boolean {
return !!argv['install-source']
|| !!argv['list-extensions']
|| !!argv['install-extension']
|| !!argv['uninstall-extension'];
|| !!argv['uninstall-extension']
|| !!argv['locate-extension'];
}
interface IMainCli {

View File

@@ -41,6 +41,7 @@ import { IExtensionManifest, ExtensionType, isLanguagePackExtension } from 'vs/p
import { isUIExtension } from 'vs/platform/extensions/node/extensionsUtil';
import { CancellationToken } from 'vs/base/common/cancellation';
import { LocalizationsService } from 'vs/platform/localizations/node/localizations';
import { Schemas } from 'vs/base/common/network';
const notFound = (id: string) => localize('notFound', "Extension '{0}' not found.", id);
const notInstalled = (id: string) => localize('notInstalled', "Extension '{0}' is not installed.", id);
@@ -92,6 +93,10 @@ export class Main {
const arg = argv['uninstall-extension'];
const ids: string[] = typeof arg === 'string' ? [arg] : arg;
await this.uninstallExtension(ids);
} else if (argv['locate-extension']) {
const arg = argv['locate-extension'];
const ids: string[] = typeof arg === 'string' ? [arg] : arg;
await this.locateExtension(ids);
}
}
@@ -257,6 +262,20 @@ export class Main {
}
}
private async locateExtension(extensions: string[]): Promise<void> {
const installed = await this.extensionManagementService.getInstalled();
extensions.forEach(e => {
installed.forEach(i => {
if (i.identifier.id === e) {
if (i.location.scheme === Schemas.file) {
console.log(i.location.fsPath);
return;
}
}
});
});
}
private async updateLocalizationsCache(): Promise<void> {
const localizationService = this.instantiationService.createInstance(LocalizationsService);
await localizationService.update();
@@ -286,10 +305,10 @@ export function main(argv: ParsedArgs): Promise<void> {
const stateService = accessor.get(IStateService);
return Promise.all([envService.appSettingsHome, envService.extensionsPath].map(p => mkdirp(p))).then(() => {
const { appRoot, extensionsPath, extensionDevelopmentLocationURI, isBuilt, installSourcePath } = envService;
const { appRoot, extensionsPath, extensionDevelopmentLocationURI: extensionDevelopmentLocationURI, isBuilt, installSourcePath } = envService;
const services = new ServiceCollection();
services.set(IConfigurationService, new SyncDescriptor(ConfigurationService));
services.set(IConfigurationService, new SyncDescriptor(ConfigurationService, [environmentService.appSettingsPath]));
services.set(IRequestService, new SyncDescriptor(RequestService));
services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService, [false]));
services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService));
@@ -321,4 +340,4 @@ export function main(argv: ParsedArgs): Promise<void> {
});
});
});
}
}

View File

@@ -109,6 +109,8 @@ export class MouseHandler extends ViewEventHandler {
this._register(mouseEvents.onMouseDown(this.viewHelper.viewDomNode, (e) => this._onMouseDown(e)));
const onMouseWheel = (browserEvent: IMouseWheelEvent) => {
this.viewController.emitMouseWheel(browserEvent);
if (!this._context.configuration.editor.viewInfo.mouseWheelZoom) {
return;
}
@@ -259,6 +261,10 @@ export class MouseHandler extends ViewEventHandler {
target: t
});
}
public _onMouseWheel(e: IMouseWheelEvent): void {
this.viewController.emitMouseWheel(e);
}
}
class MouseDownOperation extends Disposable {

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { IMouseEvent, IMouseWheelEvent } from 'vs/base/browser/mouseEvent';
import { IDisposable } from 'vs/base/common/lifecycle';
import * as editorOptions from 'vs/editor/common/config/editorOptions';
import { ICursors } from 'vs/editor/common/controller/cursorCommon';
@@ -461,6 +461,12 @@ export interface ICodeEditor extends editorCommon.IEditor {
* @event
*/
onMouseLeave(listener: (e: IPartialEditorMouseEvent) => void): IDisposable;
/**
* An event emitted on a "mousewheel"
* @event
* @internal
*/
onMouseWheel(listener: (e: IMouseWheelEvent) => void): IDisposable;
/**
* An event emitted on a "keyup".
* @event

View File

@@ -11,6 +11,7 @@ import { Position } from 'vs/editor/common/core/position';
import { Selection } from 'vs/editor/common/core/selection';
import { IConfiguration } from 'vs/editor/common/editorCommon';
import { IViewModel } from 'vs/editor/common/viewModel/viewModel';
import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent';
export interface IMouseDispatchData {
position: Position;
@@ -316,4 +317,8 @@ export class ViewController {
public emitMouseDrop(e: IPartialEditorMouseEvent): void {
this.outgoingEvents.emitMouseDrop(e);
}
public emitMouseWheel(e: IMouseWheelEvent): void {
this.outgoingEvents.emitMouseWheel(e);
}
}

View File

@@ -12,6 +12,7 @@ import { Range } from 'vs/editor/common/core/range';
import { IScrollEvent } from 'vs/editor/common/editorCommon';
import * as viewEvents from 'vs/editor/common/view/viewEvents';
import { IViewModel } from 'vs/editor/common/viewModel/viewModel';
import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent';
export interface EventCallback<T> {
(event: T): void;
@@ -31,6 +32,7 @@ export class ViewOutgoingEvents extends Disposable {
public onMouseDown: EventCallback<IEditorMouseEvent> | null = null;
public onMouseDrag: EventCallback<IEditorMouseEvent> | null = null;
public onMouseDrop: EventCallback<IPartialEditorMouseEvent> | null = null;
public onMouseWheel: EventCallback<IMouseWheelEvent> | null = null;
private readonly _viewModel: IViewModel;
@@ -111,6 +113,12 @@ export class ViewOutgoingEvents extends Disposable {
}
}
public emitMouseWheel(e: IMouseWheelEvent): void {
if (this.onMouseWheel) {
this.onMouseWheel(e);
}
}
private _convertViewToModelMouseEvent(e: IEditorMouseEvent): IEditorMouseEvent;
private _convertViewToModelMouseEvent(e: IPartialEditorMouseEvent): IPartialEditorMouseEvent;
private _convertViewToModelMouseEvent(e: IEditorMouseEvent | IPartialEditorMouseEvent): IEditorMouseEvent | IPartialEditorMouseEvent {

View File

@@ -8,7 +8,7 @@ import 'vs/css!./media/tokens';
import * as nls from 'vs/nls';
import * as dom from 'vs/base/browser/dom';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { IMouseEvent, IMouseWheelEvent } from 'vs/base/browser/mouseEvent';
import { Color } from 'vs/base/common/color';
import { onUnexpectedError } from 'vs/base/common/errors';
import { Emitter, Event } from 'vs/base/common/event';
@@ -186,6 +186,9 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
private readonly _onMouseLeave: Emitter<editorBrowser.IPartialEditorMouseEvent> = this._register(new Emitter<editorBrowser.IPartialEditorMouseEvent>());
public readonly onMouseLeave: Event<editorBrowser.IPartialEditorMouseEvent> = this._onMouseLeave.event;
private readonly _onMouseWheel: Emitter<IMouseWheelEvent> = this._register(new Emitter<IMouseWheelEvent>());
public readonly onMouseWheel: Event<IMouseWheelEvent> = this._onMouseWheel.event;
private readonly _onKeyUp: Emitter<IKeyboardEvent> = this._register(new Emitter<IKeyboardEvent>());
public readonly onKeyUp: Event<IKeyboardEvent> = this._onKeyUp.event;
@@ -1442,6 +1445,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
// In IE, the focus is not synchronous, so we give it a little help
this._editorWidgetFocus.setValue(true);
};
viewOutgoingEvents.onDidScroll = (e) => this._onDidScrollChange.fire(e);
viewOutgoingEvents.onDidLoseFocus = () => this._editorTextFocus.setValue(false);
viewOutgoingEvents.onContextMenu = (e) => this._onContextMenu.fire(e);
@@ -1452,6 +1456,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
viewOutgoingEvents.onKeyUp = (e) => this._onKeyUp.fire(e);
viewOutgoingEvents.onMouseMove = (e) => this._onMouseMove.fire(e);
viewOutgoingEvents.onMouseLeave = (e) => this._onMouseLeave.fire(e);
viewOutgoingEvents.onMouseWheel = (e) => this._onMouseWheel.fire(e);
viewOutgoingEvents.onKeyDown = (e) => this._onKeyDown.fire(e);
const view = new View(

View File

@@ -1958,7 +1958,7 @@ export class EditorOptionsValidator {
};
}
private static _santizeGotoLocationOpts(opts: IEditorOptions, defaults: InternalGoToLocationOptions): InternalGoToLocationOptions {
private static _sanitizeGotoLocationOpts(opts: IEditorOptions, defaults: InternalGoToLocationOptions): InternalGoToLocationOptions {
const gotoOpts = opts.gotoLocation || {};
return {
multiple: _stringSet<'peek' | 'gotoAndPeek' | 'goto'>(gotoOpts.multiple, defaults.multiple, ['peek', 'gotoAndPeek', 'goto'])
@@ -2117,7 +2117,7 @@ export class EditorOptionsValidator {
suggestLineHeight: _clampedInt(opts.suggestLineHeight, defaults.suggestLineHeight, 0, 1000),
tabCompletion: this._sanitizeTabCompletionOpts(opts.tabCompletion, defaults.tabCompletion),
suggest: this._sanitizeSuggestOpts(opts, defaults.suggest),
gotoLocation: this._santizeGotoLocationOpts(opts, defaults.gotoLocation),
gotoLocation: this._sanitizeGotoLocationOpts(opts, defaults.gotoLocation),
selectionHighlight: _boolean(opts.selectionHighlight, defaults.selectionHighlight),
occurrencesHighlight: _boolean(opts.occurrencesHighlight, defaults.occurrencesHighlight),
codeLens: _boolean(opts.codeLens, defaults.codeLens),

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { registerEditorAction, registerEditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { CodeActionCommand, OrganizeImportsAction, QuickFixAction, QuickFixController, RefactorAction, SourceAction, AutoFixAction } from 'vs/editor/contrib/codeAction/codeActionCommands';
import { CodeActionCommand, OrganizeImportsAction, QuickFixAction, QuickFixController, RefactorAction, SourceAction, AutoFixAction, FixAllAction } from 'vs/editor/contrib/codeAction/codeActionCommands';
registerEditorContribution(QuickFixController);
@@ -13,4 +13,5 @@ registerEditorAction(RefactorAction);
registerEditorAction(SourceAction);
registerEditorAction(OrganizeImportsAction);
registerEditorAction(AutoFixAction);
registerEditorAction(FixAllAction);
registerEditorCommand(new CodeActionCommand());

View File

@@ -13,7 +13,7 @@ import { KeyCode, KeyMod, ResolvedKeybinding } from 'vs/base/common/keyCodes';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { IEditorContribution, IScrollEvent, ScrollType } from 'vs/editor/common/editorCommon';
import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { IMenuService, MenuId } from 'vs/platform/actions/common/actions';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
@@ -21,6 +21,7 @@ import { IContextMenuService, IContextViewService } from 'vs/platform/contextvie
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { ITextModel } from 'vs/editor/common/model';
import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent';
export class ContextMenuController implements IEditorContribution {
@@ -45,8 +46,8 @@ export class ContextMenuController implements IEditorContribution {
this._editor = editor;
this._toDispose.push(this._editor.onContextMenu((e: IEditorMouseEvent) => this._onContextMenu(e)));
this._toDispose.push(this._editor.onDidScrollChange((e: IScrollEvent) => {
if (this._contextMenuIsBeingShownCount > 0 && e.scrollTopChanged) {
this._toDispose.push(this._editor.onMouseWheel((e: IMouseWheelEvent) => {
if (this._contextMenuIsBeingShownCount > 0) {
this._contextViewService.hideContextView();
}
}));

View File

@@ -214,7 +214,7 @@ export async function formatDocumentWithSelectedProvider(
const provider = getRealAndSyntheticDocumentFormattersOrdered(model);
const selected = await FormattingConflicts.select(provider, model, mode);
if (selected) {
await instaService.invokeFunction(formatDocumentWithProvider, selected, editorOrModel, token);
await instaService.invokeFunction(formatDocumentWithProvider, selected, editorOrModel, mode, token);
}
}
@@ -222,6 +222,7 @@ export async function formatDocumentWithProvider(
accessor: ServicesAccessor,
provider: DocumentFormattingEditProvider,
editorOrModel: ITextModel | IActiveCodeEditor,
mode: FormattingMode,
token: CancellationToken
): Promise<boolean> {
const workerService = accessor.get(IEditorWorkerService);
@@ -255,10 +256,13 @@ export async function formatDocumentWithProvider(
if (isCodeEditor(editorOrModel)) {
// use editor to apply edits
FormattingEdit.execute(editorOrModel, edits);
alertFormattingEdits(edits);
editorOrModel.pushUndoStop();
editorOrModel.focus();
editorOrModel.revealPositionInCenterIfOutsideViewport(editorOrModel.getPosition(), editorCommon.ScrollType.Immediate);
if (mode !== FormattingMode.Silent) {
alertFormattingEdits(edits);
editorOrModel.pushUndoStop();
editorOrModel.focus();
editorOrModel.revealPositionInCenterIfOutsideViewport(editorOrModel.getPosition(), editorCommon.ScrollType.Immediate);
}
} else {
// use model to apply edits

View File

@@ -98,7 +98,7 @@ export abstract class ReferencesController implements editorCommon.IEditorContri
}
}));
const storageKey = 'peekViewLayout';
const data = <LayoutData>JSON.parse(this._storageService.get(storageKey, StorageScope.GLOBAL, '{}'));
const data = LayoutData.fromJSON(this._storageService.get(storageKey, StorageScope.GLOBAL, '{}'));
this._widget = this._instantiationService.createInstance(ReferenceWidget, this._editor, this._defaultTreeKeyboardSupport, data);
this._widget.setTitle(nls.localize('labelLoading', "Loading..."));
this._widget.show(range);

View File

@@ -157,9 +157,25 @@ class DecorationsManager implements IDisposable {
}
}
export interface LayoutData {
export class LayoutData {
ratio: number;
heightInLines: number;
static fromJSON(raw: string): LayoutData {
let ratio: number | undefined;
let heightInLines: number | undefined;
try {
const data = <LayoutData>JSON.parse(raw);
ratio = data.ratio;
heightInLines = data.heightInLines;
} catch {
//
}
return {
ratio: ratio || 0.7,
heightInLines: heightInLines || 18
};
}
}
export interface SelectionEvent {

View File

@@ -9,9 +9,10 @@ import * as arrays from 'vs/base/common/arrays';
import * as types from 'vs/base/common/types';
import * as objects from 'vs/base/common/objects';
import { URI } from 'vs/base/common/uri';
import { OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry';
import { OVERRIDE_PROPERTY_PATTERN, ConfigurationScope, IConfigurationRegistry, Extensions, IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry';
import { IOverrides, overrideIdentifierFromKey, addToValueTree, toValuesTree, IConfigurationModel, getConfigurationValue, IConfigurationOverrides, IConfigurationData, getDefaultValues, getConfigurationKeys, IConfigurationChangeEvent, ConfigurationTarget, removeFromValueTree, toOverrides } from 'vs/platform/configuration/common/configuration';
import { Workspace } from 'vs/platform/workspace/common/workspace';
import { Registry } from 'vs/platform/registry/common/platform';
export class ConfigurationModel implements IConfigurationModel {
@@ -195,10 +196,11 @@ export class DefaultConfigurationModel extends ConfigurationModel {
export class ConfigurationModelParser {
private _raw: any = null;
private _configurationModel: ConfigurationModel | null = null;
private _parseErrors: any[] = [];
constructor(protected readonly _name: string) { }
constructor(protected readonly _name: string, private _scopes?: ConfigurationScope[]) { }
get configurationModel(): ConfigurationModel {
return this._configurationModel || new ConfigurationModel();
@@ -208,15 +210,26 @@ export class ConfigurationModelParser {
return this._parseErrors;
}
public parse(content: string | null | undefined): void {
public parseContent(content: string | null | undefined): void {
if (content) {
const raw = this.parseContent(content);
const configurationModel = this.parseRaw(raw);
this._configurationModel = new ConfigurationModel(configurationModel.contents, configurationModel.keys, configurationModel.overrides);
const raw = this.doParseContent(content);
this.parseRaw(raw);
}
}
protected parseContent(content: string): any {
public parseRaw(raw: any): void {
this._raw = raw;
const configurationModel = this.doParseRaw(raw);
this._configurationModel = new ConfigurationModel(configurationModel.contents, configurationModel.keys, configurationModel.overrides);
}
public parse(): void {
if (this._raw) {
this.parseRaw(this._raw);
}
}
protected doParseContent(content: string): any {
let raw: any = {};
let currentProperty: string | null = null;
let currentParent: any = [];
@@ -273,12 +286,36 @@ export class ConfigurationModelParser {
return raw;
}
protected parseRaw(raw: any): IConfigurationModel {
protected doParseRaw(raw: any): IConfigurationModel {
if (this._scopes) {
const configurationProperties = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurationProperties();
raw = this.filterByScope(raw, configurationProperties, true, this._scopes);
}
const contents = toValuesTree(raw, message => console.error(`Conflict in settings file ${this._name}: ${message}`));
const keys = Object.keys(raw);
const overrides: IOverrides[] = toOverrides(raw, message => console.error(`Conflict in settings file ${this._name}: ${message}`));
return { contents, keys, overrides };
}
private filterByScope(properties: {}, configurationProperties: { [qualifiedKey: string]: IConfigurationPropertySchema }, filterOverriddenProperties: boolean, scopes: ConfigurationScope[]): {} {
const result = {};
for (let key in properties) {
if (OVERRIDE_PROPERTY_PATTERN.test(key) && filterOverriddenProperties) {
result[key] = this.filterByScope(properties[key], configurationProperties, false, scopes);
} else {
const scope = this.getScope(key, configurationProperties);
if (scopes.indexOf(scope) !== -1) {
result[key] = properties[key];
}
}
}
return result;
}
private getScope(key: string, configurationProperties: { [qualifiedKey: string]: IConfigurationPropertySchema }): ConfigurationScope {
const propertySchema = configurationProperties[key];
return propertySchema && typeof propertySchema.scope !== 'undefined' ? propertySchema.scope : ConfigurationScope.WINDOW;
}
}
export class Configuration {

View File

@@ -27,7 +27,7 @@ export class NodeBasedUserConfiguration extends Disposable {
this.userConfigModelWatcher = new ConfigWatcher(this.settingsPath, {
changeBufferDelay: 300, onError: error => onUnexpectedError(error), defaultConfig: new ConfigurationModelParser(this.settingsPath), parse: (content: string, parseErrors: any[]) => {
const userConfigModelParser = new ConfigurationModelParser(this.settingsPath);
userConfigModelParser.parse(content);
userConfigModelParser.parseContent(content);
parseErrors = [...userConfigModelParser.errors];
return userConfigModelParser;
}, initCallback: () => c(undefined)

View File

@@ -9,7 +9,6 @@ import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { IConfigurationService, IConfigurationChangeEvent, IConfigurationOverrides, ConfigurationTarget, compare, isConfigurationOverrides, IConfigurationData } from 'vs/platform/configuration/common/configuration';
import { DefaultConfigurationModel, Configuration, ConfigurationChangeEvent, ConfigurationModel } from 'vs/platform/configuration/common/configurationModels';
import { Event, Emitter } from 'vs/base/common/event';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { NodeBasedUserConfiguration } from 'vs/platform/configuration/node/configuration';
@@ -24,11 +23,11 @@ export class ConfigurationService extends Disposable implements IConfigurationSe
readonly onDidChangeConfiguration: Event<IConfigurationChangeEvent> = this._onDidChangeConfiguration.event;
constructor(
@IEnvironmentService environmentService: IEnvironmentService
configurationPath: string
) {
super();
this.userConfiguration = this._register(new NodeBasedUserConfiguration(environmentService.appSettingsPath));
this.userConfiguration = this._register(new NodeBasedUserConfiguration(configurationPath));
// Initialize
const defaults = new DefaultConfigurationModel();

View File

@@ -250,10 +250,10 @@ suite('CustomConfigurationModel', () => {
test('simple merge using models', () => {
let base = new ConfigurationModelParser('base');
base.parse(JSON.stringify({ 'a': 1, 'b': 2 }));
base.parseContent(JSON.stringify({ 'a': 1, 'b': 2 }));
let add = new ConfigurationModelParser('add');
add.parse(JSON.stringify({ 'a': 3, 'c': 4 }));
add.parseContent(JSON.stringify({ 'a': 3, 'c': 4 }));
let result = base.configurationModel.merge(add.configurationModel);
assert.deepEqual(result.contents, { 'a': 3, 'b': 2, 'c': 4 });
@@ -261,14 +261,14 @@ suite('CustomConfigurationModel', () => {
test('simple merge with an undefined contents', () => {
let base = new ConfigurationModelParser('base');
base.parse(JSON.stringify({ 'a': 1, 'b': 2 }));
base.parseContent(JSON.stringify({ 'a': 1, 'b': 2 }));
let add = new ConfigurationModelParser('add');
let result = base.configurationModel.merge(add.configurationModel);
assert.deepEqual(result.contents, { 'a': 1, 'b': 2 });
base = new ConfigurationModelParser('base');
add = new ConfigurationModelParser('add');
add.parse(JSON.stringify({ 'a': 1, 'b': 2 }));
add.parseContent(JSON.stringify({ 'a': 1, 'b': 2 }));
result = base.configurationModel.merge(add.configurationModel);
assert.deepEqual(result.contents, { 'a': 1, 'b': 2 });
@@ -280,25 +280,25 @@ suite('CustomConfigurationModel', () => {
test('Recursive merge using config models', () => {
let base = new ConfigurationModelParser('base');
base.parse(JSON.stringify({ 'a': { 'b': 1 } }));
base.parseContent(JSON.stringify({ 'a': { 'b': 1 } }));
let add = new ConfigurationModelParser('add');
add.parse(JSON.stringify({ 'a': { 'b': 2 } }));
add.parseContent(JSON.stringify({ 'a': { 'b': 2 } }));
let result = base.configurationModel.merge(add.configurationModel);
assert.deepEqual(result.contents, { 'a': { 'b': 2 } });
});
test('Test contents while getting an existing property', () => {
let testObject = new ConfigurationModelParser('test');
testObject.parse(JSON.stringify({ 'a': 1 }));
testObject.parseContent(JSON.stringify({ 'a': 1 }));
assert.deepEqual(testObject.configurationModel.getValue('a'), 1);
testObject.parse(JSON.stringify({ 'a': { 'b': 1 } }));
testObject.parseContent(JSON.stringify({ 'a': { 'b': 1 } }));
assert.deepEqual(testObject.configurationModel.getValue('a'), { 'b': 1 });
});
test('Test contents are undefined for non existing properties', () => {
const testObject = new ConfigurationModelParser('test');
testObject.parse(JSON.stringify({
testObject.parseContent(JSON.stringify({
awesome: true
}));
@@ -313,7 +313,7 @@ suite('CustomConfigurationModel', () => {
test('Test configWithOverrides gives all content merged with overrides', () => {
const testObject = new ConfigurationModelParser('test');
testObject.parse(JSON.stringify({ 'a': 1, 'c': 1, '[b]': { 'a': 2 } }));
testObject.parseContent(JSON.stringify({ 'a': 1, 'c': 1, '[b]': { 'a': 2 } }));
assert.deepEqual(testObject.configurationModel.override('b').contents, { 'a': 2, 'c': 1, '[b]': { 'a': 2 } });
});
@@ -326,17 +326,17 @@ suite('CustomConfigurationModel', () => {
test('Test update with empty data', () => {
const testObject = new ConfigurationModelParser('test');
testObject.parse('');
testObject.parseContent('');
assert.deepEqual(testObject.configurationModel.contents, {});
assert.deepEqual(testObject.configurationModel.keys, []);
testObject.parse(null!);
testObject.parseContent(null!);
assert.deepEqual(testObject.configurationModel.contents, {});
assert.deepEqual(testObject.configurationModel.keys, []);
testObject.parse(undefined!);
testObject.parseContent(undefined!);
assert.deepEqual(testObject.configurationModel.contents, {});
assert.deepEqual(testObject.configurationModel.keys, []);
@@ -472,7 +472,7 @@ suite('Configuration', () => {
test('Test update value', () => {
const parser = new ConfigurationModelParser('test');
parser.parse(JSON.stringify({ 'a': 1 }));
parser.parseContent(JSON.stringify({ 'a': 1 }));
const testObject: Configuration = new Configuration(parser.configurationModel, new ConfigurationModel());
testObject.updateValue('a', 2);
@@ -482,7 +482,7 @@ suite('Configuration', () => {
test('Test update value after inspect', () => {
const parser = new ConfigurationModelParser('test');
parser.parse(JSON.stringify({ 'a': 1 }));
parser.parseContent(JSON.stringify({ 'a': 1 }));
const testObject: Configuration = new Configuration(parser.configurationModel, new ConfigurationModel());
testObject.inspect('a', {}, undefined);

View File

@@ -10,29 +10,17 @@ import * as fs from 'fs';
import { Registry } from 'vs/platform/registry/common/platform';
import { ConfigurationService } from 'vs/platform/configuration/node/configurationService';
import { ParsedArgs } from 'vs/platform/environment/common/environment';
import { parseArgs } from 'vs/platform/environment/node/argv';
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
import * as uuid from 'vs/base/common/uuid';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
import { testFile } from 'vs/base/test/node/utils';
class SettingsTestEnvironmentService extends EnvironmentService {
constructor(args: ParsedArgs, _execPath: string, private customAppSettingsHome: string) {
super(args, _execPath);
}
get appSettingsPath(): string { return this.customAppSettingsHome; }
}
suite('ConfigurationService - Node', () => {
test('simple', async () => {
const res = await testFile('config', 'config.json');
fs.writeFileSync(res.testFile, '{ "foo": "bar" }');
const service = new ConfigurationService(new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, res.testFile));
const service = new ConfigurationService(res.testFile);
const config = service.getValue<{
foo: string;
}>();
@@ -49,7 +37,7 @@ suite('ConfigurationService - Node', () => {
fs.writeFileSync(res.testFile, '{ "testworkbench.editor.tabs": true }');
const service = new ConfigurationService(new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, res.testFile));
const service = new ConfigurationService(res.testFile);
const config = service.getValue<{
testworkbench: {
editor: {
@@ -71,7 +59,7 @@ suite('ConfigurationService - Node', () => {
fs.writeFileSync(res.testFile, ',,,,');
const service = new ConfigurationService(new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, res.testFile));
const service = new ConfigurationService(res.testFile);
const config = service.getValue<{
foo: string;
}>();
@@ -87,7 +75,7 @@ suite('ConfigurationService - Node', () => {
const newDir = path.join(parentDir, 'config', id);
const testFile = path.join(newDir, 'config.json');
const service = new ConfigurationService(new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, testFile));
const service = new ConfigurationService(testFile);
const config = service.getValue<{ foo: string }>();
assert.ok(config);
@@ -98,7 +86,7 @@ suite('ConfigurationService - Node', () => {
test('trigger configuration change event', async () => {
const res = await testFile('config', 'config.json');
const service = new ConfigurationService(new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, res.testFile));
const service = new ConfigurationService(res.testFile);
return new Promise((c, e) => {
service.onDidChangeConfiguration(() => {
assert.equal(service.getValue('foo'), 'bar');
@@ -115,7 +103,7 @@ suite('ConfigurationService - Node', () => {
fs.writeFileSync(res.testFile, '{ "foo": "bar" }');
const service = new ConfigurationService(new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, res.testFile));
const service = new ConfigurationService(res.testFile);
let config = service.getValue<{
foo: string;
}>();
@@ -163,7 +151,7 @@ suite('ConfigurationService - Node', () => {
}
});
let serviceWithoutFile = new ConfigurationService(new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, '__testFile'));
let serviceWithoutFile = new ConfigurationService('__testFile');
let setting = serviceWithoutFile.getValue<ITestSetting>();
assert.ok(setting);
@@ -172,7 +160,7 @@ suite('ConfigurationService - Node', () => {
return testFile('config', 'config.json').then(async res => {
fs.writeFileSync(res.testFile, '{ "testworkbench.editor.tabs": true }');
const service = new ConfigurationService(new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, res.testFile));
const service = new ConfigurationService(res.testFile);
let setting = service.getValue<ITestSetting>();
@@ -205,7 +193,7 @@ suite('ConfigurationService - Node', () => {
});
const r = await testFile('config', 'config.json');
const service = new ConfigurationService(new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, r.testFile));
const service = new ConfigurationService(r.testFile);
let res = service.inspect('something.missing');
assert.strictEqual(res.value, undefined);
assert.strictEqual(res.default, undefined);
@@ -241,7 +229,7 @@ suite('ConfigurationService - Node', () => {
});
const r = await testFile('config', 'config.json');
const service = new ConfigurationService(new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, r.testFile));
const service = new ConfigurationService(r.testFile);
let res = service.inspect('lookup.service.testNullSetting');
assert.strictEqual(res.default, null);
assert.strictEqual(res.value, null);

View File

@@ -17,6 +17,7 @@ import { IContextMenuDelegate } from 'vs/base/browser/contextmenu';
import { EventType, $, removeNode } from 'vs/base/browser/dom';
import { attachMenuStyler } from 'vs/platform/theme/common/styler';
import { domEvent } from 'vs/base/browser/event';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
export interface IContextMenuHandlerOptions {
blockMouse: boolean;
@@ -83,6 +84,25 @@ export class ContextMenuHandler {
menu.onDidCancel(() => this.contextViewService.hideContextView(true), null, menuDisposables);
menu.onDidBlur(() => this.contextViewService.hideContextView(true), null, menuDisposables);
domEvent(window, EventType.BLUR)(() => { this.contextViewService.hideContextView(true); }, null, menuDisposables);
domEvent(window, EventType.MOUSE_DOWN)((e: MouseEvent) => {
let event = new StandardMouseEvent(e);
let element: HTMLElement | null = event.target;
// Don't do anything as we are likely creating a context menu
if (event.rightButton) {
return;
}
while (element) {
if (element === container) {
return;
}
element = element.parentElement;
}
this.contextViewService.hideContextView(true);
}, null, menuDisposables);
return combinedDisposable([...menuDisposables, menu]);
},

View File

@@ -30,6 +30,11 @@ export interface IRemoteDiagnosticInfo extends IDiagnosticInfo {
hostName: string;
}
export interface IRemoteDiagnosticError {
hostName: string;
errorMessage: string;
}
export interface IDiagnosticInfoOptions {
includeProcesses?: boolean;
folders?: UriComponents[];
@@ -46,4 +51,8 @@ export interface WorkspaceStats {
configFiles: WorkspaceStatItem[];
fileCount: number;
maxFilesReached: boolean;
}
export function isRemoteDiagnosticError(x: any): x is IRemoteDiagnosticError {
return !!x.hostName && !!x.errorMessage;
}

View File

@@ -15,7 +15,7 @@ import { app } from 'electron';
import { basename } from 'vs/base/common/path';
import { URI } from 'vs/base/common/uri';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IMachineInfo, WorkspaceStats, SystemInfo } from 'vs/platform/diagnostics/common/diagnosticsService';
import { IMachineInfo, WorkspaceStats, SystemInfo, IRemoteDiagnosticInfo, isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnosticsService';
import { collectWorkspaceStats, getMachineInfo } from 'vs/platform/diagnostics/node/diagnosticsService';
import { ProcessItem } from 'vs/base/common/processes';
@@ -92,23 +92,28 @@ export class DiagnosticsService implements IDiagnosticsService {
try {
const remoteData = await launchService.getRemoteDiagnostics({ includeProcesses: true, includeWorkspaceMetadata: true });
remoteData.forEach(diagnostics => {
processInfo += `\n\nRemote: ${diagnostics.hostName}`;
if (diagnostics.processes) {
processInfo += `\n${this.formatProcessList(info, diagnostics.processes)}`;
}
if (isRemoteDiagnosticError(diagnostics)) {
processInfo += `\n${diagnostics.errorMessage}`;
workspaceInfo += `\n${diagnostics.errorMessage}`;
} else {
processInfo += `\n\nRemote: ${diagnostics.hostName}`;
if (diagnostics.processes) {
processInfo += `\n${this.formatProcessList(info, diagnostics.processes)}`;
}
if (diagnostics.workspaceMetadata) {
workspaceInfo += `\n| Remote: ${diagnostics.hostName}`;
for (const folder of Object.keys(diagnostics.workspaceMetadata)) {
const metadata = diagnostics.workspaceMetadata[folder];
if (diagnostics.workspaceMetadata) {
workspaceInfo += `\n| Remote: ${diagnostics.hostName}`;
for (const folder of Object.keys(diagnostics.workspaceMetadata)) {
const metadata = diagnostics.workspaceMetadata[folder];
let countMessage = `${metadata.fileCount} files`;
if (metadata.maxFilesReached) {
countMessage = `more than ${countMessage}`;
let countMessage = `${metadata.fileCount} files`;
if (metadata.maxFilesReached) {
countMessage = `more than ${countMessage}`;
}
workspaceInfo += `| Folder (${folder}): ${countMessage}`;
workspaceInfo += this.formatWorkspaceStats(metadata);
}
workspaceInfo += `| Folder (${folder}): ${countMessage}`;
workspaceInfo += this.formatWorkspaceStats(metadata);
}
}
});
@@ -135,7 +140,7 @@ export class DiagnosticsService implements IDiagnosticsService {
processArgs: `${info.mainArguments.join(' ')}`,
gpuStatus: app.getGPUFeatureStatus(),
screenReader: `${app.isAccessibilitySupportEnabled() ? 'yes' : 'no'}`,
remoteData: await launchService.getRemoteDiagnostics({ includeProcesses: false, includeWorkspaceMetadata: false })
remoteData: (await launchService.getRemoteDiagnostics({ includeProcesses: false, includeWorkspaceMetadata: false })).filter((x): x is IRemoteDiagnosticInfo => !(x instanceof Error))
};
@@ -169,25 +174,29 @@ export class DiagnosticsService implements IDiagnosticsService {
try {
const data = await launchService.getRemoteDiagnostics({ includeProcesses: true, includeWorkspaceMetadata: true });
data.forEach(diagnostics => {
output.push('\n\n');
output.push(`Remote: ${diagnostics.hostName}`);
output.push(this.formatMachineInfo(diagnostics.machineInfo));
if (isRemoteDiagnosticError(diagnostics)) {
output.push(`\n${diagnostics.errorMessage}`);
} else {
output.push('\n\n');
output.push(`Remote: ${diagnostics.hostName}`);
output.push(this.formatMachineInfo(diagnostics.machineInfo));
if (diagnostics.processes) {
output.push(this.formatProcessList(info, diagnostics.processes));
}
if (diagnostics.processes) {
output.push(this.formatProcessList(info, diagnostics.processes));
}
if (diagnostics.workspaceMetadata) {
for (const folder of Object.keys(diagnostics.workspaceMetadata)) {
const metadata = diagnostics.workspaceMetadata[folder];
if (diagnostics.workspaceMetadata) {
for (const folder of Object.keys(diagnostics.workspaceMetadata)) {
const metadata = diagnostics.workspaceMetadata[folder];
let countMessage = `${metadata.fileCount} files`;
if (metadata.maxFilesReached) {
countMessage = `more than ${countMessage}`;
let countMessage = `${metadata.fileCount} files`;
if (metadata.maxFilesReached) {
countMessage = `more than ${countMessage}`;
}
output.push(`Folder (${folder}): ${countMessage}`);
output.push(this.formatWorkspaceStats(metadata));
}
output.push(`Folder (${folder}): ${countMessage}`);
output.push(this.formatWorkspaceStats(metadata));
}
}
});
@@ -306,13 +315,13 @@ export class DiagnosticsService implements IDiagnosticsService {
output.push('CPU %\tMem MB\t PID\tProcess');
if (rootProcess) {
this.formatProcessItem(mapPidToWindowTitle, output, rootProcess, 0);
this.formatProcessItem(info.mainPID, mapPidToWindowTitle, output, rootProcess, 0);
}
return output.join('\n');
}
private formatProcessItem(mapPidToWindowTitle: Map<number, string>, output: string[], item: ProcessItem, indent: number): void {
private formatProcessItem(mainPid: number, mapPidToWindowTitle: Map<number, string>, output: string[], item: ProcessItem, indent: number): void {
const isRoot = (indent === 0);
const MB = 1024 * 1024;
@@ -320,7 +329,7 @@ export class DiagnosticsService implements IDiagnosticsService {
// Format name with indent
let name: string;
if (isRoot) {
name = `${product.applicationName} main`;
name = item.pid === mainPid ? `${product.applicationName} main` : 'remote agent';
} else {
name = `${repeat(' ', indent)} ${item.name}`;
@@ -333,7 +342,7 @@ export class DiagnosticsService implements IDiagnosticsService {
// Recurse into children if any
if (Array.isArray(item.children)) {
item.children.forEach(child => this.formatProcessItem(mapPidToWindowTitle, output, child, indent + 1));
item.children.forEach(child => this.formatProcessItem(mainPid, mapPidToWindowTitle, output, child, indent + 1));
}
}
}

View File

@@ -226,12 +226,12 @@ export async function registerWindowDriver(accessor: ServicesAccessor): Promise<
const windowDriverRegistryChannel = mainProcessService.getChannel('windowDriverRegistry');
const windowDriverRegistry = new WindowDriverRegistryChannelClient(windowDriverRegistryChannel);
await windowDriverRegistry.registerWindowDriver(windowService.getCurrentWindowId());
await windowDriverRegistry.registerWindowDriver(windowService.windowId);
// const options = await windowDriverRegistry.registerWindowDriver(windowId);
// if (options.verbose) {
// windowDriver.openDevTools();
// }
return toDisposable(() => windowDriverRegistry.reloadWindowDriver(windowService.getCurrentWindowId()));
return toDisposable(() => windowDriverRegistry.reloadWindowDriver(windowService.windowId));
}

View File

@@ -50,6 +50,7 @@ export interface ParsedArgs {
'show-versions'?: boolean;
'install-extension'?: string | string[];
'uninstall-extension'?: string | string[];
'locate-extension'?: string | string[];
'enable-proposed-api'?: string | string[];
'open-url'?: boolean;
'skip-getting-started'?: boolean;
@@ -109,6 +110,9 @@ export interface IEnvironmentService {
appSettingsPath: string;
appKeybindingsPath: string;
machineSettingsHome: string;
machineSettingsPath: string;
settingsSearchBuildId?: number;
settingsSearchUrl?: string;
@@ -124,7 +128,7 @@ export interface IEnvironmentService {
disableExtensions: boolean | string[];
builtinExtensionsPath: string;
extensionsPath: string;
extensionDevelopmentLocationURI?: URI | URI[];
extensionDevelopmentLocationURI?: URI[];
extensionTestsLocationURI?: URI;
debugExtensionHost: IExtensionHostDebugParams;

View File

@@ -66,6 +66,7 @@ export const options: Option[] = [
{ id: 'max-memory', type: 'string', cat: 't', description: localize('maxMemory', "Max memory size for a window (in Mbytes).") },
{ id: 'remote', type: 'string' },
{ id: 'locate-extension', type: 'string' },
{ id: 'extensionDevelopmentPath', type: 'string' },
{ id: 'extensionTestsPath', type: 'string' },
{ id: 'debugId', type: 'string' },

View File

@@ -110,6 +110,12 @@ export class EnvironmentService implements IEnvironmentService {
@memoize
get appSettingsPath(): string { return path.join(this.appSettingsHome, 'settings.json'); }
@memoize
get machineSettingsHome(): string { return path.join(this.userDataPath, 'Machine'); }
@memoize
get machineSettingsPath(): string { return path.join(this.machineSettingsHome, 'settings.json'); }
@memoize
get globalStorageHome(): string { return path.join(this.appSettingsHome, 'globalStorage'); }
@@ -172,7 +178,7 @@ export class EnvironmentService implements IEnvironmentService {
}
@memoize
get extensionDevelopmentLocationURI(): URI | URI[] | undefined {
get extensionDevelopmentLocationURI(): URI[] | undefined {
const s = this._args.extensionDevelopmentPath;
if (Array.isArray(s)) {
return s.map(p => {
@@ -183,9 +189,9 @@ export class EnvironmentService implements IEnvironmentService {
});
} else if (s) {
if (/^[^:/?#]+?:\/\//.test(s)) {
return URI.parse(s);
return [URI.parse(s)];
}
return URI.file(path.normalize(s));
return [URI.file(path.normalize(s))];
}
return undefined;
}

View File

@@ -753,7 +753,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
}
loadAllDependencies(extensions: IExtensionIdentifier[], token: CancellationToken): Promise<IGalleryExtension[]> {
return this.getDependenciesReccursively(extensions.map(e => e.id), [], token);
return this.getDependenciesRecursively(extensions.map(e => e.id), [], token);
}
getAllVersions(extension: IGalleryExtension, compatible: boolean): Promise<IGalleryExtensionVersion[]> {
@@ -812,7 +812,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
});
}
private getDependenciesReccursively(toGet: string[], result: IGalleryExtension[], token: CancellationToken): Promise<IGalleryExtension[]> {
private getDependenciesRecursively(toGet: string[], result: IGalleryExtension[], token: CancellationToken): Promise<IGalleryExtension[]> {
if (!toGet || !toGet.length) {
return Promise.resolve(result);
}
@@ -832,7 +832,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
result = distinct(result.concat(loadedDependencies), d => d.identifier.uuid);
const dependencies: string[] = [];
dependenciesSet.forEach(d => !ExtensionGalleryService.hasExtensionByName(result, d) && dependencies.push(d));
return this.getDependenciesReccursively(dependencies, result, token);
return this.getDependenciesRecursively(dependencies, result, token);
});
}
@@ -903,7 +903,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
if (version) {
return version;
}
return this.getLastValidExtensionVersionReccursively(extension, versions);
return this.getLastValidExtensionVersionRecursively(extension, versions);
}
private getLastValidExtensionVersionFromProperties(extension: IRawGalleryExtension, versions: IRawGalleryExtensionVersion[]): Promise<IRawGalleryExtensionVersion> | null {
@@ -936,7 +936,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
.then(manifest => manifest ? manifest.engines.vscode : Promise.reject<string>('Error while reading manifest'));
}
private getLastValidExtensionVersionReccursively(extension: IRawGalleryExtension, versions: IRawGalleryExtensionVersion[]): Promise<IRawGalleryExtensionVersion | null> {
private getLastValidExtensionVersionRecursively(extension: IRawGalleryExtension, versions: IRawGalleryExtensionVersion[]): Promise<IRawGalleryExtensionVersion | null> {
if (!versions.length) {
return Promise.resolve(null);
}
@@ -945,7 +945,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
return this.getEngine(version)
.then(engine => {
if (!isEngineValid(engine)) {
return this.getLastValidExtensionVersionReccursively(extension, versions.slice(1));
return this.getLastValidExtensionVersionRecursively(extension, versions.slice(1));
}
version.properties = version.properties || [];

View File

@@ -13,6 +13,7 @@ import { startsWithIgnoreCase } from 'vs/base/common/strings';
import { IDisposable } from 'vs/base/common/lifecycle';
import { isEqualOrParent, isEqual } from 'vs/base/common/resources';
import { isUndefinedOrNull } from 'vs/base/common/types';
import { VSBuffer, VSBufferReadable } from 'vs/base/common/buffer';
export const IFileService = createDecorator<IFileService>('fileService');
@@ -123,10 +124,15 @@ export interface IFileService {
*/
resolveStreamContent(resource: URI, options?: IResolveContentOptions): Promise<IStreamContent>;
/**
* @deprecated use writeFile instead
*/
updateContent(resource: URI, value: string | ITextSnapshot, options?: IWriteTextFileOptions): Promise<IFileStatWithMetadata>;
/**
* Updates the content replacing its previous value.
*/
updateContent(resource: URI, value: string | ITextSnapshot, options?: IUpdateContentOptions): Promise<IFileStatWithMetadata>;
writeFile(resource: URI, bufferOrReadable: VSBuffer | VSBufferReadable, options?: IWriteFileOptions): Promise<IFileStatWithMetadata>;
/**
* Moves the file/folder to a new path identified by the resource.
@@ -143,12 +149,17 @@ export interface IFileService {
copy(source: URI, target: URI, overwrite?: boolean): Promise<IFileStatWithMetadata>;
/**
* Creates a new file with the given path. The returned promise
* @deprecated use createFile2 instead
*/
createFile(resource: URI, content?: string, options?: ICreateFileOptions): Promise<IFileStatWithMetadata>;
/**
* Creates a new file with the given path and optional contents. The returned promise
* will have the stat model object as a result.
*
* The optional parameter content can be used as value to fill into the new file.
*/
createFile(resource: URI, content?: string, options?: ICreateFileOptions): Promise<IFileStatWithMetadata>;
createFile2(resource: URI, bufferOrReadable?: VSBuffer | VSBufferReadable, options?: ICreateFileOptions): Promise<IFileStatWithMetadata>;
/**
* Creates a new folder with the given path. The returned promise
@@ -650,17 +661,6 @@ export interface ITextSnapshot {
read(): string | null;
}
export class StringSnapshot implements ITextSnapshot {
private _value: string | null;
constructor(value: string) {
this._value = value;
}
read(): string | null {
let ret = this._value;
this._value = null;
return ret;
}
}
/**
* Helper method to convert a snapshot into its full string form.
*/
@@ -674,6 +674,35 @@ export function snapshotToString(snapshot: ITextSnapshot): string {
return chunks.join('');
}
export class TextSnapshotReadable implements VSBufferReadable {
private preambleHandled: boolean;
constructor(private snapshot: ITextSnapshot, private preamble?: string) { }
read(): VSBuffer | null {
let value = this.snapshot.read();
// Handle preamble if provided
if (!this.preambleHandled) {
this.preambleHandled = true;
if (typeof this.preamble === 'string') {
if (typeof value === 'string') {
value = this.preamble + value;
} else {
value = this.preamble;
}
}
}
if (typeof value === 'string') {
return VSBuffer.fromString(value);
}
return null;
}
}
/**
* Streamable content and meta information of a file.
*/
@@ -724,7 +753,20 @@ export interface IResolveContentOptions {
position?: number;
}
export interface IUpdateContentOptions {
export interface IWriteFileOptions {
/**
* The last known modification time of the file. This can be used to prevent dirty writes.
*/
mtime?: number;
/**
* The etag of the file. This can be used to prevent dirty writes.
*/
etag?: string;
}
export interface IWriteTextFileOptions extends IWriteFileOptions {
/**
* The encoding to use when updating a file.
@@ -746,21 +788,6 @@ export interface IUpdateContentOptions {
* ask the user to authenticate as super user.
*/
writeElevated?: boolean;
/**
* The last known modification time of the file. This can be used to prevent dirty writes.
*/
mtime?: number;
/**
* The etag of the file. This can be used to prevent dirty writes.
*/
etag?: string;
/**
* Run mkdirp before saving.
*/
mkdirp?: boolean;
}
export interface IResolveFileOptions {
@@ -797,7 +824,7 @@ export interface ICreateFileOptions {
}
export class FileOperationError extends Error {
constructor(message: string, public fileOperationResult: FileOperationResult, public options?: IResolveContentOptions & IUpdateContentOptions & ICreateFileOptions) {
constructor(message: string, public fileOperationResult: FileOperationResult, public options?: IResolveContentOptions & IWriteTextFileOptions & ICreateFileOptions) {
super(message);
}
@@ -1132,7 +1159,7 @@ export interface ILegacyFileService extends IDisposable {
resolveStreamContent(resource: URI, options?: IResolveContentOptions): Promise<IStreamContent>;
updateContent(resource: URI, value: string | ITextSnapshot, options?: IUpdateContentOptions): Promise<IFileStatWithMetadata>;
updateContent(resource: URI, value: string | ITextSnapshot, options?: IWriteTextFileOptions): Promise<IFileStatWithMetadata>;
createFile(resource: URI, content?: string, options?: ICreateFileOptions): Promise<IFileStatWithMetadata>;
}

View File

@@ -33,7 +33,7 @@ export class SharedProcessService implements ISharedProcessService {
@IEnvironmentService environmentService: IEnvironmentService
) {
this.withSharedProcessConnection = windowsService.whenSharedProcessReady()
.then(() => connect(environmentService.sharedIPCHandle, `window:${windowService.getCurrentWindowId()}`));
.then(() => connect(environmentService.sharedIPCHandle, `window:${windowService.windowId}`));
}
getChannel(channelName: string): IChannel {

View File

@@ -15,6 +15,8 @@ import { isMacintosh, IProcessEnvironment } from 'vs/base/common/platform';
import { ILogService } from 'vs/platform/log/common/log';
import { IWindowsService } from 'vs/platform/windows/common/windows';
import { IWindowState } from 'vs/platform/windows/electron-main/windows';
import { listProcesses } from 'vs/base/node/ps';
import { isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnosticsService';
const DEFAULT_BACKGROUND_COLOR = '#1E1E1E';
@@ -44,6 +46,35 @@ export class IssueService implements IIssueService {
});
});
ipcMain.on('vscode:listProcesses', async (event: Event) => {
const processes = [];
try {
const mainPid = await this.launchService.getMainProcessId();
processes.push({ name: localize('local', "Local"), rootProcess: await listProcesses(mainPid) });
(await this.launchService.getRemoteDiagnostics({ includeProcesses: true }))
.forEach(data => {
if (isRemoteDiagnosticError(data)) {
processes.push({
name: data.hostName,
rootProcess: data
});
} else {
if (data.processes) {
processes.push({
name: data.hostName,
rootProcess: data.processes
});
}
}
});
} catch (e) {
this.logService.error(`Listing processes failed: ${e}`);
}
event.sender.send('vscode:listProcessesResponse', processes);
});
ipcMain.on('vscode:issuePerformanceInfoRequest', (event: Event) => {
this.getPerformanceInfo().then(msg => {
event.sender.send('vscode:issuePerformanceInfoResponse', msg);

View File

@@ -19,7 +19,7 @@ import { BrowserWindow, ipcMain, Event as IpcEvent } from 'electron';
import { Event } from 'vs/base/common/event';
import { hasArgs } from 'vs/platform/environment/node/argv';
import { coalesce } from 'vs/base/common/arrays';
import { IDiagnosticInfoOptions, IDiagnosticInfo, IRemoteDiagnosticInfo } from 'vs/platform/diagnostics/common/diagnosticsService';
import { IDiagnosticInfoOptions, IDiagnosticInfo, IRemoteDiagnosticInfo, IRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnosticsService';
export const ID = 'launchService';
export const ILaunchService = createDecorator<ILaunchService>(ID);
@@ -71,7 +71,7 @@ export interface ILaunchService {
getMainProcessId(): Promise<number>;
getMainProcessInfo(): Promise<IMainProcessInfo>;
getLogsPath(): Promise<string>;
getRemoteDiagnostics(options: IRemoteDiagnosticOptions): Promise<IRemoteDiagnosticInfo[]>;
getRemoteDiagnostics(options: IRemoteDiagnosticOptions): Promise<(IRemoteDiagnosticInfo | IRemoteDiagnosticError)[]>;
}
export class LaunchChannel implements IServerChannel {
@@ -299,9 +299,9 @@ export class LaunchService implements ILaunchService {
return Promise.resolve(this.environmentService.logsPath);
}
getRemoteDiagnostics(options: IRemoteDiagnosticOptions): Promise<IRemoteDiagnosticInfo[]> {
getRemoteDiagnostics(options: IRemoteDiagnosticOptions): Promise<(IRemoteDiagnosticInfo | IRemoteDiagnosticError)[]> {
const windows = this.windowsMainService.getWindows();
const promises: Promise<IDiagnosticInfo | undefined>[] = windows.map(window => {
const promises: Promise<IDiagnosticInfo | IRemoteDiagnosticError | undefined>[] = windows.map(window => {
return new Promise((resolve, reject) => {
if (window.remoteAuthority) {
const replyChannel = `vscode:getDiagnosticInfoResponse${window.id}`;
@@ -315,18 +315,14 @@ export class LaunchService implements ILaunchService {
ipcMain.once(replyChannel, (_: IpcEvent, data: IRemoteDiagnosticInfo) => {
// No data is returned if getting the connection fails.
if (!data) {
resolve();
}
if (typeof (data) === 'string') {
reject(new Error(data));
resolve({ hostName: window.remoteAuthority!, errorMessage: `Unable to resolve connection to '${window.remoteAuthority}'.` });
}
resolve(data);
});
setTimeout(() => {
resolve();
resolve({ hostName: window.remoteAuthority!, errorMessage: `Fetching remote diagnostics for '${window.remoteAuthority}' timed out.` });
}, 5000);
} else {
resolve();
@@ -334,7 +330,7 @@ export class LaunchService implements ILaunchService {
});
});
return Promise.all(promises).then(diagnostics => diagnostics.filter((x): x is IRemoteDiagnosticInfo => !!x));
return Promise.all(promises).then(diagnostics => diagnostics.filter((x): x is IRemoteDiagnosticInfo | IRemoteDiagnosticError => !!x));
}
private getFolderURIs(window: ICodeWindow): URI[] {

View File

@@ -53,7 +53,7 @@ export class LifecycleService extends AbstractLifecycleService {
}
private registerListeners(): void {
const windowId = this.windowService.getCurrentWindowId();
const windowId = this.windowService.windowId;
// Main side indicates that window is about to unload, check for vetos
ipc.on('vscode:onBeforeUnload', (_event: unknown, reply: { okChannel: string, cancelChannel: string, reason: ShutdownReason }) => {

View File

@@ -29,7 +29,7 @@ import { ObjectTree, IObjectTreeOptions } from 'vs/base/browser/ui/tree/objectTr
import { ITreeEvent, ITreeRenderer, IAsyncDataSource, IDataSource, ITreeMouseEvent } from 'vs/base/browser/ui/tree/tree';
import { AsyncDataTree, IAsyncDataTreeOptions } from 'vs/base/browser/ui/tree/asyncDataTree';
import { DataTree, IDataTreeOptions } from 'vs/base/browser/ui/tree/dataTree';
import { IKeyboardNavigationEventFilter } from 'vs/base/browser/ui/tree/abstractTree';
import { IKeyboardNavigationEventFilter, IAbstractTreeOptions } from 'vs/base/browser/ui/tree/abstractTree';
import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
export type ListWidget = List<any> | PagedList<any> | ITree | ObjectTree<any, any> | DataTree<any, any, any> | AsyncDataTree<any, any, any>;
@@ -659,8 +659,9 @@ export interface IOpenEvent<T> {
browserEvent?: UIEvent;
}
export interface IResourceResultsNavigationOptions {
openOnFocus: boolean;
export interface IResourceResultsNavigationOptions2 {
openOnFocus?: boolean;
openOnSelection?: boolean;
}
export interface SelectionKeyboardEvent extends KeyboardEvent {
@@ -676,14 +677,24 @@ export function getSelectionKeyboardEvent(typeArg = 'keydown', preserveFocus?: b
export class TreeResourceNavigator2<T, TFilterData> extends Disposable {
private options: IResourceResultsNavigationOptions2;
private readonly _onDidOpenResource = new Emitter<IOpenEvent<T | null>>();
readonly onDidOpenResource: Event<IOpenEvent<T | null>> = this._onDidOpenResource.event;
constructor(
private tree: WorkbenchObjectTree<T, TFilterData> | WorkbenchDataTree<any, T, TFilterData> | WorkbenchAsyncDataTree<any, T, TFilterData>,
private options?: IResourceResultsNavigationOptions
options?: IResourceResultsNavigationOptions2
) {
super();
this.options = {
...<IResourceResultsNavigationOptions2>{
openOnSelection: true
},
...(options || {})
};
this.registerListeners();
}
@@ -692,7 +703,10 @@ export class TreeResourceNavigator2<T, TFilterData> extends Disposable {
this._register(this.tree.onDidChangeFocus(e => this.onFocus(e)));
}
this._register(this.tree.onDidChangeSelection(e => this.onSelection(e)));
if (this.options && this.options.openOnSelection) {
this._register(this.tree.onDidChangeSelection(e => this.onSelection(e)));
}
this._register(this.tree.onDidOpen(e => this.onSelection(e)));
}
@@ -766,15 +780,9 @@ function createKeyboardNavigationEventFilter(container: HTMLElement, keybindingS
export class WorkbenchObjectTree<T extends NonNullable<any>, TFilterData = void> extends ObjectTree<T, TFilterData> {
readonly contextKeyService: IContextKeyService;
protected disposables: IDisposable[];
private hasSelectionOrFocus: IContextKey<boolean>;
private hasDoubleSelection: IContextKey<boolean>;
private hasMultiSelection: IContextKey<boolean>;
private _useAltAsMultipleSelectionModifier: boolean;
private internals: WorkbenchTreeInternals<any, T, TFilterData>;
get contextKeyService(): IContextKeyService { return this.internals.contextKeyService; }
get useAltAsMultipleSelectionModifier(): boolean { return this.internals.useAltAsMultipleSelectionModifier; }
constructor(
container: HTMLElement,
@@ -788,132 +796,19 @@ export class WorkbenchObjectTree<T extends NonNullable<any>, TFilterData = void>
@IKeybindingService keybindingService: IKeybindingService,
@IAccessibilityService accessibilityService: IAccessibilityService
) {
WorkbenchListSupportsKeyboardNavigation.bindTo(contextKeyService);
if (!didBindWorkbenchListAutomaticKeyboardNavigation) {
WorkbenchListAutomaticKeyboardNavigation.bindTo(contextKeyService);
didBindWorkbenchListAutomaticKeyboardNavigation = true;
}
const getAutomaticKeyboardNavigation = () => {
// give priority to the context key value to disable this completely
let automaticKeyboardNavigation = contextKeyService.getContextKeyValue<boolean>(WorkbenchListAutomaticKeyboardNavigationKey);
if (automaticKeyboardNavigation) {
automaticKeyboardNavigation = configurationService.getValue<boolean>(automaticKeyboardNavigationSettingKey);
}
return automaticKeyboardNavigation;
};
const accessibilityOn = accessibilityService.getAccessibilitySupport() === AccessibilitySupport.Enabled;
const keyboardNavigation = accessibilityOn ? 'simple' : configurationService.getValue<string>(keyboardNavigationSettingKey);
const horizontalScrolling = typeof options.horizontalScrolling !== 'undefined' ? options.horizontalScrolling : getHorizontalScrollingSetting(configurationService);
const openOnSingleClick = useSingleClickToOpen(configurationService);
const [workbenchListOptions, workbenchListOptionsDisposable] = toWorkbenchListOptions(options, configurationService, keybindingService);
super(container, delegate, renderers, {
keyboardSupport: false,
styleController: new DefaultStyleController(getSharedListStyleSheet()),
...computeStyles(themeService.getTheme(), defaultListStyles),
...workbenchListOptions,
indent: configurationService.getValue(treeIndentKey),
automaticKeyboardNavigation: getAutomaticKeyboardNavigation(),
simpleKeyboardNavigation: keyboardNavigation === 'simple',
filterOnType: keyboardNavigation === 'filter',
horizontalScrolling,
openOnSingleClick,
keyboardNavigationEventFilter: createKeyboardNavigationEventFilter(container, keybindingService)
});
this.disposables.push(workbenchListOptionsDisposable);
this.contextKeyService = createScopedContextKeyService(contextKeyService, this);
const listSupportsMultiSelect = WorkbenchListSupportsMultiSelectContextKey.bindTo(this.contextKeyService);
listSupportsMultiSelect.set(!(options.multipleSelectionSupport === false));
this.hasSelectionOrFocus = WorkbenchListHasSelectionOrFocus.bindTo(this.contextKeyService);
this.hasDoubleSelection = WorkbenchListDoubleSelection.bindTo(this.contextKeyService);
this.hasMultiSelection = WorkbenchListMultiSelection.bindTo(this.contextKeyService);
this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService);
const interestingContextKeys = new Set();
interestingContextKeys.add(WorkbenchListAutomaticKeyboardNavigationKey);
const updateKeyboardNavigation = () => {
const accessibilityOn = accessibilityService.getAccessibilitySupport() === AccessibilitySupport.Enabled;
const keyboardNavigation = accessibilityOn ? 'simple' : configurationService.getValue<string>(keyboardNavigationSettingKey);
this.updateOptions({
simpleKeyboardNavigation: keyboardNavigation === 'simple',
filterOnType: keyboardNavigation === 'filter'
});
};
this.disposables.push(
this.contextKeyService,
(listService as ListService).register(this),
attachListStyler(this, themeService),
this.onDidChangeSelection(() => {
const selection = this.getSelection();
const focus = this.getFocus();
this.hasSelectionOrFocus.set(selection.length > 0 || focus.length > 0);
this.hasMultiSelection.set(selection.length > 1);
this.hasDoubleSelection.set(selection.length === 2);
}),
this.onDidChangeFocus(() => {
const selection = this.getSelection();
const focus = this.getFocus();
this.hasSelectionOrFocus.set(selection.length > 0 || focus.length > 0);
}),
configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration(openModeSettingKey)) {
this.updateOptions({ openOnSingleClick: useSingleClickToOpen(configurationService) });
}
if (e.affectsConfiguration(multiSelectModifierSettingKey)) {
this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService);
}
if (e.affectsConfiguration(treeIndentKey)) {
const indent = configurationService.getValue<number>(treeIndentKey);
this.updateOptions({ indent });
}
if (e.affectsConfiguration(keyboardNavigationSettingKey)) {
updateKeyboardNavigation();
}
if (e.affectsConfiguration(automaticKeyboardNavigationSettingKey)) {
this.updateOptions({ automaticKeyboardNavigation: getAutomaticKeyboardNavigation() });
}
}),
this.contextKeyService.onDidChangeContext(e => {
if (e.affectsSome(interestingContextKeys)) {
this.updateOptions({ automaticKeyboardNavigation: getAutomaticKeyboardNavigation() });
}
}),
accessibilityService.onDidChangeAccessibilitySupport(() => updateKeyboardNavigation())
);
}
get useAltAsMultipleSelectionModifier(): boolean {
return this._useAltAsMultipleSelectionModifier;
}
dispose(): void {
super.dispose();
this.disposables = dispose(this.disposables);
const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble(container, options, contextKeyService, themeService, configurationService, keybindingService, accessibilityService);
super(container, delegate, renderers, treeOptions);
this.disposables.push(disposable);
this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, contextKeyService, listService, themeService, configurationService, accessibilityService);
this.disposables.push(this.internals);
}
}
export class WorkbenchDataTree<TInput, T, TFilterData = void> extends DataTree<TInput, T, TFilterData> {
readonly contextKeyService: IContextKeyService;
private hasSelectionOrFocus: IContextKey<boolean>;
private hasDoubleSelection: IContextKey<boolean>;
private hasMultiSelection: IContextKey<boolean>;
private _useAltAsMultipleSelectionModifier: boolean;
private internals: WorkbenchTreeInternals<TInput, T, TFilterData>;
get contextKeyService(): IContextKeyService { return this.internals.contextKeyService; }
get useAltAsMultipleSelectionModifier(): boolean { return this.internals.useAltAsMultipleSelectionModifier; }
constructor(
container: HTMLElement,
@@ -928,127 +823,19 @@ export class WorkbenchDataTree<TInput, T, TFilterData = void> extends DataTree<T
@IKeybindingService keybindingService: IKeybindingService,
@IAccessibilityService accessibilityService: IAccessibilityService
) {
WorkbenchListSupportsKeyboardNavigation.bindTo(contextKeyService);
if (!didBindWorkbenchListAutomaticKeyboardNavigation) {
WorkbenchListAutomaticKeyboardNavigation.bindTo(contextKeyService);
didBindWorkbenchListAutomaticKeyboardNavigation = true;
}
const getAutomaticKeyboardNavigation = () => {
// give priority to the context key value to disable this completely
let automaticKeyboardNavigation = contextKeyService.getContextKeyValue<boolean>(WorkbenchListAutomaticKeyboardNavigationKey);
if (automaticKeyboardNavigation) {
automaticKeyboardNavigation = configurationService.getValue<boolean>(automaticKeyboardNavigationSettingKey);
}
return automaticKeyboardNavigation;
};
const accessibilityOn = accessibilityService.getAccessibilitySupport() === AccessibilitySupport.Enabled;
const keyboardNavigation = accessibilityOn ? 'simple' : configurationService.getValue<string>(keyboardNavigationSettingKey);
const horizontalScrolling = typeof options.horizontalScrolling !== 'undefined' ? options.horizontalScrolling : getHorizontalScrollingSetting(configurationService);
const openOnSingleClick = useSingleClickToOpen(configurationService);
const [workbenchListOptions, workbenchListOptionsDisposable] = toWorkbenchListOptions(options, configurationService, keybindingService);
super(container, delegate, renderers, dataSource, {
keyboardSupport: false,
styleController: new DefaultStyleController(getSharedListStyleSheet()),
...computeStyles(themeService.getTheme(), defaultListStyles),
...workbenchListOptions,
indent: configurationService.getValue(treeIndentKey),
automaticKeyboardNavigation: getAutomaticKeyboardNavigation(),
simpleKeyboardNavigation: keyboardNavigation === 'simple',
filterOnType: keyboardNavigation === 'filter',
horizontalScrolling,
openOnSingleClick,
keyboardNavigationEventFilter: createKeyboardNavigationEventFilter(container, keybindingService)
});
this.disposables.push(workbenchListOptionsDisposable);
this.contextKeyService = createScopedContextKeyService(contextKeyService, this);
const listSupportsMultiSelect = WorkbenchListSupportsMultiSelectContextKey.bindTo(this.contextKeyService);
listSupportsMultiSelect.set(!(options.multipleSelectionSupport === false));
this.hasSelectionOrFocus = WorkbenchListHasSelectionOrFocus.bindTo(this.contextKeyService);
this.hasDoubleSelection = WorkbenchListDoubleSelection.bindTo(this.contextKeyService);
this.hasMultiSelection = WorkbenchListMultiSelection.bindTo(this.contextKeyService);
this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService);
const interestingContextKeys = new Set();
interestingContextKeys.add(WorkbenchListAutomaticKeyboardNavigationKey);
const updateKeyboardNavigation = () => {
const accessibilityOn = accessibilityService.getAccessibilitySupport() === AccessibilitySupport.Enabled;
const keyboardNavigation = accessibilityOn ? 'simple' : configurationService.getValue<string>(keyboardNavigationSettingKey);
this.updateOptions({
simpleKeyboardNavigation: keyboardNavigation === 'simple',
filterOnType: keyboardNavigation === 'filter'
});
};
this.disposables.push(
this.contextKeyService,
(listService as ListService).register(this),
attachListStyler(this, themeService),
this.onDidChangeSelection(() => {
const selection = this.getSelection();
const focus = this.getFocus();
this.hasSelectionOrFocus.set(selection.length > 0 || focus.length > 0);
this.hasMultiSelection.set(selection.length > 1);
this.hasDoubleSelection.set(selection.length === 2);
}),
this.onDidChangeFocus(() => {
const selection = this.getSelection();
const focus = this.getFocus();
this.hasSelectionOrFocus.set(selection.length > 0 || focus.length > 0);
}),
configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration(openModeSettingKey)) {
this.updateOptions({ openOnSingleClick: useSingleClickToOpen(configurationService) });
}
if (e.affectsConfiguration(multiSelectModifierSettingKey)) {
this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService);
}
if (e.affectsConfiguration(treeIndentKey)) {
const indent = configurationService.getValue<number>(treeIndentKey);
this.updateOptions({ indent });
}
if (e.affectsConfiguration(keyboardNavigationSettingKey)) {
updateKeyboardNavigation();
}
if (e.affectsConfiguration(automaticKeyboardNavigationSettingKey)) {
this.updateOptions({ automaticKeyboardNavigation: getAutomaticKeyboardNavigation() });
}
}),
this.contextKeyService.onDidChangeContext(e => {
if (e.affectsSome(interestingContextKeys)) {
this.updateOptions({ automaticKeyboardNavigation: getAutomaticKeyboardNavigation() });
}
}),
accessibilityService.onDidChangeAccessibilitySupport(() => updateKeyboardNavigation())
);
}
get useAltAsMultipleSelectionModifier(): boolean {
return this._useAltAsMultipleSelectionModifier;
const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble(container, options, contextKeyService, themeService, configurationService, keybindingService, accessibilityService);
super(container, delegate, renderers, dataSource, treeOptions);
this.disposables.push(disposable);
this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, contextKeyService, listService, themeService, configurationService, accessibilityService);
this.disposables.push(this.internals);
}
}
export class WorkbenchAsyncDataTree<TInput, T, TFilterData = void> extends AsyncDataTree<TInput, T, TFilterData> {
readonly contextKeyService: IContextKeyService;
private hasSelectionOrFocus: IContextKey<boolean>;
private hasDoubleSelection: IContextKey<boolean>;
private hasMultiSelection: IContextKey<boolean>;
private _useAltAsMultipleSelectionModifier: boolean;
private internals: WorkbenchTreeInternals<TInput, T, TFilterData>;
get contextKeyService(): IContextKeyService { return this.internals.contextKeyService; }
get useAltAsMultipleSelectionModifier(): boolean { return this.internals.useAltAsMultipleSelectionModifier; }
constructor(
container: HTMLElement,
@@ -1063,31 +850,51 @@ export class WorkbenchAsyncDataTree<TInput, T, TFilterData = void> extends Async
@IKeybindingService keybindingService: IKeybindingService,
@IAccessibilityService accessibilityService: IAccessibilityService
) {
WorkbenchListSupportsKeyboardNavigation.bindTo(contextKeyService);
const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble(container, options, contextKeyService, themeService, configurationService, keybindingService, accessibilityService);
super(container, delegate, renderers, dataSource, treeOptions);
this.disposables.push(disposable);
this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, contextKeyService, listService, themeService, configurationService, accessibilityService);
this.disposables.push(this.internals);
}
}
if (!didBindWorkbenchListAutomaticKeyboardNavigation) {
WorkbenchListAutomaticKeyboardNavigation.bindTo(contextKeyService);
didBindWorkbenchListAutomaticKeyboardNavigation = true;
function workbenchTreeDataPreamble<T, TFilterData>(
container: HTMLElement,
options: IAbstractTreeOptions<T, TFilterData>,
contextKeyService: IContextKeyService,
themeService: IThemeService,
configurationService: IConfigurationService,
keybindingService: IKeybindingService,
accessibilityService: IAccessibilityService,
): { options: IAbstractTreeOptions<T, TFilterData>, getAutomaticKeyboardNavigation: () => boolean | undefined, disposable: IDisposable } {
WorkbenchListSupportsKeyboardNavigation.bindTo(contextKeyService);
if (!didBindWorkbenchListAutomaticKeyboardNavigation) {
WorkbenchListAutomaticKeyboardNavigation.bindTo(contextKeyService);
didBindWorkbenchListAutomaticKeyboardNavigation = true;
}
const getAutomaticKeyboardNavigation = () => {
// give priority to the context key value to disable this completely
let automaticKeyboardNavigation = contextKeyService.getContextKeyValue<boolean>(WorkbenchListAutomaticKeyboardNavigationKey);
if (automaticKeyboardNavigation) {
automaticKeyboardNavigation = configurationService.getValue<boolean>(automaticKeyboardNavigationSettingKey);
}
const getAutomaticKeyboardNavigation = () => {
// give priority to the context key value to disable this completely
let automaticKeyboardNavigation = contextKeyService.getContextKeyValue<boolean>(WorkbenchListAutomaticKeyboardNavigationKey);
return automaticKeyboardNavigation;
};
if (automaticKeyboardNavigation) {
automaticKeyboardNavigation = configurationService.getValue<boolean>(automaticKeyboardNavigationSettingKey);
}
const accessibilityOn = accessibilityService.getAccessibilitySupport() === AccessibilitySupport.Enabled;
const keyboardNavigation = accessibilityOn ? 'simple' : configurationService.getValue<string>(keyboardNavigationSettingKey);
const horizontalScrolling = typeof options.horizontalScrolling !== 'undefined' ? options.horizontalScrolling : getHorizontalScrollingSetting(configurationService);
const openOnSingleClick = useSingleClickToOpen(configurationService);
const [workbenchListOptions, disposable] = toWorkbenchListOptions(options, configurationService, keybindingService);
return automaticKeyboardNavigation;
};
const accessibilityOn = accessibilityService.getAccessibilitySupport() === AccessibilitySupport.Enabled;
const keyboardNavigation = accessibilityOn ? 'simple' : configurationService.getValue<string>(keyboardNavigationSettingKey);
const horizontalScrolling = typeof options.horizontalScrolling !== 'undefined' ? options.horizontalScrolling : getHorizontalScrollingSetting(configurationService);
const openOnSingleClick = useSingleClickToOpen(configurationService);
const [workbenchListOptions, workbenchListOptionsDisposable] = toWorkbenchListOptions(options, configurationService, keybindingService);
super(container, delegate, renderers, dataSource, {
return {
getAutomaticKeyboardNavigation,
disposable,
options: {
keyboardSupport: false,
styleController: new DefaultStyleController(getSharedListStyleSheet()),
...computeStyles(themeService.getTheme(), defaultListStyles),
@@ -1099,11 +906,30 @@ export class WorkbenchAsyncDataTree<TInput, T, TFilterData = void> extends Async
horizontalScrolling,
openOnSingleClick,
keyboardNavigationEventFilter: createKeyboardNavigationEventFilter(container, keybindingService)
});
}
};
}
this.disposables.push(workbenchListOptionsDisposable);
class WorkbenchTreeInternals<TInput, T, TFilterData> {
this.contextKeyService = createScopedContextKeyService(contextKeyService, this);
readonly contextKeyService: IContextKeyService;
private hasSelectionOrFocus: IContextKey<boolean>;
private hasDoubleSelection: IContextKey<boolean>;
private hasMultiSelection: IContextKey<boolean>;
private _useAltAsMultipleSelectionModifier: boolean;
private disposables: IDisposable[] = [];
constructor(
tree: WorkbenchObjectTree<T, TFilterData> | WorkbenchDataTree<TInput, T, TFilterData> | WorkbenchAsyncDataTree<TInput, T, TFilterData>,
options: IAbstractTreeOptions<T, TFilterData>,
getAutomaticKeyboardNavigation: () => boolean | undefined,
@IContextKeyService contextKeyService: IContextKeyService,
@IListService listService: IListService,
@IThemeService themeService: IThemeService,
@IConfigurationService configurationService: IConfigurationService,
@IAccessibilityService accessibilityService: IAccessibilityService,
) {
this.contextKeyService = createScopedContextKeyService(contextKeyService, tree);
const listSupportsMultiSelect = WorkbenchListSupportsMultiSelectContextKey.bindTo(this.contextKeyService);
listSupportsMultiSelect.set(!(options.multipleSelectionSupport === false));
@@ -1119,7 +945,7 @@ export class WorkbenchAsyncDataTree<TInput, T, TFilterData = void> extends Async
const updateKeyboardNavigation = () => {
const accessibilityOn = accessibilityService.getAccessibilitySupport() === AccessibilitySupport.Enabled;
const keyboardNavigation = accessibilityOn ? 'simple' : configurationService.getValue<string>(keyboardNavigationSettingKey);
this.updateOptions({
tree.updateOptions({
simpleKeyboardNavigation: keyboardNavigation === 'simple',
filterOnType: keyboardNavigation === 'filter'
});
@@ -1127,43 +953,43 @@ export class WorkbenchAsyncDataTree<TInput, T, TFilterData = void> extends Async
this.disposables.push(
this.contextKeyService,
(listService as ListService).register(this),
attachListStyler(this, themeService),
this.onDidChangeSelection(() => {
const selection = this.getSelection();
const focus = this.getFocus();
(listService as ListService).register(tree),
attachListStyler(tree, themeService),
tree.onDidChangeSelection(() => {
const selection = tree.getSelection();
const focus = tree.getFocus();
this.hasSelectionOrFocus.set(selection.length > 0 || focus.length > 0);
this.hasMultiSelection.set(selection.length > 1);
this.hasDoubleSelection.set(selection.length === 2);
}),
this.onDidChangeFocus(() => {
const selection = this.getSelection();
const focus = this.getFocus();
tree.onDidChangeFocus(() => {
const selection = tree.getSelection();
const focus = tree.getFocus();
this.hasSelectionOrFocus.set(selection.length > 0 || focus.length > 0);
}),
configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration(openModeSettingKey)) {
this.updateOptions({ openOnSingleClick: useSingleClickToOpen(configurationService) });
tree.updateOptions({ openOnSingleClick: useSingleClickToOpen(configurationService) });
}
if (e.affectsConfiguration(multiSelectModifierSettingKey)) {
this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService);
}
if (e.affectsConfiguration(treeIndentKey)) {
const indent = configurationService.getValue<number>(treeIndentKey);
this.updateOptions({ indent });
tree.updateOptions({ indent });
}
if (e.affectsConfiguration(keyboardNavigationSettingKey)) {
updateKeyboardNavigation();
}
if (e.affectsConfiguration(automaticKeyboardNavigationSettingKey)) {
this.updateOptions({ automaticKeyboardNavigation: getAutomaticKeyboardNavigation() });
tree.updateOptions({ automaticKeyboardNavigation: getAutomaticKeyboardNavigation() });
}
}),
this.contextKeyService.onDidChangeContext(e => {
if (e.affectsSome(interestingContextKeys)) {
this.updateOptions({ automaticKeyboardNavigation: getAutomaticKeyboardNavigation() });
tree.updateOptions({ automaticKeyboardNavigation: getAutomaticKeyboardNavigation() });
}
}),
accessibilityService.onDidChangeAccessibilitySupport(() => updateKeyboardNavigation())
@@ -1173,6 +999,10 @@ export class WorkbenchAsyncDataTree<TInput, T, TFilterData = void> extends Async
get useAltAsMultipleSelectionModifier(): boolean {
return this._useAltAsMultipleSelectionModifier;
}
dispose(): void {
this.disposables = dispose(this.disposables);
}
}
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);

View File

@@ -11,7 +11,7 @@ export interface IRemoteAgentEnvironment {
pid: number;
appRoot: URI;
appSettingsHome: URI;
appSettingsPath: URI;
settingsPath: URI;
logsPath: URI;
extensionsPath: URI;
extensionHostLogsPath: URI;

View File

@@ -4,8 +4,9 @@
*--------------------------------------------------------------------------------------------*/
import { URI } from 'vs/base/common/uri';
import { Schemas } from 'vs/base/common/network';
export const REMOTE_HOST_SCHEME = 'vscode-remote';
export const REMOTE_HOST_SCHEME = Schemas.vscodeRemote;
export function getRemoteAuthority(uri: URI): string | undefined {
return uri.scheme === REMOTE_HOST_SCHEME ? uri.authority : undefined;

View File

@@ -0,0 +1,21 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export const ITunnelService = createDecorator<ITunnelService>('tunnelService');
export interface RemoteTunnel {
readonly tunnelRemotePort: number;
readonly tunnelLocalPort: number;
dispose(): void;
}
export interface ITunnelService {
_serviceBrand: any;
openTunnel(remotePort: number): Promise<RemoteTunnel> | undefined;
}

View File

@@ -3,10 +3,42 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as net from 'net';
import { NodeSocket } from 'vs/base/parts/ipc/node/ipc.net';
import { IWebSocketFactory, IConnectCallback } from 'vs/platform/remote/common/remoteAgentConnection';
export const nodeWebSocketFactory = new class implements IWebSocketFactory {
connect(host: string, port: number, query: string, callback: IConnectCallback): void {
throw new Error(`Not implemented`);
const errorListener = (err: any) => callback(err, undefined);
const socket = net.createConnection({ host: host, port: port }, () => {
socket.removeListener('error', errorListener);
// https://tools.ietf.org/html/rfc6455#section-4
const buffer = Buffer.alloc(16);
for (let i = 0; i < 16; i++) {
buffer[i] = Math.round(Math.random() * 256);
}
const nonce = buffer.toString('base64');
let headers = [
`GET ws://${host}:${port}/?${query}&skipWebSocketFrames=true HTTP/1.1`,
`Connection: Upgrade`,
`Upgrade: websocket`,
`Sec-WebSocket-Key: ${nonce}`
];
socket.write(headers.join('\r\n') + '\r\n\r\n');
const onData = (data: Buffer) => {
const strData = data.toString();
if (strData.indexOf('\r\n\r\n') >= 0) {
// headers received OK
socket.off('data', onData);
callback(undefined, new NodeSocket(socket));
}
};
socket.on('data', onData);
});
socket.once('error', errorListener);
}
};

View File

@@ -0,0 +1,21 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
export class TunnelService implements ITunnelService {
_serviceBrand: any;
public constructor(
) {
}
openTunnel(remotePort: number): Promise<RemoteTunnel> | undefined {
return undefined;
}
}
registerSingleton(ITunnelService, TunnelService);

View File

@@ -67,13 +67,21 @@ export interface IStatusbarService {
_serviceBrand: any;
/**
* Adds an entry to the statusbar with the given alignment and priority. Use the returned IDisposable
* to remove the statusbar entry.
* Adds an entry to the statusbar with the given alignment and priority. Use the returned accessor
* to update or remove the statusbar entry.
*/
addEntry(entry: IStatusbarEntry, alignment: StatusbarAlignment, priority?: number): IDisposable;
addEntry(entry: IStatusbarEntry, alignment: StatusbarAlignment, priority?: number): IStatusbarEntryAccessor;
/**
* Prints something to the status bar area with optional auto dispose and delay.
*/
setStatusMessage(message: string, autoDisposeAfter?: number, delayBy?: number): IDisposable;
}
export interface IStatusbarEntryAccessor extends IDisposable {
/**
* Allows to update an existing status bar entry.
*/
update(properties: IStatusbarEntry): void;
}

View File

@@ -226,8 +226,8 @@ export interface IWindowService {
readonly hasFocus: boolean;
getConfiguration(): IWindowConfiguration;
getCurrentWindowId(): number;
readonly windowId: number;
pickFileFolderAndOpen(options: INativeOpenDialogOptions): Promise<void>;
pickFileAndOpen(options: INativeOpenDialogOptions): Promise<void>;
pickFolderAndOpen(options: INativeOpenDialogOptions): Promise<void>;
@@ -459,7 +459,7 @@ export class ActiveWindowManager implements IDisposable {
this.firstActiveWindowIdPromise = createCancelablePromise(_ => windowsService.getActiveWindowId());
this.firstActiveWindowIdPromise
.then(id => this.activeWindowId = id)
.then(id => this.activeWindowId = typeof this.activeWindowId === 'number' ? this.activeWindowId : id)
.finally(this.firstActiveWindowIdPromise = undefined);
}

View File

@@ -812,28 +812,28 @@ declare module 'vscode' {
/**
* The id of the comment
*/
commentId: string;
readonly commentId: string;
/**
* The text of the comment
*/
body: MarkdownString;
readonly body: MarkdownString;
/**
* Optional label describing the [Comment](#Comment)
* Label will be rendered next to userName if exists.
*/
label?: string;
readonly label?: string;
/**
* The display name of the user who created the comment
*/
userName: string;
readonly userName: string;
/**
* The icon path for the user who created the comment
*/
userIconPath?: Uri;
readonly userIconPath?: Uri;
/**
* @deprecated Use userIconPath instead. The avatar src of the user who created the comment
@@ -869,17 +869,17 @@ declare module 'vscode' {
/**
* The command to be executed if the comment is selected in the Comments Panel
*/
selectCommand?: Command;
readonly selectCommand?: Command;
/**
* The command to be executed when users try to save the edits to the comment
*/
editCommand?: Command;
readonly editCommand?: Command;
/**
* The command to be executed when users try to delete the comment
*/
deleteCommand?: Command;
readonly deleteCommand?: Command;
/**
* Deprecated

View File

@@ -17,6 +17,8 @@ import { ExtHostContext, ExtHostDocumentsShape, IExtHostContext, MainThreadDocum
import { ITextEditorModel } from 'vs/workbench/common/editor';
import { ITextFileService, TextFileModelChangeEvent } from 'vs/workbench/services/textfile/common/textfiles';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { toLocalResource } from 'vs/base/common/resources';
export class BoundModelReferenceCollection {
@@ -69,6 +71,7 @@ export class MainThreadDocuments implements MainThreadDocumentsShape {
private readonly _textFileService: ITextFileService;
private readonly _fileService: IFileService;
private readonly _untitledEditorService: IUntitledEditorService;
private readonly _environmentService: IWorkbenchEnvironmentService;
private _toDispose: IDisposable[];
private _modelToDisposeMap: { [modelUrl: string]: IDisposable; };
@@ -85,12 +88,14 @@ export class MainThreadDocuments implements MainThreadDocumentsShape {
@IFileService fileService: IFileService,
@ITextModelService textModelResolverService: ITextModelService,
@IUntitledEditorService untitledEditorService: IUntitledEditorService,
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService
) {
this._modelService = modelService;
this._textModelResolverService = textModelResolverService;
this._textFileService = textFileService;
this._fileService = fileService;
this._untitledEditorService = untitledEditorService;
this._environmentService = environmentService;
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostDocuments);
this._modelIsSynced = {};
@@ -214,10 +219,10 @@ export class MainThreadDocuments implements MainThreadDocumentsShape {
}
private _handleUntitledScheme(uri: URI): Promise<boolean> {
const asFileUri = uri.with({ scheme: Schemas.file });
return this._fileService.resolve(asFileUri).then(stats => {
const asLocalUri = toLocalResource(uri, this._environmentService.configuration.remoteAuthority);
return this._fileService.resolve(asLocalUri).then(stats => {
// don't create a new file ontop of an existing file
return Promise.reject(new Error('file already exists on disk'));
return Promise.reject(new Error('file already exists'));
}, err => {
return this._doCreateUntitled(uri).then(resource => !!resource);
});

View File

@@ -29,6 +29,7 @@ import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editor
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
namespace delta {
@@ -330,12 +331,12 @@ export class MainThreadDocumentsAndEditors {
@IUntitledEditorService untitledEditorService: IUntitledEditorService,
@IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService,
@IBulkEditService bulkEditService: IBulkEditService,
@IPanelService panelService: IPanelService
@IPanelService panelService: IPanelService,
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService
) {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostDocumentsAndEditors);
const mainThreadDocuments = new MainThreadDocuments(this, extHostContext, this._modelService, modeService, this._textFileService, fileService, textModelResolverService, untitledEditorService);
const mainThreadDocuments = new MainThreadDocuments(this, extHostContext, this._modelService, modeService, this._textFileService, fileService, textModelResolverService, untitledEditorService, environmentService);
extHostContext.set(MainContext.MainThreadDocuments, mainThreadDocuments);
const mainThreadTextEditors = new MainThreadTextEditors(this, extHostContext, codeEditorService, bulkEditService, this._editorService, this._editorGroupService);

View File

@@ -10,6 +10,7 @@ import { FileWriteOptions, FileSystemProviderCapabilities, IFileChange, IFileSer
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { ExtHostContext, ExtHostFileSystemShape, IExtHostContext, IFileChangeDto, MainContext, MainThreadFileSystemShape } from '../common/extHost.protocol';
import { ResourceLabelFormatter, ILabelService } from 'vs/platform/label/common/label';
import { VSBuffer } from 'vs/base/common/buffer';
@extHostNamedCustomer(MainContext.MainThreadFileSystem)
export class MainThreadFileSystem implements MainThreadFileSystemShape {
@@ -105,10 +106,6 @@ class RemoteFileSystemProvider implements IFileSystemProvider {
// --- forwarding calls
private static _asBuffer(data: Uint8Array): Buffer {
return Buffer.isBuffer(data) ? data : Buffer.from(data.buffer, data.byteOffset, data.byteLength);
}
stat(resource: URI): Promise<IStat> {
return this._proxy.$stat(this._handle, resource).then(undefined, err => {
throw err;
@@ -116,11 +113,11 @@ class RemoteFileSystemProvider implements IFileSystemProvider {
}
readFile(resource: URI): Promise<Uint8Array> {
return this._proxy.$readFile(this._handle, resource);
return this._proxy.$readFile(this._handle, resource).then(buffer => buffer.buffer);
}
writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise<void> {
return this._proxy.$writeFile(this._handle, resource, RemoteFileSystemProvider._asBuffer(content), opts);
return this._proxy.$writeFile(this._handle, resource, VSBuffer.wrap(content), opts);
}
delete(resource: URI, opts: FileDeleteOptions): Promise<void> {
@@ -153,12 +150,12 @@ class RemoteFileSystemProvider implements IFileSystemProvider {
read(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number> {
return this._proxy.$read(this._handle, fd, pos, length).then(readData => {
data.set(readData, offset);
data.set(readData.buffer, offset);
return readData.byteLength;
});
}
write(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number> {
return this._proxy.$write(this._handle, fd, pos, Buffer.from(data, offset, length));
return this._proxy.$write(this._handle, fd, pos, VSBuffer.wrap(data).slice(offset, offset + length));
}
}

View File

@@ -240,6 +240,7 @@ class FormatOnSaveParticipant implements ISaveParticipantParticipant {
constructor(
@IConfigurationService private readonly _configurationService: IConfigurationService,
@ICodeEditorService private readonly _codeEditorService: ICodeEditorService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
) {
// Nothing
@@ -256,8 +257,9 @@ class FormatOnSaveParticipant implements ISaveParticipantParticipant {
return new Promise<any>((resolve, reject) => {
const source = new CancellationTokenSource();
const editorOrModel = findEditor(model, this._codeEditorService) || model;
const timeout = this._configurationService.getValue<number>('editor.formatOnSaveTimeout', overrides);
const request = this._instantiationService.invokeFunction(formatDocumentWithSelectedProvider, model, FormattingMode.Silent, source.token);
const request = this._instantiationService.invokeFunction(formatDocumentWithSelectedProvider, editorOrModel, FormattingMode.Silent, source.token);
setTimeout(() => {
reject(localize('timeout.formatOnSave', "Aborted format on save after {0}ms", timeout));

View File

@@ -3,47 +3,55 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IStatusbarService, StatusbarAlignment as MainThreadStatusBarAlignment } from 'vs/platform/statusbar/common/statusbar';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IStatusbarService, StatusbarAlignment as MainThreadStatusBarAlignment, IStatusbarEntryAccessor } from 'vs/platform/statusbar/common/statusbar';
import { MainThreadStatusBarShape, MainContext, IExtHostContext } from '../common/extHost.protocol';
import { ThemeColor } from 'vs/platform/theme/common/themeService';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { dispose } from 'vs/base/common/lifecycle';
@extHostNamedCustomer(MainContext.MainThreadStatusBar)
export class MainThreadStatusBar implements MainThreadStatusBarShape {
private readonly _entries: { [id: number]: IDisposable };
private readonly entries: Map<number, { accessor: IStatusbarEntryAccessor, alignment: MainThreadStatusBarAlignment, priority: number }> = new Map();
constructor(
extHostContext: IExtHostContext,
@IStatusbarService private readonly _statusbarService: IStatusbarService
) {
this._entries = Object.create(null);
}
_extHostContext: IExtHostContext,
@IStatusbarService private readonly statusbarService: IStatusbarService
) { }
dispose(): void {
for (const key in this._entries) {
this._entries[key].dispose();
}
this.entries.forEach(entry => entry.accessor.dispose());
this.entries.clear();
}
$setEntry(id: number, extensionId: ExtensionIdentifier, text: string, tooltip: string, command: string, color: string | ThemeColor, alignment: MainThreadStatusBarAlignment, priority: number): void {
const entry = { text, tooltip, command, color, extensionId };
// Dispose any old
this.$dispose(id);
// Reset existing entry if alignment or priority changed
let existingEntry = this.entries.get(id);
if (existingEntry && (existingEntry.alignment !== alignment || existingEntry.priority !== priority)) {
dispose(existingEntry.accessor);
this.entries.delete(id);
existingEntry = undefined;
}
// Add new
const entry = this._statusbarService.addEntry({ text, tooltip, command, color, extensionId }, alignment, priority);
this._entries[id] = entry;
// Create new entry if not existing
if (!existingEntry) {
this.entries.set(id, { accessor: this.statusbarService.addEntry(entry, alignment, priority), alignment, priority });
}
// Otherwise update
else {
existingEntry.accessor.update(entry);
}
}
$dispose(id: number) {
const disposeable = this._entries[id];
if (disposeable) {
disposeable.dispose();
const entry = this.entries.get(id);
if (entry) {
dispose(entry.accessor);
this.entries.delete(id);
}
delete this._entries[id];
}
}

View File

@@ -9,17 +9,22 @@ import { URI, UriComponents } from 'vs/base/common/uri';
import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { ExtHostContext, ExtHostWindowShape, IExtHostContext, MainContext, MainThreadWindowShape } from '../common/extHost.protocol';
import { ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
@extHostNamedCustomer(MainContext.MainThreadWindow)
export class MainThreadWindow implements MainThreadWindowShape {
private readonly proxy: ExtHostWindowShape;
private disposables: IDisposable[] = [];
private readonly _tunnels = new Map<number, Promise<RemoteTunnel>>();
constructor(
extHostContext: IExtHostContext,
@IWindowService private readonly windowService: IWindowService,
@IWindowsService private readonly windowsService: IWindowsService
@IWindowsService private readonly windowsService: IWindowsService,
@ITunnelService private readonly tunnelService: ITunnelService,
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService
) {
this.proxy = extHostContext.getProxy(ExtHostContext.ExtHostWindow);
@@ -29,13 +34,54 @@ export class MainThreadWindow implements MainThreadWindowShape {
dispose(): void {
this.disposables = dispose(this.disposables);
for (const tunnel of this._tunnels.values()) {
tunnel.then(tunnel => tunnel.dispose());
}
this._tunnels.clear();
}
$getWindowVisibility(): Promise<boolean> {
return this.windowService.isFocused();
}
$openUri(uri: UriComponents): Promise<boolean> {
return this.windowsService.openExternal(URI.revive(uri).toString(true));
async $openUri(uriComponent: UriComponents): Promise<boolean> {
const uri = URI.revive(uriComponent);
if (!!this.environmentService.configuration.remoteAuthority) {
if (uri.scheme === 'http' || uri.scheme === 'https') {
const port = this.getLocalhostPort(uri);
if (typeof port === 'number') {
const tunnel = await this.getOrCreateTunnel(port);
if (tunnel) {
const tunneledUrl = uri.toString().replace(
new RegExp(`^${uri.scheme}://localhost:${port}/`),
`${uri.scheme}://localhost:${tunnel.tunnelLocalPort}/`);
return this.windowsService.openExternal(tunneledUrl);
}
}
}
}
return this.windowsService.openExternal(uri.toString(true));
}
private getLocalhostPort(uri: URI): number | undefined {
const match = /^localhost:(\d+)$/.exec(uri.authority);
if (match) {
return +match[1];
}
return undefined;
}
private getOrCreateTunnel(remotePort: number): Promise<RemoteTunnel> | undefined {
const existing = this._tunnels.get(remotePort);
if (existing) {
return existing;
}
const tunnel = this.tunnelService.openTunnel(remotePort);
if (tunnel) {
this._tunnels.set(remotePort, tunnel);
}
return tunnel;
}
}

View File

@@ -5,7 +5,7 @@
import * as vscode from 'vscode';
import { URI } from 'vs/base/common/uri';
import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters';
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
import { CommandsRegistry, ICommandService, ICommandHandler } from 'vs/platform/commands/common/commands';
import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor';

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