mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-13 17:22:15 -05:00
Merge from vscode 31e03b8ffbb218a87e3941f2b63a249f061fe0e4 (#4986)
This commit is contained in:
@@ -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
|
||||
|
||||
176
build/azure-pipelines/common/sync-mooncake.ts
Normal file
176
build/azure-pipelines/common/sync-mooncake.ts
Normal 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();
|
||||
@@ -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 \
|
||||
|
||||
29
build/azure-pipelines/distro-build.yml
Normal file
29
build/azure-pipelines/distro-build.yml
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
@@ -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
|
||||
18
build/azure-pipelines/sync-mooncake.yml
Normal file
18
build/azure-pipelines/sync-mooncake.yml
Normal 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"
|
||||
@@ -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" }
|
||||
|
||||
@@ -87,6 +87,7 @@ const indentationFilter = [
|
||||
'!build/azure-pipelines/**/*.js',
|
||||
'!build/azure-pipelines/**/*.config',
|
||||
'!**/Dockerfile',
|
||||
'!**/Dockerfile.*',
|
||||
'!**/*.Dockerfile',
|
||||
'!**/*.dockerfile',
|
||||
'!extensions/markdown-language-features/media/*.js'
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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/**"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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' })
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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==
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -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>;
|
||||
|
||||
@@ -44,4 +44,6 @@ export namespace Schemas {
|
||||
export const data: string = 'data';
|
||||
|
||||
export const command: string = 'command';
|
||||
|
||||
export const vscodeRemote: string = 'vscode-remote';
|
||||
}
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
20
src/vs/base/test/common/buffer.test.ts
Normal file
20
src/vs/base/test/common/buffer.test.ts
Normal 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');
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -24,6 +24,10 @@ td {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
tr td:first-child {
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
label {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
1
src/vs/code/electron-browser/processExplorer/media/collapsed.svg
Executable file
1
src/vs/code/electron-browser/processExplorer/media/collapsed.svg
Executable 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 |
1
src/vs/code/electron-browser/processExplorer/media/expanded.svg
Executable file
1
src/vs/code/electron-browser/processExplorer/media/expanded.svg
Executable 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 |
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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('===========================================================');
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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 });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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> {
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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]);
|
||||
},
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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' },
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 || [];
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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[] {
|
||||
|
||||
@@ -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 }) => {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -11,7 +11,7 @@ export interface IRemoteAgentEnvironment {
|
||||
pid: number;
|
||||
appRoot: URI;
|
||||
appSettingsHome: URI;
|
||||
appSettingsPath: URI;
|
||||
settingsPath: URI;
|
||||
logsPath: URI;
|
||||
extensionsPath: URI;
|
||||
extensionHostLogsPath: URI;
|
||||
|
||||
@@ -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;
|
||||
|
||||
21
src/vs/platform/remote/common/tunnel.ts
Normal file
21
src/vs/platform/remote/common/tunnel.ts
Normal 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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
21
src/vs/platform/remote/node/tunnelService.ts
Normal file
21
src/vs/platform/remote/node/tunnelService.ts
Normal 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);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
16
src/vs/vscode.proposed.d.ts
vendored
16
src/vs/vscode.proposed.d.ts
vendored
@@ -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
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user