Merge remote-tracking branch 'origin/master' into tyriar/replace_vscode_xterm
This commit is contained in:
commit
7dab6dffcc
|
@ -65,7 +65,7 @@ This project incorporates components from the projects listed below. The origina
|
|||
58. TypeScript-TmLanguage version 1.0.0 (https://github.com/Microsoft/TypeScript-TmLanguage)
|
||||
59. Unicode version 12.0.0 (http://www.unicode.org/)
|
||||
60. vscode-logfile-highlighter version 2.4.1 (https://github.com/emilast/vscode-logfile-highlighter)
|
||||
61. vscode-octicons-font version 1.2.0 (https://github.com/Microsoft/vscode-octicons-font)
|
||||
61. vscode-octicons-font version 1.3.0 (https://github.com/Microsoft/vscode-octicons-font)
|
||||
62. vscode-swift version 0.0.1 (https://github.com/owensd/vscode-swift)
|
||||
63. Web Background Synchronization (https://github.com/WICG/BackgroundSync)
|
||||
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as cp from 'child_process';
|
||||
import * as path from 'path';
|
||||
|
||||
function yarnInstall(packageName: string): void {
|
||||
cp.execSync(`yarn add --no-lockfile ${packageName}`);
|
||||
cp.execSync(`yarn add --no-lockfile ${packageName}`, { cwd: path.join( process.cwd(), 'remote') });
|
||||
}
|
||||
|
||||
const product = require('../../../product.json');
|
||||
const dependencies = product.dependencies || {} as { [name: string]: string; };
|
||||
|
||||
Object.keys(dependencies).forEach(name => {
|
||||
const url = dependencies[name];
|
||||
yarnInstall(url);
|
||||
});
|
38
build/azure-pipelines/common/installDistroDependencies.ts
Normal file
38
build/azure-pipelines/common/installDistroDependencies.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as cp from 'child_process';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
|
||||
function yarnInstall(packageName: string, cwd: string): void {
|
||||
console.log(`yarn add --no-lockfile ${packageName}`, cwd);
|
||||
cp.execSync(`yarn add --no-lockfile ${packageName}`, { cwd, stdio: 'inherit' });
|
||||
}
|
||||
|
||||
/**
|
||||
* Install additional dependencies listed on each quality `package.json` file.
|
||||
*/
|
||||
function main() {
|
||||
const quality = process.env['VSCODE_QUALITY'];
|
||||
|
||||
if (!quality) {
|
||||
throw new Error('Missing VSCODE_QUALITY, can\'t install distro');
|
||||
}
|
||||
|
||||
const rootPath = path.dirname(path.dirname(path.dirname(__dirname)));
|
||||
const qualityPath = path.join(rootPath, 'quality', quality);
|
||||
const packagePath = path.join(qualityPath, 'package.json');
|
||||
const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
|
||||
const dependencies = pkg.dependencies || {} as { [name: string]: string; };
|
||||
|
||||
Object.keys(dependencies).forEach(name => {
|
||||
const url = dependencies[name];
|
||||
const cwd = process.argv.length < 3 ? process.cwd() : path.join(process.cwd(), process.argv[2]);
|
||||
yarnInstall(url, cwd);
|
||||
});
|
||||
}
|
||||
|
||||
main();
|
|
@ -34,7 +34,8 @@ steps:
|
|||
yarn gulp mixin
|
||||
yarn gulp hygiene
|
||||
yarn monaco-compile-check
|
||||
node build/azure-pipelines/common/installDistro.js
|
||||
node build/azure-pipelines/common/installDistroDependencies.js
|
||||
node build/azure-pipelines/common/installDistroDependencies.js remote
|
||||
node build/lib/builtInExtensions.js
|
||||
displayName: Prepare build
|
||||
|
||||
|
|
3
build/azure-pipelines/linux/build-arm.sh
Executable file
3
build/azure-pipelines/linux/build-arm.sh
Executable file
|
@ -0,0 +1,3 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
echo 'noop'
|
3
build/azure-pipelines/linux/prebuild-arm.sh
Executable file
3
build/azure-pipelines/linux/prebuild-arm.sh
Executable file
|
@ -0,0 +1,3 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
echo 'noop'
|
65
build/azure-pipelines/linux/product-build-linux-arm.yml
Normal file
65
build/azure-pipelines/linux/product-build-linux-arm.yml
Normal file
|
@ -0,0 +1,65 @@
|
|||
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"
|
||||
|
||||
- task: AzureKeyVault@1
|
||||
displayName: 'Azure Key Vault: Get Secrets'
|
||||
inputs:
|
||||
azureSubscription: 'vscode-builds-subscription'
|
||||
KeyVaultName: vscode
|
||||
|
||||
- task: Docker@1
|
||||
displayName: 'Pull image'
|
||||
inputs:
|
||||
azureSubscriptionEndpoint: 'vscode-builds-subscription'
|
||||
azureContainerRegistry: vscodehub.azurecr.io
|
||||
command: 'Run an image'
|
||||
imageName: 'vscode-linux-build-agent:armhf'
|
||||
containerCommand: uname
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
|
||||
cat << EOF > ~/.netrc
|
||||
machine monacotools.visualstudio.com
|
||||
password $(devops-pat)
|
||||
machine github.com
|
||||
login vscode
|
||||
password $(github-distro-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")
|
||||
|
||||
CHILD_CONCURRENCY=1 yarn
|
||||
yarn gulp mixin
|
||||
yarn gulp hygiene
|
||||
yarn monaco-compile-check
|
||||
./build/azure-pipelines/linux/prebuild-arm.sh
|
||||
displayName: Prepare build
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
./build/azure-pipelines/linux/build-arm.sh
|
||||
displayName: Build
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \
|
||||
AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \
|
||||
VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \
|
||||
VSCODE_HOCKEYAPP_TOKEN="$(vscode-hockeyapp-token)" \
|
||||
./build/azure-pipelines/linux/publish-arm.sh
|
||||
displayName: Publish
|
||||
|
||||
- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0
|
||||
displayName: 'Component Detection'
|
||||
continueOnError: true
|
|
@ -38,7 +38,8 @@ steps:
|
|||
yarn gulp mixin
|
||||
yarn gulp hygiene
|
||||
yarn monaco-compile-check
|
||||
node build/azure-pipelines/common/installDistro.js
|
||||
node build/azure-pipelines/common/installDistroDependencies.js
|
||||
node build/azure-pipelines/common/installDistroDependencies.js remote
|
||||
node build/lib/builtInExtensions.js
|
||||
displayName: Prepare build
|
||||
|
||||
|
|
3
build/azure-pipelines/linux/publish-arm.sh
Executable file
3
build/azure-pipelines/linux/publish-arm.sh
Executable file
|
@ -0,0 +1,3 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
echo 'noop'
|
|
@ -1,11 +1,11 @@
|
|||
resources:
|
||||
containers:
|
||||
- container: vscode-x64
|
||||
endpoint: VSCodeHub
|
||||
image: vscodehub.azurecr.io/vscode-linux-build-agent:x64
|
||||
- container: vscode-ia32
|
||||
endpoint: VSCodeHub
|
||||
- container: vscode-ia32
|
||||
image: vscodehub.azurecr.io/vscode-linux-build-agent:ia32
|
||||
endpoint: VSCodeHub
|
||||
- container: snapcraft
|
||||
image: snapcore/snapcraft
|
||||
|
||||
|
@ -59,6 +59,15 @@ jobs:
|
|||
steps:
|
||||
- template: linux/product-build-linux.yml
|
||||
|
||||
- job: LinuxArmhf
|
||||
condition: eq(variables['VSCODE_BUILD_LINUX_ARMHF'], 'true')
|
||||
pool:
|
||||
vmImage: 'Ubuntu-16.04'
|
||||
variables:
|
||||
VSCODE_ARCH: armhf
|
||||
steps:
|
||||
- template: linux/product-build-linux-arm.yml
|
||||
|
||||
- job: macOS
|
||||
condition: eq(variables['VSCODE_BUILD_MACOS'], 'true')
|
||||
pool:
|
||||
|
@ -76,6 +85,7 @@ jobs:
|
|||
- Linux
|
||||
- LinuxSnap
|
||||
- Linux32
|
||||
- LinuxArmhf
|
||||
- macOS
|
||||
steps:
|
||||
- template: sync-mooncake.yml
|
|
@ -35,7 +35,8 @@ steps:
|
|||
exec { yarn gulp mixin }
|
||||
exec { yarn gulp hygiene }
|
||||
exec { yarn monaco-compile-check }
|
||||
exec { node build/azure-pipelines/common/installDistro.js }
|
||||
exec { node build/azure-pipelines/common/installDistroDependencies.js }
|
||||
exec { node build/azure-pipelines/common/installDistroDependencies.js remote }
|
||||
exec { node build/lib/builtInExtensions.js }
|
||||
displayName: Prepare build
|
||||
|
||||
|
|
|
@ -27,7 +27,8 @@ gulp.task('mixin', function () {
|
|||
fancyLog(ansiColors.blue('[mixin]'), `Mixing in sources:`);
|
||||
return vfs
|
||||
.src(`quality/${quality}/**`, { base: `quality/${quality}` })
|
||||
.pipe(filter(function (f) { return !f.isDirectory(); }))
|
||||
.pipe(filter(f => !f.isDirectory()))
|
||||
.pipe(filter(['**', '!**/package.json']))
|
||||
.pipe(productJsonFilter)
|
||||
.pipe(buffer())
|
||||
.pipe(json(o => Object.assign({}, require('../product.json'), o)))
|
||||
|
|
|
@ -13,4 +13,4 @@ gulp.task('vscode-reh-win32-ia32-min', noop);
|
|||
gulp.task('vscode-reh-win32-x64-min', noop);
|
||||
gulp.task('vscode-reh-darwin-min', noop);
|
||||
gulp.task('vscode-reh-linux-x64-min', noop);
|
||||
gulp.task('vscode-reh-linux-arm-min', noop);
|
||||
gulp.task('vscode-reh-linux-armhf-min', noop);
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"git": {
|
||||
"name": "octref/language-css",
|
||||
"repositoryUrl": "https://github.com/octref/language-css",
|
||||
"commitHash": "6d3a2d01dd67ef062030f4520dd42a5424330a3b"
|
||||
"commitHash": "377734aad976be88a425aab5667784f3f71ea7e5"
|
||||
}
|
||||
},
|
||||
"license": "MIT",
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"If you want to provide a fix or improvement, please create a pull request against the original repository.",
|
||||
"Once accepted there, we are happy to receive an update request."
|
||||
],
|
||||
"version": "https://github.com/octref/language-css/commit/6d3a2d01dd67ef062030f4520dd42a5424330a3b",
|
||||
"version": "https://github.com/octref/language-css/commit/377734aad976be88a425aab5667784f3f71ea7e5",
|
||||
"name": "CSS",
|
||||
"scopeName": "source.css",
|
||||
"patterns": [
|
||||
|
@ -606,8 +606,32 @@
|
|||
]
|
||||
},
|
||||
{
|
||||
"begin": "(?i)(?=@[\\w-]+(\\s|\\(|{|;|/\\*|$))",
|
||||
"end": "(?<=}|;)(?!\\G)",
|
||||
"begin": "(?i)(?=@[\\w-]+[^;]+;s*$)",
|
||||
"end": "(?<=;)(?!\\G)",
|
||||
"patterns": [
|
||||
{
|
||||
"begin": "(?i)\\G(@)[\\w-]+",
|
||||
"beginCaptures": {
|
||||
"0": {
|
||||
"name": "keyword.control.at-rule.css"
|
||||
},
|
||||
"1": {
|
||||
"name": "punctuation.definition.keyword.css"
|
||||
}
|
||||
},
|
||||
"end": ";",
|
||||
"endCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.terminator.rule.css"
|
||||
}
|
||||
},
|
||||
"name": "meta.at-rule.header.css"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"begin": "(?i)(?=@[\\w-]+(\\s|\\(|{|/\\*|$))",
|
||||
"end": "(?<=})(?!\\G)",
|
||||
"patterns": [
|
||||
{
|
||||
"begin": "(?i)\\G(@)[\\w-]+",
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"git": {
|
||||
"name": "ionide/ionide-fsgrammar",
|
||||
"repositoryUrl": "https://github.com/ionide/ionide-fsgrammar",
|
||||
"commitHash": "b2100c95d7857c5421d111a860fcdd20954a0263"
|
||||
"commitHash": "b57388e5a0971412c081cf0cea8b50b9c24ea4e8"
|
||||
}
|
||||
},
|
||||
"license": "MIT",
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"If you want to provide a fix or improvement, please create a pull request against the original repository.",
|
||||
"Once accepted there, we are happy to receive an update request."
|
||||
],
|
||||
"version": "https://github.com/ionide/ionide-fsgrammar/commit/b2100c95d7857c5421d111a860fcdd20954a0263",
|
||||
"version": "https://github.com/ionide/ionide-fsgrammar/commit/b57388e5a0971412c081cf0cea8b50b9c24ea4e8",
|
||||
"name": "fsharp",
|
||||
"scopeName": "source.fsharp",
|
||||
"patterns": [
|
||||
|
@ -335,6 +335,44 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"anonymous_record_declaration": {
|
||||
"begin": "(\\{\\|)",
|
||||
"end": "(\\|\\})",
|
||||
"beginCaptures": {
|
||||
"1": {
|
||||
"name": "keyword.symbol.fsharp"
|
||||
}
|
||||
},
|
||||
"endCaptures": {
|
||||
"1": {
|
||||
"name": "keyword.symbol.fsharp"
|
||||
}
|
||||
},
|
||||
"patterns": [
|
||||
{
|
||||
"match": "[[:alpha:]0-9'`^_ ]+(:)",
|
||||
"captures": {
|
||||
"1": {
|
||||
"name": "keyword.symbol.fsharp"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"match": "([[:alpha:]0-9'`^_ ]+)",
|
||||
"captures": {
|
||||
"1": {
|
||||
"name": "entity.name.type.fsharp"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"include": "#anonymous_record_declaration"
|
||||
},
|
||||
{
|
||||
"include": "#keywords"
|
||||
}
|
||||
]
|
||||
},
|
||||
"record_signature": {
|
||||
"patterns": [
|
||||
{
|
||||
|
@ -819,6 +857,9 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"include": "#anonymous_record_declaration"
|
||||
},
|
||||
{
|
||||
"begin": "({)",
|
||||
"end": "(})",
|
||||
|
@ -982,6 +1023,9 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"include": "#anonymous_record_declaration"
|
||||
},
|
||||
{
|
||||
"include": "#keywords"
|
||||
}
|
||||
|
@ -1062,6 +1106,9 @@
|
|||
"name": "entity.name.section.fsharp"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"include": "#comments"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -1255,6 +1302,9 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"include": "#anonymous_record_declaration"
|
||||
},
|
||||
{
|
||||
"begin": "(\\?{0,1})([[:alpha:]0-9'`^._ ]+)\\s*(:)(\\s*([?[:alpha:]0-9'`^._ ]+)(<))",
|
||||
"end": "(>)",
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"git": {
|
||||
"name": "fadeevab/make.tmbundle",
|
||||
"repositoryUrl": "https://github.com/fadeevab/make.tmbundle",
|
||||
"commitHash": "bd71f44ea55d61be711bd7676e600a7333cc79ea"
|
||||
"commitHash": "1b05209b483f81f42270bdda5514590e013e4896"
|
||||
}
|
||||
},
|
||||
"licenseDetail": [
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"If you want to provide a fix or improvement, please create a pull request against the original repository.",
|
||||
"Once accepted there, we are happy to receive an update request."
|
||||
],
|
||||
"version": "https://github.com/fadeevab/make.tmbundle/commit/bd71f44ea55d61be711bd7676e600a7333cc79ea",
|
||||
"version": "https://github.com/fadeevab/make.tmbundle/commit/1b05209b483f81f42270bdda5514590e013e4896",
|
||||
"name": "Makefile",
|
||||
"scopeName": "source.makefile",
|
||||
"patterns": [
|
||||
|
@ -132,6 +132,9 @@
|
|||
},
|
||||
{
|
||||
"include": "#comment"
|
||||
},
|
||||
{
|
||||
"include": "#directives"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"git": {
|
||||
"name": "language-rust",
|
||||
"repositoryUrl": "https://github.com/zargony/atom-language-rust",
|
||||
"commitHash": "5238d9834953ed7c58d9b5b9bb0c084c3c11ecd6"
|
||||
"commitHash": "7d59e2ad79fbe5925bd2fd3bd3857bf9f421ff6f"
|
||||
}
|
||||
},
|
||||
"license": "MIT",
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"If you want to provide a fix or improvement, please create a pull request against the original repository.",
|
||||
"Once accepted there, we are happy to receive an update request."
|
||||
],
|
||||
"version": "https://github.com/zargony/atom-language-rust/commit/5238d9834953ed7c58d9b5b9bb0c084c3c11ecd6",
|
||||
"version": "https://github.com/zargony/atom-language-rust/commit/7d59e2ad79fbe5925bd2fd3bd3857bf9f421ff6f",
|
||||
"name": "Rust",
|
||||
"scopeName": "source.rust",
|
||||
"patterns": [
|
||||
|
@ -160,7 +160,7 @@
|
|||
{
|
||||
"comment": "Control keyword",
|
||||
"name": "keyword.control.rust",
|
||||
"match": "\\b(break|continue|else|if|in|for|loop|match|return|while)\\b"
|
||||
"match": "\\b(async|await|break|continue|else|if|in|for|loop|match|return|try|while)\\b"
|
||||
},
|
||||
{
|
||||
"comment": "Keyword",
|
||||
|
|
|
@ -6,6 +6,25 @@
|
|||
import * as vscode from 'vscode';
|
||||
import { ResourceMap } from '../utils/resourceMap';
|
||||
import { DiagnosticLanguage, allDiagnosticLanguages } from '../utils/languageDescription';
|
||||
import * as arrays from '../utils/arrays';
|
||||
|
||||
function diagnosticsEquals(a: vscode.Diagnostic, b: vscode.Diagnostic): boolean {
|
||||
if (a === b) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return a.code === b.code
|
||||
&& a.message === b.message
|
||||
&& a.severity === b.severity
|
||||
&& a.source === b.source
|
||||
&& a.range.isEqual(b.range)
|
||||
&& arrays.equals(a.relatedInformation || arrays.empty, b.relatedInformation || arrays.empty, (a, b) => {
|
||||
return a.message === b.message
|
||||
&& a.location.range.isEqual(b.location.range)
|
||||
&& a.location.uri.fsPath === b.location.uri.fsPath;
|
||||
})
|
||||
&& arrays.equals(a.tags || arrays.empty, b.tags || arrays.empty);
|
||||
}
|
||||
|
||||
export const enum DiagnosticKind {
|
||||
Syntax,
|
||||
|
@ -31,12 +50,10 @@ class FileDiagnostics {
|
|||
this.language = language;
|
||||
}
|
||||
|
||||
if (diagnostics.length === 0) {
|
||||
const existing = this._diagnostics.get(kind);
|
||||
if (!existing || existing && existing.length === 0) {
|
||||
// No need to update
|
||||
return false;
|
||||
}
|
||||
const existing = this._diagnostics.get(kind);
|
||||
if (arrays.equals(existing || arrays.empty, diagnostics, diagnosticsEquals)) {
|
||||
// No need to update
|
||||
return false;
|
||||
}
|
||||
|
||||
this._diagnostics.set(kind, diagnostics);
|
||||
|
|
|
@ -2,20 +2,23 @@
|
|||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
export function equals<T>(one: ReadonlyArray<T>, other: ReadonlyArray<T>, itemEquals: (a: T, b: T) => boolean = (a, b) => a === b): boolean {
|
||||
if (one.length !== other.length) {
|
||||
|
||||
export const empty = Object.freeze([]);
|
||||
|
||||
export function equals<T>(
|
||||
a: ReadonlyArray<T>,
|
||||
b: ReadonlyArray<T>,
|
||||
itemEquals: (a: T, b: T) => boolean = (a, b) => a === b
|
||||
): boolean {
|
||||
if (a === b) {
|
||||
return true;
|
||||
}
|
||||
if (a.length !== b.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0, len = one.length; i < len; i++) {
|
||||
if (!itemEquals(one[i], other[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return a.every((x, i) => itemEquals(x, b[i]));
|
||||
}
|
||||
|
||||
export function flatten<T>(arr: ReadonlyArray<T>[]): T[] {
|
||||
return ([] as T[]).concat.apply([], arr);
|
||||
return Array.prototype.concat.apply([], arr);
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "code-oss-dev",
|
||||
"version": "1.35.0",
|
||||
"distro": "78b820fcfbca46e0a1387efef3b8216e04100f45",
|
||||
"version": "1.36.0",
|
||||
"distro": "15aab9a6b297eb15ea96c450d3b92b0758572cca",
|
||||
"author": {
|
||||
"name": "Microsoft Corporation"
|
||||
},
|
||||
|
@ -155,4 +155,4 @@
|
|||
"windows-mutex": "0.2.1",
|
||||
"windows-process-tree": "0.2.3"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -96,6 +96,16 @@ const _empty = '';
|
|||
const _slash = '/';
|
||||
const _regexp = /^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/;
|
||||
|
||||
function _isQueryStringScheme(scheme: string) {
|
||||
switch (scheme.toLowerCase()) {
|
||||
case 'http':
|
||||
case 'https':
|
||||
case 'ftp':
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uniform Resource Identifier (URI) http://tools.ietf.org/html/rfc3986.
|
||||
* This class is a simple parser which creates the basic component parts
|
||||
|
@ -282,14 +292,14 @@ export class URI implements UriComponents {
|
|||
static parse(value: string, _strict: boolean = false): URI {
|
||||
const match = _regexp.exec(value);
|
||||
if (!match) {
|
||||
return new _URI(_empty, _empty, _empty, _empty, _empty);
|
||||
return new _URI(_empty, _empty, _empty, _empty, _empty, _strict);
|
||||
}
|
||||
return new _URI(
|
||||
match[2] || _empty,
|
||||
decodeURIComponent(match[4] || _empty),
|
||||
decodeURIComponent(match[5] || _empty),
|
||||
decodeURIComponent(match[7] || _empty),
|
||||
decodeURIComponent(match[9] || _empty),
|
||||
decodeURIComponentFast(match[4] || _empty, false, false),
|
||||
decodeURIComponentFast(match[5] || _empty, true, false),
|
||||
decodeURIComponentFast(match[7] || _empty, false, _isQueryStringScheme(match[2])),
|
||||
decodeURIComponentFast(match[9] || _empty, false, false),
|
||||
_strict
|
||||
);
|
||||
}
|
||||
|
@ -465,6 +475,84 @@ class _URI extends URI {
|
|||
}
|
||||
}
|
||||
|
||||
function isHex(value: string, pos: number): boolean {
|
||||
if (pos >= value.length) {
|
||||
return false;
|
||||
}
|
||||
const code = value.charCodeAt(pos);
|
||||
return (code >= CharCode.Digit0 && code <= CharCode.Digit9)// 0-9
|
||||
|| (code >= CharCode.a && code <= CharCode.f) //a-f
|
||||
|| (code >= CharCode.A && code <= CharCode.F); //A-F
|
||||
}
|
||||
|
||||
|
||||
function decodeURIComponentFast(uriComponent: string, isPath: boolean, isQueryString: boolean): string {
|
||||
|
||||
let res: string | undefined;
|
||||
let nativeDecodePos = -1;
|
||||
|
||||
for (let pos = 0; pos < uriComponent.length; pos++) {
|
||||
const code = uriComponent.charCodeAt(pos);
|
||||
|
||||
// decoding needed
|
||||
if (code === CharCode.PercentSign && isHex(uriComponent, pos + 1) && isHex(uriComponent, pos + 2)) {
|
||||
|
||||
const chA = uriComponent.charCodeAt(pos + 1);
|
||||
const chB = uriComponent.charCodeAt(pos + 2);
|
||||
|
||||
// when in a path -> check and accept %2f and %2F (fwd slash)
|
||||
// when in a query string -> check and accept %3D, %26, and %3B (equals, ampersand, semi-colon)
|
||||
if (
|
||||
(isPath && chA === CharCode.Digit2 && (chB === CharCode.F || chB === CharCode.f))
|
||||
||
|
||||
(isQueryString && (
|
||||
(chA === CharCode.Digit2 && chB === CharCode.Digit6) // %26
|
||||
||
|
||||
(chA === CharCode.Digit3 && (chB === CharCode.B || chB === CharCode.b || chB === CharCode.D || chB === CharCode.d)) // %3D, %3D
|
||||
))
|
||||
) {
|
||||
if (nativeDecodePos !== -1) {
|
||||
res += decodeURIComponent(uriComponent.substring(nativeDecodePos, pos));
|
||||
nativeDecodePos = -1;
|
||||
}
|
||||
|
||||
if (res !== undefined) {
|
||||
res += uriComponent.substr(pos, 3);
|
||||
}
|
||||
|
||||
pos += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (res === undefined) {
|
||||
res = uriComponent.substring(0, pos);
|
||||
}
|
||||
if (nativeDecodePos === -1) {
|
||||
nativeDecodePos = pos;
|
||||
}
|
||||
|
||||
pos += 2;
|
||||
|
||||
} else {
|
||||
|
||||
if (nativeDecodePos !== -1) {
|
||||
res += decodeURIComponent(uriComponent.substring(nativeDecodePos, pos));
|
||||
nativeDecodePos = -1;
|
||||
}
|
||||
|
||||
if (res !== undefined) {
|
||||
res += String.fromCharCode(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nativeDecodePos !== -1) {
|
||||
res += decodeURIComponent(uriComponent.substr(nativeDecodePos));
|
||||
}
|
||||
|
||||
return res !== undefined ? res : uriComponent;
|
||||
}
|
||||
|
||||
// reserved characters: https://tools.ietf.org/html/rfc3986#section-2.2
|
||||
const encodeTable: { [ch: number]: string } = {
|
||||
[CharCode.Colon]: '%3A', // gen-delims
|
||||
|
@ -490,7 +578,7 @@ const encodeTable: { [ch: number]: string } = {
|
|||
[CharCode.Space]: '%20',
|
||||
};
|
||||
|
||||
function encodeURIComponentFast(uriComponent: string, allowSlash: boolean): string {
|
||||
function encodeURIComponentFast(uriComponent: string, isPath: boolean, isQueryString: boolean): string {
|
||||
let res: string | undefined = undefined;
|
||||
let nativeEncodePos = -1;
|
||||
|
||||
|
@ -506,7 +594,8 @@ function encodeURIComponentFast(uriComponent: string, allowSlash: boolean): stri
|
|||
|| code === CharCode.Period
|
||||
|| code === CharCode.Underline
|
||||
|| code === CharCode.Tilde
|
||||
|| (allowSlash && code === CharCode.Slash)
|
||||
|| (isPath && code === CharCode.Slash) // path => allow slash AS-IS
|
||||
|| (isQueryString && (code === CharCode.Equals || code === CharCode.Ampersand || code === CharCode.Semicolon)) // query string => allow &=;
|
||||
) {
|
||||
// check if we are delaying native encode
|
||||
if (nativeEncodePos !== -1) {
|
||||
|
@ -518,6 +607,20 @@ function encodeURIComponentFast(uriComponent: string, allowSlash: boolean): stri
|
|||
res += uriComponent.charAt(pos);
|
||||
}
|
||||
|
||||
} else if (code === CharCode.PercentSign && isHex(uriComponent, pos + 1) && isHex(uriComponent, pos + 2)) {
|
||||
// at percentage encoded value
|
||||
|
||||
// check if we are delaying native encode
|
||||
if (nativeEncodePos !== -1) {
|
||||
res += encodeURIComponent(uriComponent.substring(nativeEncodePos, pos));
|
||||
nativeEncodePos = -1;
|
||||
}
|
||||
// check if we write into a new string (by default we try to return the param)
|
||||
if (res !== undefined) {
|
||||
res += uriComponent.substr(pos, 3);
|
||||
}
|
||||
pos += 2;
|
||||
|
||||
} else {
|
||||
// encoding needed, we need to allocate a new string
|
||||
if (res === undefined) {
|
||||
|
@ -606,6 +709,7 @@ function _asFormatted(uri: URI, skipEncoding: boolean): string {
|
|||
|
||||
let res = '';
|
||||
let { scheme, authority, path, query, fragment } = uri;
|
||||
|
||||
if (scheme) {
|
||||
res += scheme;
|
||||
res += ':';
|
||||
|
@ -622,22 +726,22 @@ function _asFormatted(uri: URI, skipEncoding: boolean): string {
|
|||
authority = authority.substr(idx + 1);
|
||||
idx = userinfo.indexOf(':');
|
||||
if (idx === -1) {
|
||||
res += encoder(userinfo, false);
|
||||
res += encoder(userinfo, false, false);
|
||||
} else {
|
||||
// <user>:<pass>@<auth>
|
||||
res += encoder(userinfo.substr(0, idx), false);
|
||||
res += encoder(userinfo.substr(0, idx), false, false);
|
||||
res += ':';
|
||||
res += encoder(userinfo.substr(idx + 1), false);
|
||||
res += encoder(userinfo.substr(idx + 1), false, false);
|
||||
}
|
||||
res += '@';
|
||||
}
|
||||
authority = authority.toLowerCase();
|
||||
idx = authority.indexOf(':');
|
||||
if (idx === -1) {
|
||||
res += encoder(authority, false);
|
||||
res += encoder(authority, false, false);
|
||||
} else {
|
||||
// <auth>:<port>
|
||||
res += encoder(authority.substr(0, idx), false);
|
||||
res += encoder(authority.substr(0, idx), false, false);
|
||||
res += authority.substr(idx);
|
||||
}
|
||||
}
|
||||
|
@ -655,15 +759,15 @@ function _asFormatted(uri: URI, skipEncoding: boolean): string {
|
|||
}
|
||||
}
|
||||
// encode the rest of the path
|
||||
res += encoder(path, true);
|
||||
res += encoder(path, true, false);
|
||||
}
|
||||
if (query) {
|
||||
res += '?';
|
||||
res += encoder(query, false);
|
||||
res += encoder(query, false, _isQueryStringScheme(scheme));
|
||||
}
|
||||
if (fragment) {
|
||||
res += '#';
|
||||
res += !skipEncoding ? encodeURIComponentFast(fragment, false) : fragment;
|
||||
res += !skipEncoding ? encodeURIComponentFast(fragment, false, false) : fragment;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -109,10 +109,10 @@ export class ConfigWatcher<T> implements IConfigWatcher<T>, IDisposable {
|
|||
try {
|
||||
this.parseErrors = [];
|
||||
res = this.options.parse ? this.options.parse(raw, this.parseErrors) : json.parse(raw, this.parseErrors);
|
||||
|
||||
return res || this.options.defaultConfig;
|
||||
} catch (error) {
|
||||
// Ignore parsing errors
|
||||
return this.options.defaultConfig;
|
||||
return this.options.defaultConfig; // Ignore parsing errors
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ suite('URI', () => {
|
|||
assert.equal(URI.from({ scheme: 'http', authority: '', path: 'my/path' }).toString(), 'http:/my/path');
|
||||
assert.equal(URI.from({ scheme: 'http', authority: '', path: '/my/path' }).toString(), 'http:/my/path');
|
||||
//http://a-test-site.com/#test=true
|
||||
assert.equal(URI.from({ scheme: 'http', authority: 'a-test-site.com', path: '/', query: 'test=true' }).toString(), 'http://a-test-site.com/?test%3Dtrue');
|
||||
assert.equal(URI.from({ scheme: 'http', authority: 'a-test-site.com', path: '/', query: 'test=true' }).toString(), 'http://a-test-site.com/?test=true');
|
||||
assert.equal(URI.from({ scheme: 'http', authority: 'a-test-site.com', path: '/', query: '', fragment: 'test=true' }).toString(), 'http://a-test-site.com/#test%3Dtrue');
|
||||
});
|
||||
|
||||
|
@ -102,11 +102,11 @@ suite('URI', () => {
|
|||
|
||||
test('with, changes', () => {
|
||||
assert.equal(URI.parse('before:some/file/path').with({ scheme: 'after' }).toString(), 'after:some/file/path');
|
||||
assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'http', path: '/api/files/test.me', query: 't=1234' }).toString(), 'http:/api/files/test.me?t%3D1234');
|
||||
assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'http', authority: '', path: '/api/files/test.me', query: 't=1234', fragment: '' }).toString(), 'http:/api/files/test.me?t%3D1234');
|
||||
assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'https', authority: '', path: '/api/files/test.me', query: 't=1234', fragment: '' }).toString(), 'https:/api/files/test.me?t%3D1234');
|
||||
assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'HTTP', authority: '', path: '/api/files/test.me', query: 't=1234', fragment: '' }).toString(), 'HTTP:/api/files/test.me?t%3D1234');
|
||||
assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'HTTPS', authority: '', path: '/api/files/test.me', query: 't=1234', fragment: '' }).toString(), 'HTTPS:/api/files/test.me?t%3D1234');
|
||||
assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'http', path: '/api/files/test.me', query: 't=1234' }).toString(), 'http:/api/files/test.me?t=1234');
|
||||
assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'http', authority: '', path: '/api/files/test.me', query: 't=1234', fragment: '' }).toString(), 'http:/api/files/test.me?t=1234');
|
||||
assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'https', authority: '', path: '/api/files/test.me', query: 't=1234', fragment: '' }).toString(), 'https:/api/files/test.me?t=1234');
|
||||
assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'HTTP', authority: '', path: '/api/files/test.me', query: 't=1234', fragment: '' }).toString(), 'HTTP:/api/files/test.me?t=1234');
|
||||
assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'HTTPS', authority: '', path: '/api/files/test.me', query: 't=1234', fragment: '' }).toString(), 'HTTPS:/api/files/test.me?t=1234');
|
||||
assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'boo', authority: '', path: '/api/files/test.me', query: 't=1234', fragment: '' }).toString(), 'boo:/api/files/test.me?t%3D1234');
|
||||
});
|
||||
|
||||
|
@ -262,11 +262,11 @@ suite('URI', () => {
|
|||
|
||||
value = URI.file('c:\\test with %25\\path');
|
||||
assert.equal(value.path, '/c:/test with %25/path');
|
||||
assert.equal(value.toString(), 'file:///c%3A/test%20with%20%2525/path');
|
||||
assert.equal(value.toString(), 'file:///c%3A/test%20with%20%25/path');
|
||||
|
||||
value = URI.file('c:\\test with %25\\c#code');
|
||||
assert.equal(value.path, '/c:/test with %25/c#code');
|
||||
assert.equal(value.toString(), 'file:///c%3A/test%20with%20%2525/c%23code');
|
||||
assert.equal(value.toString(), 'file:///c%3A/test%20with%20%25/c%23code');
|
||||
|
||||
value = URI.file('\\\\shares');
|
||||
assert.equal(value.scheme, 'file');
|
||||
|
@ -376,7 +376,7 @@ suite('URI', () => {
|
|||
let uri = URI.parse('https://go.microsoft.com/fwlink/?LinkId=518008');
|
||||
assert.equal(uri.query, 'LinkId=518008');
|
||||
assert.equal(uri.toString(true), 'https://go.microsoft.com/fwlink/?LinkId=518008');
|
||||
assert.equal(uri.toString(), 'https://go.microsoft.com/fwlink/?LinkId%3D518008');
|
||||
assert.equal(uri.toString(), 'https://go.microsoft.com/fwlink/?LinkId=518008');
|
||||
|
||||
let uri2 = URI.parse(uri.toString());
|
||||
assert.equal(uri2.query, 'LinkId=518008');
|
||||
|
@ -385,7 +385,7 @@ suite('URI', () => {
|
|||
uri = URI.parse('https://go.microsoft.com/fwlink/?LinkId=518008&foö&ké¥=üü');
|
||||
assert.equal(uri.query, 'LinkId=518008&foö&ké¥=üü');
|
||||
assert.equal(uri.toString(true), 'https://go.microsoft.com/fwlink/?LinkId=518008&foö&ké¥=üü');
|
||||
assert.equal(uri.toString(), 'https://go.microsoft.com/fwlink/?LinkId%3D518008%26fo%C3%B6%26k%C3%A9%C2%A5%3D%C3%BC%C3%BC');
|
||||
assert.equal(uri.toString(), 'https://go.microsoft.com/fwlink/?LinkId=518008&fo%C3%B6&k%C3%A9%C2%A5=%C3%BC%C3%BC');
|
||||
|
||||
uri2 = URI.parse(uri.toString());
|
||||
assert.equal(uri2.query, 'LinkId=518008&foö&ké¥=üü');
|
||||
|
@ -426,6 +426,57 @@ suite('URI', () => {
|
|||
assert.equal(uri.toString(true), input);
|
||||
});
|
||||
|
||||
test('Support URL specific encodings (query component) #25852', function () {
|
||||
let input = 'http://example.com/over/there?name=ferret';
|
||||
assert.equal(input, URI.parse(input).toString());
|
||||
|
||||
input = 'http://example.com/over/there?name=ferret&foo=bar';
|
||||
assert.equal(input, URI.parse(input).toString());
|
||||
|
||||
input = 'attp://example.com/over/there?name=ferret';
|
||||
assert.equal('attp://example.com/over/there?name%3Dferret', URI.parse(input).toString());
|
||||
});
|
||||
|
||||
test('Uri#parse can break path-component #45515', function () {
|
||||
let uri: URI;
|
||||
uri = URI.from({ scheme: 's', authority: 'a', path: '/o%2f' });
|
||||
assert.equal(uri.toString(), 's://a/o%2f');
|
||||
uri = URI.from({ scheme: 's', authority: 'a', path: '/o%2fü' });
|
||||
assert.equal(uri.toString(), 's://a/o%2f%C3%BC');
|
||||
uri = URI.from({ scheme: 's', authority: 'a', path: '/o%2f%' });
|
||||
assert.equal(uri.toString(), 's://a/o%2f%25');
|
||||
|
||||
uri = URI.file('/test with %25/c#code');
|
||||
assert.equal(uri.path, '/test with %25/c#code');
|
||||
assert.equal(uri.toString(), 'file:///test%20with%20%25/c%23code');
|
||||
|
||||
uri = URI.from({
|
||||
scheme: 'http',
|
||||
authority: 'a',
|
||||
path: '/o/products%2FzVNZkudXJyq8bPGTXUxx%2FBetterave-Sesame.jpg'
|
||||
});
|
||||
assert.equal(uri.path, '/o/products%2FzVNZkudXJyq8bPGTXUxx%2FBetterave-Sesame.jpg');
|
||||
assert.equal(uri.toString(), 'http://a/o/products%2FzVNZkudXJyq8bPGTXUxx%2FBetterave-Sesame.jpg');
|
||||
|
||||
assert.equal(URI.parse(uri.toString()).path, '/o/products%2FzVNZkudXJyq8bPGTXUxx%2FBetterave-Sesame.jpg');
|
||||
assert.equal(uri.toString(), URI.parse(uri.toString()).toString()); // identity
|
||||
|
||||
uri = URI.parse('s://a/p%2ft%c3%bc');
|
||||
assert.equal(uri.path, '/p%2ftü');
|
||||
|
||||
uri = URI.parse('s://a/%c3%bcp%2f-REST');
|
||||
assert.equal(uri.path, '/üp%2f-REST');
|
||||
|
||||
uri = URI.parse('s://a/%c3%bcp%2fd%c3%b6wn');
|
||||
assert.equal(uri.path, '/üp%2fdöwn');
|
||||
|
||||
//https://github.com/microsoft/vscode/issues/25852
|
||||
uri = URI.parse('http://www.test.com/path/service?authId=CN%3DQ10');
|
||||
assert.equal(uri.query, 'authId=CN%3DQ10');
|
||||
assert.equal(uri.toString(), 'http://www.test.com/path/service?authId=CN%3DQ10');
|
||||
});
|
||||
|
||||
|
||||
test('URI - (de)serialize', function () {
|
||||
|
||||
const values = [
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/platform/update/node/update.config.contribution';
|
|
@ -9,7 +9,7 @@ import { WindowsManager } from 'vs/code/electron-main/windows';
|
|||
import { IWindowsService, OpenContext, ActiveWindowManager, IURIToOpen } from 'vs/platform/windows/common/windows';
|
||||
import { WindowsChannel } from 'vs/platform/windows/node/windowsIpc';
|
||||
import { WindowsService } from 'vs/platform/windows/electron-main/windowsService';
|
||||
import { ILifecycleService, LifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
|
||||
import { ILifecycleService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
|
||||
import { getShellEnvironment } from 'vs/code/node/shellEnv';
|
||||
import { IUpdateService } from 'vs/platform/update/common/update';
|
||||
import { UpdateChannel } from 'vs/platform/update/node/updateIpc';
|
||||
|
@ -36,12 +36,11 @@ import { getDelayedChannel, StaticRouter } from 'vs/base/parts/ipc/common/ipc';
|
|||
import product from 'vs/platform/product/node/product';
|
||||
import pkg from 'vs/platform/product/node/package';
|
||||
import { ProxyAuthHandler } from 'vs/code/electron-main/auth';
|
||||
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { ConfigurationService } from 'vs/platform/configuration/node/configurationService';
|
||||
import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-main/windows';
|
||||
import { IHistoryMainService } from 'vs/platform/history/common/history';
|
||||
import { withUndefinedAsNull } from 'vs/base/common/types';
|
||||
import { KeyboardLayoutMonitor } from 'vs/code/electron-main/keyboard';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { WorkspacesChannel } from 'vs/platform/workspaces/node/workspacesIpc';
|
||||
import { IWorkspacesMainService, hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces';
|
||||
|
@ -90,12 +89,7 @@ export class CodeApplication extends Disposable {
|
|||
|
||||
private static APP_ICON_REFRESH_KEY = 'macOSAppIconRefresh7';
|
||||
|
||||
private windowsMainService: IWindowsMainService;
|
||||
|
||||
private electronIpcServer: ElectronIPCServer;
|
||||
|
||||
private sharedProcess: SharedProcess;
|
||||
private sharedProcessClient: Promise<Client>;
|
||||
private windowsMainService: IWindowsMainService | undefined;
|
||||
|
||||
constructor(
|
||||
private readonly mainIpcServer: Server,
|
||||
|
@ -109,9 +103,6 @@ export class CodeApplication extends Disposable {
|
|||
) {
|
||||
super();
|
||||
|
||||
this._register(mainIpcServer);
|
||||
this._register(configurationService);
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
|
@ -122,12 +113,12 @@ export class CodeApplication extends Disposable {
|
|||
process.on('uncaughtException', err => this.onUnexpectedError(err));
|
||||
process.on('unhandledRejection', (reason: unknown) => onUnexpectedError(reason));
|
||||
|
||||
// Contextmenu via IPC support
|
||||
registerContextMenuListener();
|
||||
|
||||
// Dispose on shutdown
|
||||
this.lifecycleService.onWillShutdown(() => this.dispose());
|
||||
|
||||
// Contextmenu via IPC support
|
||||
registerContextMenuListener();
|
||||
|
||||
app.on('accessibility-support-changed', (event: Event, accessibilitySupportEnabled: boolean) => {
|
||||
if (this.windowsMainService) {
|
||||
this.windowsMainService.sendToAll('vscode:accessibilitySupportChanged', accessibilitySupportEnabled);
|
||||
|
@ -200,7 +191,7 @@ export class CodeApplication extends Disposable {
|
|||
event.preventDefault();
|
||||
|
||||
// Keep in array because more might come!
|
||||
macOpenFileURIs.push(getURIToOpenFromPathSync(path));
|
||||
macOpenFileURIs.push(this.getURIToOpenFromPathSync(path));
|
||||
|
||||
// Clear previous handler if any
|
||||
if (runningTimeout !== null) {
|
||||
|
@ -217,6 +208,7 @@ export class CodeApplication extends Disposable {
|
|||
urisToOpen: macOpenFileURIs,
|
||||
preferNewWindow: true /* dropping on the dock or opening from finder prefers to open in a new window */
|
||||
});
|
||||
|
||||
macOpenFileURIs = [];
|
||||
runningTimeout = null;
|
||||
}
|
||||
|
@ -224,7 +216,9 @@ export class CodeApplication extends Disposable {
|
|||
});
|
||||
|
||||
app.on('new-window-for-tab', () => {
|
||||
this.windowsMainService.openNewWindow(OpenContext.DESKTOP); //macOS native tab "+" button
|
||||
if (this.windowsMainService) {
|
||||
this.windowsMainService.openNewWindow(OpenContext.DESKTOP); //macOS native tab "+" button
|
||||
}
|
||||
});
|
||||
|
||||
ipc.on('vscode:exit', (event: Event, code: number) => {
|
||||
|
@ -234,25 +228,26 @@ export class CodeApplication extends Disposable {
|
|||
this.lifecycleService.kill(code);
|
||||
});
|
||||
|
||||
ipc.on('vscode:fetchShellEnv', (event: Event) => {
|
||||
ipc.on('vscode:fetchShellEnv', async (event: Event) => {
|
||||
const webContents = event.sender;
|
||||
getShellEnvironment(this.logService).then(shellEnv => {
|
||||
|
||||
try {
|
||||
const shellEnv = await getShellEnvironment(this.logService);
|
||||
if (!webContents.isDestroyed()) {
|
||||
webContents.send('vscode:acceptShellEnv', shellEnv);
|
||||
}
|
||||
}, err => {
|
||||
} catch (error) {
|
||||
if (!webContents.isDestroyed()) {
|
||||
webContents.send('vscode:acceptShellEnv', {});
|
||||
}
|
||||
|
||||
this.logService.error('Error fetching shell env', err);
|
||||
});
|
||||
this.logService.error('Error fetching shell env', error);
|
||||
}
|
||||
});
|
||||
|
||||
ipc.on('vscode:extensionHostDebug', (_: Event, windowId: number, broadcast: any) => {
|
||||
if (this.windowsMainService) {
|
||||
// Send to all windows (except sender window)
|
||||
this.windowsMainService.sendToAll('vscode:extensionHostDebug', broadcast, [windowId]);
|
||||
this.windowsMainService.sendToAll('vscode:extensionHostDebug', broadcast, [windowId]); // Send to all windows (except sender window)
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -261,11 +256,28 @@ export class CodeApplication extends Disposable {
|
|||
|
||||
ipc.on('vscode:reloadWindow', (event: Event) => event.sender.reload());
|
||||
|
||||
powerMonitor.on('resume', () => { // After waking up from sleep
|
||||
if (this.windowsMainService) {
|
||||
this.windowsMainService.sendToAll('vscode:osResume', undefined);
|
||||
}
|
||||
});
|
||||
// After waking up from sleep (after window opened)
|
||||
(async () => {
|
||||
await this.lifecycleService.when(LifecycleMainPhase.AfterWindowOpen);
|
||||
|
||||
powerMonitor.on('resume', () => {
|
||||
if (this.windowsMainService) {
|
||||
this.windowsMainService.sendToAll('vscode:osResume', undefined);
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
// Keyboard layout changes (after window opened)
|
||||
(async () => {
|
||||
await this.lifecycleService.when(LifecycleMainPhase.AfterWindowOpen);
|
||||
|
||||
const nativeKeymap = await import('native-keymap');
|
||||
nativeKeymap.onDidChangeKeyboardLayout(() => {
|
||||
if (this.windowsMainService) {
|
||||
this.windowsMainService.sendToAll('vscode:keyboardLayoutChanged', false);
|
||||
}
|
||||
});
|
||||
})();
|
||||
}
|
||||
|
||||
private onUnexpectedError(err: Error): void {
|
||||
|
@ -317,24 +329,31 @@ export class CodeApplication extends Disposable {
|
|||
}
|
||||
|
||||
// Create Electron IPC Server
|
||||
this.electronIpcServer = new ElectronIPCServer();
|
||||
const electronIpcServer = new ElectronIPCServer();
|
||||
|
||||
// Resolve unique machine ID
|
||||
this.logService.trace('Resolving machine identifier...');
|
||||
const machineId = await this.resolveMachineId();
|
||||
this.logService.trace(`Resolved machine identifier: ${machineId}`);
|
||||
|
||||
// Spawn shared process
|
||||
this.sharedProcess = this.instantiationService.createInstance(SharedProcess, machineId, this.userEnv);
|
||||
this.sharedProcessClient = this.sharedProcess.whenReady().then(() => connect(this.environmentService.sharedIPCHandle, 'main'));
|
||||
// Spawn shared process after the first window has opened and 3s have passed
|
||||
const sharedProcess = this.instantiationService.createInstance(SharedProcess, machineId, this.userEnv);
|
||||
const sharedProcessClient = sharedProcess.whenReady().then(() => connect(this.environmentService.sharedIPCHandle, 'main'));
|
||||
this.lifecycleService.when(LifecycleMainPhase.AfterWindowOpen).then(() => {
|
||||
this._register(new RunOnceScheduler(async () => {
|
||||
const userEnv = await getShellEnvironment(this.logService);
|
||||
|
||||
sharedProcess.spawn(userEnv);
|
||||
}, 3000)).schedule();
|
||||
});
|
||||
|
||||
// Services
|
||||
const appInstantiationService = await this.initServices(machineId);
|
||||
const appInstantiationService = await this.createServices(machineId, sharedProcess, sharedProcessClient);
|
||||
|
||||
// Create driver
|
||||
if (this.environmentService.driverHandle) {
|
||||
(async () => {
|
||||
const server = await serveDriver(this.electronIpcServer, this.environmentService.driverHandle!, this.environmentService, appInstantiationService);
|
||||
const server = await serveDriver(electronIpcServer, this.environmentService.driverHandle!, this.environmentService, appInstantiationService);
|
||||
|
||||
this.logService.info('Driver started at:', this.environmentService.driverHandle);
|
||||
this._register(server);
|
||||
|
@ -346,10 +365,10 @@ export class CodeApplication extends Disposable {
|
|||
this._register(authHandler);
|
||||
|
||||
// Open Windows
|
||||
const windows = appInstantiationService.invokeFunction(accessor => this.openFirstWindow(accessor));
|
||||
const windows = appInstantiationService.invokeFunction(accessor => this.openFirstWindow(accessor, electronIpcServer, sharedProcessClient));
|
||||
|
||||
// Post Open Windows Tasks
|
||||
appInstantiationService.invokeFunction(accessor => this.afterWindowOpen(accessor));
|
||||
this.afterWindowOpen();
|
||||
|
||||
// Tracing: Stop tracing after windows are ready if enabled
|
||||
if (this.environmentService.args.trace) {
|
||||
|
@ -371,6 +390,63 @@ export class CodeApplication extends Disposable {
|
|||
return machineId;
|
||||
}
|
||||
|
||||
private async createServices(machineId: string, sharedProcess: SharedProcess, sharedProcessClient: Promise<Client<string>>): Promise<IInstantiationService> {
|
||||
const services = new ServiceCollection();
|
||||
|
||||
switch (process.platform) {
|
||||
case 'win32':
|
||||
services.set(IUpdateService, new SyncDescriptor(Win32UpdateService));
|
||||
break;
|
||||
|
||||
case 'linux':
|
||||
if (process.env.SNAP && process.env.SNAP_REVISION) {
|
||||
services.set(IUpdateService, new SyncDescriptor(SnapUpdateService, [process.env.SNAP, process.env.SNAP_REVISION]));
|
||||
} else {
|
||||
services.set(IUpdateService, new SyncDescriptor(LinuxUpdateService));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'darwin':
|
||||
services.set(IUpdateService, new SyncDescriptor(DarwinUpdateService));
|
||||
break;
|
||||
}
|
||||
|
||||
services.set(IWindowsMainService, new SyncDescriptor(WindowsManager, [machineId, this.userEnv]));
|
||||
services.set(IWindowsService, new SyncDescriptor(WindowsService, [sharedProcess]));
|
||||
services.set(ILaunchService, new SyncDescriptor(LaunchService));
|
||||
services.set(IIssueService, new SyncDescriptor(IssueService, [machineId, this.userEnv]));
|
||||
services.set(IMenubarService, new SyncDescriptor(MenubarService));
|
||||
|
||||
const storageMainService = new StorageMainService(this.logService, this.environmentService);
|
||||
services.set(IStorageMainService, storageMainService);
|
||||
this.lifecycleService.onWillShutdown(e => e.join(storageMainService.close()));
|
||||
|
||||
const backupMainService = new BackupMainService(this.environmentService, this.configurationService, this.logService);
|
||||
services.set(IBackupMainService, backupMainService);
|
||||
|
||||
services.set(IHistoryMainService, new SyncDescriptor(HistoryMainService));
|
||||
services.set(IURLService, new SyncDescriptor(URLService));
|
||||
services.set(IWorkspacesMainService, new SyncDescriptor(WorkspacesMainService));
|
||||
|
||||
// Telemetry
|
||||
if (!this.environmentService.isExtensionDevelopment && !this.environmentService.args['disable-telemetry'] && !!product.enableTelemetry) {
|
||||
const channel = getDelayedChannel(sharedProcessClient.then(client => client.getChannel('telemetryAppender')));
|
||||
const appender = combinedAppender(new TelemetryAppenderClient(channel), new LogAppender(this.logService));
|
||||
const commonProperties = resolveCommonProperties(product.commit, pkg.version, machineId, this.environmentService.installSourcePath);
|
||||
const piiPaths = [this.environmentService.appRoot, this.environmentService.extensionsPath];
|
||||
const config: ITelemetryServiceConfig = { appender, commonProperties, piiPaths };
|
||||
|
||||
services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [config]));
|
||||
} else {
|
||||
services.set(ITelemetryService, NullTelemetryService);
|
||||
}
|
||||
|
||||
// Init services that require it
|
||||
await backupMainService.initialize();
|
||||
|
||||
return this.instantiationService.createChild(services);
|
||||
}
|
||||
|
||||
private stopTracingEventually(windows: ICodeWindow[]): void {
|
||||
this.logService.info(`Tracing: waiting for windows to get ready...`);
|
||||
|
||||
|
@ -384,12 +460,14 @@ export class CodeApplication extends Disposable {
|
|||
|
||||
contentTracing.stopRecording(join(homedir(), `${product.applicationName}-${Math.random().toString(16).slice(-4)}.trace.txt`), path => {
|
||||
if (!timeout) {
|
||||
this.windowsMainService.showMessageBox({
|
||||
type: 'info',
|
||||
message: localize('trace.message', "Successfully created trace."),
|
||||
detail: localize('trace.detail', "Please create an issue and manually attach the following file:\n{0}", path),
|
||||
buttons: [localize('trace.ok', "Ok")]
|
||||
}, this.windowsMainService.getLastActiveWindow());
|
||||
if (this.windowsMainService) {
|
||||
this.windowsMainService.showMessageBox({
|
||||
type: 'info',
|
||||
message: localize('trace.message', "Successfully created trace."),
|
||||
detail: localize('trace.detail', "Please create an issue and manually attach the following file:\n{0}", path),
|
||||
buttons: [localize('trace.ok', "Ok")]
|
||||
}, this.windowsMainService.getLastActiveWindow());
|
||||
}
|
||||
} else {
|
||||
this.logService.info(`Tracing: data recorded (after 30s timeout) to ${path}`);
|
||||
}
|
||||
|
@ -406,73 +484,7 @@ export class CodeApplication extends Disposable {
|
|||
});
|
||||
}
|
||||
|
||||
private async initServices(machineId: string): Promise<IInstantiationService> {
|
||||
const services = new ServiceCollection();
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
services.set(IUpdateService, new SyncDescriptor(Win32UpdateService));
|
||||
} else if (process.platform === 'linux') {
|
||||
if (process.env.SNAP && process.env.SNAP_REVISION) {
|
||||
services.set(IUpdateService, new SyncDescriptor(SnapUpdateService, [process.env.SNAP, process.env.SNAP_REVISION]));
|
||||
} else {
|
||||
services.set(IUpdateService, new SyncDescriptor(LinuxUpdateService));
|
||||
}
|
||||
} else if (process.platform === 'darwin') {
|
||||
services.set(IUpdateService, new SyncDescriptor(DarwinUpdateService));
|
||||
}
|
||||
|
||||
services.set(IWindowsMainService, new SyncDescriptor(WindowsManager, [machineId]));
|
||||
services.set(IWindowsService, new SyncDescriptor(WindowsService, [this.sharedProcess]));
|
||||
services.set(ILaunchService, new SyncDescriptor(LaunchService));
|
||||
services.set(IIssueService, new SyncDescriptor(IssueService, [machineId, this.userEnv]));
|
||||
services.set(IMenubarService, new SyncDescriptor(MenubarService));
|
||||
services.set(IStorageMainService, new SyncDescriptor(StorageMainService));
|
||||
services.set(IBackupMainService, new SyncDescriptor(BackupMainService));
|
||||
services.set(IHistoryMainService, new SyncDescriptor(HistoryMainService));
|
||||
services.set(IURLService, new SyncDescriptor(URLService));
|
||||
services.set(IWorkspacesMainService, new SyncDescriptor(WorkspacesMainService));
|
||||
|
||||
// Telemetry
|
||||
if (!this.environmentService.isExtensionDevelopment && !this.environmentService.args['disable-telemetry'] && !!product.enableTelemetry) {
|
||||
const channel = getDelayedChannel(this.sharedProcessClient.then(c => c.getChannel('telemetryAppender')));
|
||||
const appender = combinedAppender(new TelemetryAppenderClient(channel), new LogAppender(this.logService));
|
||||
const commonProperties = resolveCommonProperties(product.commit, pkg.version, machineId, this.environmentService.installSourcePath);
|
||||
const piiPaths = [this.environmentService.appRoot, this.environmentService.extensionsPath];
|
||||
const config: ITelemetryServiceConfig = { appender, commonProperties, piiPaths };
|
||||
|
||||
services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [config]));
|
||||
} else {
|
||||
services.set(ITelemetryService, NullTelemetryService);
|
||||
}
|
||||
|
||||
const appInstantiationService = this.instantiationService.createChild(services);
|
||||
|
||||
// Init services that require it
|
||||
await appInstantiationService.invokeFunction(accessor => Promise.all([
|
||||
this.initStorageService(accessor),
|
||||
this.initBackupService(accessor)
|
||||
]));
|
||||
|
||||
return appInstantiationService;
|
||||
}
|
||||
|
||||
private initStorageService(accessor: ServicesAccessor): Promise<void> {
|
||||
const storageMainService = accessor.get(IStorageMainService) as StorageMainService;
|
||||
|
||||
// Ensure to close storage on shutdown
|
||||
this.lifecycleService.onWillShutdown(e => e.join(storageMainService.close()));
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
private initBackupService(accessor: ServicesAccessor): Promise<void> {
|
||||
const backupMainService = accessor.get(IBackupMainService) as BackupMainService;
|
||||
|
||||
return backupMainService.initialize();
|
||||
}
|
||||
|
||||
private openFirstWindow(accessor: ServicesAccessor): ICodeWindow[] {
|
||||
const appInstantiationService = accessor.get(IInstantiationService);
|
||||
private openFirstWindow(accessor: ServicesAccessor, electronIpcServer: ElectronIPCServer, sharedProcessClient: Promise<Client<string>>): ICodeWindow[] {
|
||||
|
||||
// Register more Main IPC services
|
||||
const launchService = accessor.get(ILaunchService);
|
||||
|
@ -482,40 +494,40 @@ export class CodeApplication extends Disposable {
|
|||
// Register more Electron IPC services
|
||||
const updateService = accessor.get(IUpdateService);
|
||||
const updateChannel = new UpdateChannel(updateService);
|
||||
this.electronIpcServer.registerChannel('update', updateChannel);
|
||||
electronIpcServer.registerChannel('update', updateChannel);
|
||||
|
||||
const issueService = accessor.get(IIssueService);
|
||||
const issueChannel = new IssueChannel(issueService);
|
||||
this.electronIpcServer.registerChannel('issue', issueChannel);
|
||||
electronIpcServer.registerChannel('issue', issueChannel);
|
||||
|
||||
const workspacesService = accessor.get(IWorkspacesMainService);
|
||||
const workspacesChannel = appInstantiationService.createInstance(WorkspacesChannel, workspacesService);
|
||||
this.electronIpcServer.registerChannel('workspaces', workspacesChannel);
|
||||
const workspacesChannel = new WorkspacesChannel(workspacesService);
|
||||
electronIpcServer.registerChannel('workspaces', workspacesChannel);
|
||||
|
||||
const windowsService = accessor.get(IWindowsService);
|
||||
const windowsChannel = new WindowsChannel(windowsService);
|
||||
this.electronIpcServer.registerChannel('windows', windowsChannel);
|
||||
this.sharedProcessClient.then(client => client.registerChannel('windows', windowsChannel));
|
||||
electronIpcServer.registerChannel('windows', windowsChannel);
|
||||
sharedProcessClient.then(client => client.registerChannel('windows', windowsChannel));
|
||||
|
||||
const menubarService = accessor.get(IMenubarService);
|
||||
const menubarChannel = new MenubarChannel(menubarService);
|
||||
this.electronIpcServer.registerChannel('menubar', menubarChannel);
|
||||
electronIpcServer.registerChannel('menubar', menubarChannel);
|
||||
|
||||
const urlService = accessor.get(IURLService);
|
||||
const urlChannel = new URLServiceChannel(urlService);
|
||||
this.electronIpcServer.registerChannel('url', urlChannel);
|
||||
electronIpcServer.registerChannel('url', urlChannel);
|
||||
|
||||
const storageMainService = accessor.get(IStorageMainService);
|
||||
const storageChannel = this._register(new GlobalStorageDatabaseChannel(this.logService, storageMainService as StorageMainService));
|
||||
this.electronIpcServer.registerChannel('storage', storageChannel);
|
||||
electronIpcServer.registerChannel('storage', storageChannel);
|
||||
|
||||
// Log level management
|
||||
const logLevelChannel = new LogLevelSetterChannel(accessor.get(ILogService));
|
||||
this.electronIpcServer.registerChannel('loglevel', logLevelChannel);
|
||||
this.sharedProcessClient.then(client => client.registerChannel('loglevel', logLevelChannel));
|
||||
electronIpcServer.registerChannel('loglevel', logLevelChannel);
|
||||
sharedProcessClient.then(client => client.registerChannel('loglevel', logLevelChannel));
|
||||
|
||||
// Lifecycle
|
||||
(this.lifecycleService as LifecycleService).ready();
|
||||
// Signal phase: ready (services set)
|
||||
this.lifecycleService.phase = LifecycleMainPhase.Ready;
|
||||
|
||||
// Propagate to clients
|
||||
const windowsMainService = this.windowsMainService = accessor.get(IWindowsMainService); // TODO@Joao: unfold this
|
||||
|
@ -523,7 +535,7 @@ export class CodeApplication extends Disposable {
|
|||
// Create a URL handler which forwards to the last active window
|
||||
const activeWindowManager = new ActiveWindowManager(windowsService);
|
||||
const activeWindowRouter = new StaticRouter(ctx => activeWindowManager.getActiveClientId().then(id => ctx === id));
|
||||
const urlHandlerChannel = this.electronIpcServer.getChannel('urlHandler', activeWindowRouter);
|
||||
const urlHandlerChannel = electronIpcServer.getChannel('urlHandler', activeWindowRouter);
|
||||
const multiplexURLHandler = new URLHandlerChannelClient(urlHandlerChannel);
|
||||
|
||||
// On Mac, Code can be running without any open windows, so we must create a window to handle urls,
|
||||
|
@ -553,11 +565,9 @@ export class CodeApplication extends Disposable {
|
|||
// Watch Electron URLs and forward them to the UrlService
|
||||
const args = this.environmentService.args;
|
||||
const urls = args['open-url'] ? args._urls : [];
|
||||
const urlListener = new ElectronURLListener(urls || [], urlService, this.windowsMainService);
|
||||
const urlListener = new ElectronURLListener(urls || [], urlService, windowsMainService);
|
||||
this._register(urlListener);
|
||||
|
||||
this.windowsMainService.ready(this.userEnv);
|
||||
|
||||
// Open our first window
|
||||
const macOpenFiles: string[] = (<any>global).macOpenFiles;
|
||||
const context = !!process.env['VSCODE_CLI'] ? OpenContext.CLI : OpenContext.DESKTOP;
|
||||
|
@ -567,9 +577,9 @@ export class CodeApplication extends Disposable {
|
|||
const noRecentEntry = args['skip-add-to-recently-opened'] === true;
|
||||
const waitMarkerFileURI = args.wait && args.waitMarkerFilePath ? URI.file(args.waitMarkerFilePath) : undefined;
|
||||
|
||||
// new window if "-n" was used without paths
|
||||
if (args['new-window'] && !hasCliArgs && !hasFolderURIs && !hasFileURIs) {
|
||||
// new window if "-n" was used without paths
|
||||
return this.windowsMainService.open({
|
||||
return windowsMainService.open({
|
||||
context,
|
||||
cli: args,
|
||||
forceNewWindow: true,
|
||||
|
@ -582,10 +592,10 @@ export class CodeApplication extends Disposable {
|
|||
|
||||
// mac: open-file event received on startup
|
||||
if (macOpenFiles && macOpenFiles.length && !hasCliArgs && !hasFolderURIs && !hasFileURIs) {
|
||||
return this.windowsMainService.open({
|
||||
return windowsMainService.open({
|
||||
context: OpenContext.DOCK,
|
||||
cli: args,
|
||||
urisToOpen: macOpenFiles.map(getURIToOpenFromPathSync),
|
||||
urisToOpen: macOpenFiles.map(file => this.getURIToOpenFromPathSync(file)),
|
||||
noRecentEntry,
|
||||
waitMarkerFileURI,
|
||||
initialStartup: true
|
||||
|
@ -593,7 +603,7 @@ export class CodeApplication extends Disposable {
|
|||
}
|
||||
|
||||
// default: read paths from cli
|
||||
return this.windowsMainService.open({
|
||||
return windowsMainService.open({
|
||||
context,
|
||||
cli: args,
|
||||
forceNewWindow: args['new-window'] || (!hasCliArgs && args['unity-launch']),
|
||||
|
@ -604,62 +614,31 @@ export class CodeApplication extends Disposable {
|
|||
});
|
||||
}
|
||||
|
||||
private afterWindowOpen(accessor: ServicesAccessor): void {
|
||||
const windowsMainService = accessor.get(IWindowsMainService);
|
||||
const historyMainService = accessor.get(IHistoryMainService);
|
||||
|
||||
if (isWindows) {
|
||||
|
||||
// Setup Windows mutex
|
||||
try {
|
||||
const Mutex = (require.__$__nodeRequire('windows-mutex') as any).Mutex;
|
||||
const windowsMutex = new Mutex(product.win32MutexName);
|
||||
this._register(toDisposable(() => windowsMutex.release()));
|
||||
} catch (e) {
|
||||
if (!this.environmentService.isBuilt) {
|
||||
windowsMainService.showMessageBox({
|
||||
title: product.nameLong,
|
||||
type: 'warning',
|
||||
message: 'Failed to load windows-mutex!',
|
||||
detail: e.toString(),
|
||||
noLink: true
|
||||
});
|
||||
}
|
||||
private getURIToOpenFromPathSync(path: string): IURIToOpen {
|
||||
try {
|
||||
const fileStat = statSync(path);
|
||||
if (fileStat.isDirectory()) {
|
||||
return { folderUri: URI.file(path) };
|
||||
}
|
||||
|
||||
// Ensure Windows foreground love module
|
||||
try {
|
||||
// tslint:disable-next-line:no-unused-expression
|
||||
require.__$__nodeRequire('windows-foreground-love');
|
||||
} catch (e) {
|
||||
if (!this.environmentService.isBuilt) {
|
||||
windowsMainService.showMessageBox({
|
||||
title: product.nameLong,
|
||||
type: 'warning',
|
||||
message: 'Failed to load windows-foreground-love!',
|
||||
detail: e.toString(),
|
||||
noLink: true
|
||||
});
|
||||
}
|
||||
if (hasWorkspaceFileExtension(path)) {
|
||||
return { workspaceUri: URI.file(path) };
|
||||
}
|
||||
} catch (error) {
|
||||
// ignore errors
|
||||
}
|
||||
|
||||
return { fileUri: URI.file(path) };
|
||||
}
|
||||
|
||||
private afterWindowOpen(): void {
|
||||
|
||||
// Signal phase: after window open
|
||||
this.lifecycleService.phase = LifecycleMainPhase.AfterWindowOpen;
|
||||
|
||||
// Remote Authorities
|
||||
this.handleRemoteAuthorities();
|
||||
|
||||
// Keyboard layout changes
|
||||
KeyboardLayoutMonitor.INSTANCE.onDidChangeKeyboardLayout(() => {
|
||||
this.windowsMainService.sendToAll('vscode:keyboardLayoutChanged', false);
|
||||
});
|
||||
|
||||
// Jump List
|
||||
historyMainService.updateWindowsJumpList();
|
||||
historyMainService.onRecentlyOpenedChange(() => historyMainService.updateWindowsJumpList());
|
||||
|
||||
// Start shared process after a while
|
||||
const sharedProcessSpawn = this._register(new RunOnceScheduler(() => getShellEnvironment(this.logService).then(userEnv => this.sharedProcess.spawn(userEnv)), 3000));
|
||||
sharedProcessSpawn.schedule();
|
||||
|
||||
// Helps application icon refresh after an update with new icon is installed (macOS)
|
||||
// TODO@Ben remove after a couple of releases
|
||||
if (isMacintosh) {
|
||||
|
@ -697,8 +676,9 @@ export class CodeApplication extends Disposable {
|
|||
|
||||
constructor(authority: string, host: string, port: number) {
|
||||
this._authority = authority;
|
||||
|
||||
const options: IConnectionOptions = {
|
||||
isBuilt: isBuilt,
|
||||
isBuilt,
|
||||
commit: product.commit,
|
||||
webSocketFactory: nodeWebSocketFactory,
|
||||
addressProvider: {
|
||||
|
@ -707,6 +687,7 @@ export class CodeApplication extends Disposable {
|
|||
}
|
||||
}
|
||||
};
|
||||
|
||||
this._connection = connectRemoteAgentManagement(options, authority, `main`);
|
||||
this._disposeRunner = new RunOnceScheduler(() => this.dispose(), 5000);
|
||||
}
|
||||
|
@ -720,6 +701,7 @@ export class CodeApplication extends Disposable {
|
|||
async getClient(): Promise<Client<RemoteAgentConnectionContext>> {
|
||||
this._disposeRunner.schedule();
|
||||
const connection = await this._connection;
|
||||
|
||||
return connection.client;
|
||||
}
|
||||
}
|
||||
|
@ -727,7 +709,9 @@ export class CodeApplication extends Disposable {
|
|||
const resolvedAuthorities = new Map<string, ResolvedAuthority>();
|
||||
ipc.on('vscode:remoteAuthorityResolved', (event: Electron.Event, data: ResolvedAuthority) => {
|
||||
this.logService.info('Received resolved authority', data.authority);
|
||||
|
||||
resolvedAuthorities.set(data.authority, data);
|
||||
|
||||
// Make sure to close and remove any existing connections
|
||||
if (connectionPool.has(data.authority)) {
|
||||
connectionPool.get(data.authority)!.dispose();
|
||||
|
@ -736,15 +720,19 @@ export class CodeApplication extends Disposable {
|
|||
|
||||
const resolveAuthority = (authority: string): ResolvedAuthority | null => {
|
||||
this.logService.info('Resolving authority', authority);
|
||||
|
||||
if (authority.indexOf('+') >= 0) {
|
||||
if (resolvedAuthorities.has(authority)) {
|
||||
return withUndefinedAsNull(resolvedAuthorities.get(authority));
|
||||
}
|
||||
|
||||
this.logService.info('Didnot find resolved authority for', authority);
|
||||
|
||||
return null;
|
||||
} else {
|
||||
const [host, strPort] = authority.split(':');
|
||||
const port = parseInt(strPort, 10);
|
||||
|
||||
return { authority, host, port };
|
||||
}
|
||||
};
|
||||
|
@ -753,6 +741,7 @@ export class CodeApplication extends Disposable {
|
|||
if (request.method !== 'GET') {
|
||||
return callback(undefined);
|
||||
}
|
||||
|
||||
const uri = URI.parse(request.url);
|
||||
|
||||
let activeConnection: ActiveConnection | undefined;
|
||||
|
@ -764,9 +753,11 @@ export class CodeApplication extends Disposable {
|
|||
callback(undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
activeConnection = new ActiveConnection(uri.authority, resolvedAuthority.host, resolvedAuthority.port);
|
||||
connectionPool.set(uri.authority, activeConnection);
|
||||
}
|
||||
|
||||
try {
|
||||
const rawClient = await activeConnection!.getClient();
|
||||
if (connectionPool.has(uri.authority)) { // not disposed in the meantime
|
||||
|
@ -785,16 +776,3 @@ export class CodeApplication extends Disposable {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getURIToOpenFromPathSync(path: string): IURIToOpen {
|
||||
try {
|
||||
const fileStat = statSync(path);
|
||||
if (fileStat.isDirectory()) {
|
||||
return { folderUri: URI.file(path) };
|
||||
} else if (hasWorkspaceFileExtension(path)) {
|
||||
return { workspaceUri: URI.file(path) };
|
||||
}
|
||||
} catch (error) {
|
||||
}
|
||||
return { fileUri: URI.file(path) };
|
||||
}
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nativeKeymap from 'native-keymap';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
|
||||
export class KeyboardLayoutMonitor {
|
||||
|
||||
public static readonly INSTANCE = new KeyboardLayoutMonitor();
|
||||
|
||||
private readonly _emitter: Emitter<void>;
|
||||
private _registered: boolean;
|
||||
|
||||
private constructor() {
|
||||
this._emitter = new Emitter<void>();
|
||||
this._registered = false;
|
||||
}
|
||||
|
||||
public onDidChangeKeyboardLayout(callback: () => void): IDisposable {
|
||||
if (!this._registered) {
|
||||
this._registered = true;
|
||||
|
||||
nativeKeymap.onDidChangeKeyboardLayout(() => {
|
||||
this._emitter.fire();
|
||||
});
|
||||
}
|
||||
return this._emitter.event(callback);
|
||||
}
|
||||
}
|
|
@ -22,10 +22,10 @@ interface PostResult {
|
|||
|
||||
class Endpoint {
|
||||
private constructor(
|
||||
public readonly url: string
|
||||
readonly url: string
|
||||
) { }
|
||||
|
||||
public static getFromProduct(): Endpoint | undefined {
|
||||
static getFromProduct(): Endpoint | undefined {
|
||||
const logUploaderUrl = product.logUploaderUrl;
|
||||
return logUploaderUrl ? new Endpoint(logUploaderUrl) : undefined;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/code/code.main';
|
||||
import 'vs/platform/update/node/update.config.contribution';
|
||||
import { app, dialog } from 'electron';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
|
@ -39,6 +39,7 @@ import { uploadLogs } from 'vs/code/electron-main/logUploader';
|
|||
import { setUnexpectedErrorHandler } from 'vs/base/common/errors';
|
||||
import { IThemeMainService, ThemeMainService } from 'vs/platform/theme/electron-main/themeMainService';
|
||||
import { Client } from 'vs/base/parts/ipc/common/ipc.net';
|
||||
import { once } from 'vs/base/common/functional';
|
||||
|
||||
class ExpectedError extends Error {
|
||||
readonly isExpected = true;
|
||||
|
@ -90,17 +91,14 @@ class CodeMain {
|
|||
// log file access on Windows (https://github.com/Microsoft/vscode/issues/41218)
|
||||
const bufferLogService = new BufferLogService();
|
||||
|
||||
const instantiationService = this.createServices(args, bufferLogService);
|
||||
const [instantiationService, instanceEnvironment] = this.createServices(args, bufferLogService);
|
||||
try {
|
||||
|
||||
// Init services
|
||||
await instantiationService.invokeFunction(async accessor => {
|
||||
const environmentService = accessor.get(IEnvironmentService);
|
||||
const stateService = accessor.get(IStateService);
|
||||
const logService = accessor.get(ILogService);
|
||||
|
||||
// Patch `process.env` with the instance's environment
|
||||
const instanceEnvironment = this.patchEnvironment(environmentService);
|
||||
|
||||
// Startup
|
||||
try {
|
||||
await this.initServices(environmentService, stateService as StateService);
|
||||
} catch (error) {
|
||||
|
@ -110,9 +108,19 @@ class CodeMain {
|
|||
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
// Startup
|
||||
await instantiationService.invokeFunction(async accessor => {
|
||||
const environmentService = accessor.get(IEnvironmentService);
|
||||
const logService = accessor.get(ILogService);
|
||||
const lifecycleService = accessor.get(ILifecycleService);
|
||||
const configurationService = accessor.get(IConfigurationService);
|
||||
|
||||
const mainIpcServer = await this.doStartup(logService, environmentService, lifecycleService, instantiationService, true);
|
||||
|
||||
const mainIpcServer = await this.doStartup(logService, environmentService, instantiationService, true);
|
||||
bufferLogService.logger = new SpdLogService('main', environmentService.logsPath, bufferLogService.getLevel());
|
||||
once(lifecycleService.onWillShutdown)(() => (configurationService as ConfigurationService).dispose());
|
||||
|
||||
return instantiationService.createInstance(CodeApplication, mainIpcServer, instanceEnvironment).startup();
|
||||
});
|
||||
|
@ -121,16 +129,17 @@ class CodeMain {
|
|||
}
|
||||
}
|
||||
|
||||
private createServices(args: ParsedArgs, bufferLogService: BufferLogService): IInstantiationService {
|
||||
private createServices(args: ParsedArgs, bufferLogService: BufferLogService): [IInstantiationService, typeof process.env] {
|
||||
const services = new ServiceCollection();
|
||||
|
||||
const environmentService = new EnvironmentService(args, process.execPath);
|
||||
const instanceEnvironment = this.patchEnvironment(environmentService); // Patch `process.env` with the instance's environment
|
||||
services.set(IEnvironmentService, environmentService);
|
||||
|
||||
const logService = new MultiplexLogService([new ConsoleLogMainService(getLogLevel(environmentService)), bufferLogService]);
|
||||
process.once('exit', () => logService.dispose());
|
||||
|
||||
services.set(IEnvironmentService, environmentService);
|
||||
services.set(ILogService, logService);
|
||||
|
||||
services.set(ILifecycleService, new SyncDescriptor(LifecycleService));
|
||||
services.set(IStateService, new SyncDescriptor(StateService));
|
||||
services.set(IConfigurationService, new SyncDescriptor(ConfigurationService, [environmentService.appSettingsPath]));
|
||||
|
@ -138,12 +147,12 @@ class CodeMain {
|
|||
services.set(IDiagnosticsService, new SyncDescriptor(DiagnosticsService));
|
||||
services.set(IThemeMainService, new SyncDescriptor(ThemeMainService));
|
||||
|
||||
return new InstantiationService(services, true);
|
||||
return [new InstantiationService(services, true), instanceEnvironment];
|
||||
}
|
||||
|
||||
private initServices(environmentService: IEnvironmentService, stateService: StateService): Promise<unknown> {
|
||||
|
||||
// Ensure paths for environment service exist
|
||||
// Environment service (paths)
|
||||
const environmentServiceInitialization = Promise.all<void | undefined>([
|
||||
environmentService.extensionsPath,
|
||||
environmentService.nodeCachedDataDir,
|
||||
|
@ -175,7 +184,7 @@ class CodeMain {
|
|||
return instanceEnvironment;
|
||||
}
|
||||
|
||||
private async doStartup(logService: ILogService, environmentService: IEnvironmentService, instantiationService: IInstantiationService, retry: boolean): Promise<Server> {
|
||||
private async doStartup(logService: ILogService, environmentService: IEnvironmentService, lifecycleService: ILifecycleService, instantiationService: IInstantiationService, retry: boolean): Promise<Server> {
|
||||
|
||||
// Try to setup a server for running. If that succeeds it means
|
||||
// we are the first instance to startup. Otherwise it is likely
|
||||
|
@ -183,6 +192,7 @@ class CodeMain {
|
|||
let server: Server;
|
||||
try {
|
||||
server = await serve(environmentService.mainIPCHandle);
|
||||
once(lifecycleService.onWillShutdown)(() => server.dispose());
|
||||
} catch (error) {
|
||||
|
||||
// Handle unexpected errors (the only expected error is EADDRINUSE that
|
||||
|
@ -226,10 +236,11 @@ class CodeMain {
|
|||
fs.unlinkSync(environmentService.mainIPCHandle);
|
||||
} catch (error) {
|
||||
logService.warn('Could not delete obsolete instance handle', error);
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
return this.doStartup(logService, environmentService, instantiationService, false);
|
||||
return this.doStartup(logService, environmentService, lifecycleService, instantiationService, false);
|
||||
}
|
||||
|
||||
// Tests from CLI require to be the only instance currently
|
||||
|
@ -261,8 +272,8 @@ class CodeMain {
|
|||
if (environmentService.args.status) {
|
||||
return instantiationService.invokeFunction(async accessor => {
|
||||
const diagnostics = await accessor.get(IDiagnosticsService).getDiagnostics(launchClient);
|
||||
|
||||
console.log(diagnostics);
|
||||
|
||||
throw new ExpectedError();
|
||||
});
|
||||
}
|
||||
|
@ -300,12 +311,14 @@ class CodeMain {
|
|||
// Print --status usage info
|
||||
if (environmentService.args.status) {
|
||||
logService.warn('Warning: The --status argument can only be used if Code is already running. Please run it again after Code has started.');
|
||||
|
||||
throw new ExpectedError('Terminating...');
|
||||
}
|
||||
|
||||
// Log uploader usage info
|
||||
if (typeof environmentService.args['upload-logs'] !== 'undefined') {
|
||||
logService.warn('Warning: The --upload-logs argument can only be used if Code is already running. Please run it again after Code has started.');
|
||||
|
||||
throw new ExpectedError('Terminating...');
|
||||
}
|
||||
|
||||
|
|
|
@ -203,12 +203,6 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
|||
return !!this.config.extensionTestsPath;
|
||||
}
|
||||
|
||||
/*
|
||||
get extensionDevelopmentPaths(): string | string[] | undefined {
|
||||
return this.config.extensionDevelopmentPath;
|
||||
}
|
||||
*/
|
||||
|
||||
get config(): IWindowConfiguration {
|
||||
return this.currentConfig;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ import { CodeWindow, defaultWindowState } from 'vs/code/electron-main/window';
|
|||
import { hasArgs, asArray } from 'vs/platform/environment/node/argv';
|
||||
import { ipcMain as ipc, screen, BrowserWindow, dialog, systemPreferences, FileFilter } from 'electron';
|
||||
import { parseLineAndColumnAware } from 'vs/code/node/paths';
|
||||
import { ILifecycleService, UnloadReason, LifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
|
||||
import { ILifecycleService, UnloadReason, LifecycleService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IWindowSettings, OpenContext, IPath, IWindowConfiguration, INativeOpenDialogOptions, IPathsToWaitFor, IEnterWorkspaceResult, IMessageBoxResult, INewWindowOptions, IURIToOpen, isFileToOpen, isWorkspaceToOpen, isFolderToOpen } from 'vs/platform/windows/common/windows';
|
||||
|
@ -38,6 +38,7 @@ import { getComparisonKey, isEqual, normalizePath, basename as resourcesBasename
|
|||
import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts';
|
||||
import { restoreWindowsState, WindowsStateStorageData, getWindowsStateStoreData } from 'vs/code/electron-main/windowsStateStorage';
|
||||
import { getWorkspaceIdentifier } from 'vs/platform/workspaces/electron-main/workspacesMainService';
|
||||
import { once } from 'vs/base/common/functional';
|
||||
|
||||
const enum WindowError {
|
||||
UNRESPONSIVE = 1,
|
||||
|
@ -159,9 +160,7 @@ export class WindowsManager implements IWindowsMainService {
|
|||
|
||||
private static readonly windowsStateStorageKey = 'windowsState';
|
||||
|
||||
private static WINDOWS: ICodeWindow[] = [];
|
||||
|
||||
private initialUserEnv: IProcessEnvironment;
|
||||
private static readonly WINDOWS: ICodeWindow[] = [];
|
||||
|
||||
private readonly windowsState: IWindowsState;
|
||||
private lastClosedWindowState?: IWindowState;
|
||||
|
@ -170,19 +169,20 @@ export class WindowsManager implements IWindowsMainService {
|
|||
private readonly workspacesManager: WorkspacesManager;
|
||||
|
||||
private _onWindowReady = new Emitter<ICodeWindow>();
|
||||
onWindowReady: CommonEvent<ICodeWindow> = this._onWindowReady.event;
|
||||
readonly onWindowReady: CommonEvent<ICodeWindow> = this._onWindowReady.event;
|
||||
|
||||
private _onWindowClose = new Emitter<number>();
|
||||
onWindowClose: CommonEvent<number> = this._onWindowClose.event;
|
||||
readonly onWindowClose: CommonEvent<number> = this._onWindowClose.event;
|
||||
|
||||
private _onWindowLoad = new Emitter<number>();
|
||||
onWindowLoad: CommonEvent<number> = this._onWindowLoad.event;
|
||||
readonly onWindowLoad: CommonEvent<number> = this._onWindowLoad.event;
|
||||
|
||||
private _onWindowsCountChanged = new Emitter<IWindowsCountChangedEvent>();
|
||||
onWindowsCountChanged: CommonEvent<IWindowsCountChangedEvent> = this._onWindowsCountChanged.event;
|
||||
readonly onWindowsCountChanged: CommonEvent<IWindowsCountChangedEvent> = this._onWindowsCountChanged.event;
|
||||
|
||||
constructor(
|
||||
private readonly machineId: string,
|
||||
private readonly initialUserEnv: IProcessEnvironment,
|
||||
@ILogService private readonly logService: ILogService,
|
||||
@IStateService private readonly stateService: IStateService,
|
||||
@IEnvironmentService private readonly environmentService: IEnvironmentService,
|
||||
|
@ -203,12 +203,50 @@ export class WindowsManager implements IWindowsMainService {
|
|||
|
||||
this.dialogs = new Dialogs(stateService, this);
|
||||
this.workspacesManager = new WorkspacesManager(workspacesMainService, backupMainService, this);
|
||||
|
||||
this.lifecycleService.when(LifecycleMainPhase.Ready).then(() => this.registerListeners());
|
||||
this.lifecycleService.when(LifecycleMainPhase.AfterWindowOpen).then(() => this.setupNativeHelpers());
|
||||
}
|
||||
|
||||
ready(initialUserEnv: IProcessEnvironment): void {
|
||||
this.initialUserEnv = initialUserEnv;
|
||||
private setupNativeHelpers(): void {
|
||||
if (isWindows) {
|
||||
|
||||
this.registerListeners();
|
||||
// Setup Windows mutex
|
||||
try {
|
||||
const WindowsMutex = (require.__$__nodeRequire('windows-mutex') as typeof import('windows-mutex')).Mutex;
|
||||
const mutex = new WindowsMutex(product.win32MutexName);
|
||||
once(this.lifecycleService.onWillShutdown)(() => mutex.release());
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
|
||||
if (!this.environmentService.isBuilt) {
|
||||
this.showMessageBox({
|
||||
title: product.nameLong,
|
||||
type: 'warning',
|
||||
message: 'Failed to load windows-mutex!',
|
||||
detail: e.toString(),
|
||||
noLink: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Dev only: Ensure Windows foreground love module is present
|
||||
if (!this.environmentService.isBuilt) {
|
||||
try {
|
||||
require.__$__nodeRequire('windows-foreground-love');
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
|
||||
this.showMessageBox({
|
||||
title: product.nameLong,
|
||||
type: 'warning',
|
||||
message: 'Failed to load windows-foreground-love!',
|
||||
detail: e.toString(),
|
||||
noLink: true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
|
@ -543,6 +581,7 @@ export class WindowsManager implements IWindowsMainService {
|
|||
|
||||
// Find suitable window or folder path to open files in
|
||||
const fileToCheck = fileInputs.filesToOpenOrCreate[0] || fileInputs.filesToDiff[0];
|
||||
|
||||
// only look at the windows with correct authority
|
||||
const windows = WindowsManager.WINDOWS.filter(w => w.remoteAuthority === fileInputs!.remoteAuthority);
|
||||
|
||||
|
@ -639,7 +678,6 @@ export class WindowsManager implements IWindowsMainService {
|
|||
|
||||
// Handle folders to open (instructed and to restore)
|
||||
const allFoldersToOpen = arrays.distinct(foldersToOpen, folder => getComparisonKey(folder.folderUri)); // prevent duplicates
|
||||
|
||||
if (allFoldersToOpen.length > 0) {
|
||||
|
||||
// Check for existing instances
|
||||
|
@ -713,7 +751,9 @@ export class WindowsManager implements IWindowsMainService {
|
|||
if (fileInputs && !emptyToOpen) {
|
||||
emptyToOpen++;
|
||||
}
|
||||
|
||||
const remoteAuthority = fileInputs ? fileInputs.remoteAuthority : (openConfig.cli && openConfig.cli.remote || undefined);
|
||||
|
||||
for (let i = 0; i < emptyToOpen; i++) {
|
||||
usedWindows.push(this.openInBrowserWindow({
|
||||
userEnv: openConfig.userEnv,
|
||||
|
|
|
@ -57,6 +57,7 @@ export async function main(argv: string[]): Promise<any> {
|
|||
else if (shouldSpawnCliProcess(args)) {
|
||||
const cli = await new Promise<IMainCli>((c, e) => require(['vs/code/node/cliProcessMain'], c, e));
|
||||
await cli.main(args);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,12 +27,18 @@ export class CodeActionContextMenu {
|
|||
private readonly _onApplyCodeAction: (action: CodeAction) => Promise<any>
|
||||
) { }
|
||||
|
||||
async show(actionsToShow: Promise<CodeActionSet>, at?: { x: number; y: number } | Position): Promise<void> {
|
||||
public async show(actionsToShow: Promise<CodeActionSet>, at?: { x: number; y: number } | Position): Promise<void> {
|
||||
const codeActions = await actionsToShow;
|
||||
if (!codeActions.actions.length) {
|
||||
this._visible = false;
|
||||
return;
|
||||
}
|
||||
if (!this._editor.getDomNode()) {
|
||||
// cancel when editor went off-dom
|
||||
this._visible = false;
|
||||
return Promise.reject(canceled());
|
||||
}
|
||||
|
||||
this._visible = true;
|
||||
const actions = codeActions.actions.map(action => this.codeActionToAction(action));
|
||||
this._contextMenuService.showContextMenu({
|
||||
|
|
|
@ -32,7 +32,7 @@ suite('Configuration', () => {
|
|||
assert.deepEqual(target, { 'a': { 'b': 2 } });
|
||||
});
|
||||
|
||||
test('removeFromValueTree: remove a single segemented key', () => {
|
||||
test('removeFromValueTree: remove a single segmented key', () => {
|
||||
let target = { 'a': 1 };
|
||||
|
||||
removeFromValueTree(target, 'a');
|
||||
|
@ -40,7 +40,7 @@ suite('Configuration', () => {
|
|||
assert.deepEqual(target, {});
|
||||
});
|
||||
|
||||
test('removeFromValueTree: remove a single segemented key when its value is undefined', () => {
|
||||
test('removeFromValueTree: remove a single segmented key when its value is undefined', () => {
|
||||
let target = { 'a': undefined };
|
||||
|
||||
removeFromValueTree(target, 'a');
|
||||
|
@ -48,7 +48,7 @@ suite('Configuration', () => {
|
|||
assert.deepEqual(target, {});
|
||||
});
|
||||
|
||||
test('removeFromValueTree: remove a multi segemented key when its value is undefined', () => {
|
||||
test('removeFromValueTree: remove a multi segmented key when its value is undefined', () => {
|
||||
let target = { 'a': { 'b': 1 } };
|
||||
|
||||
removeFromValueTree(target, 'a.b');
|
||||
|
@ -56,7 +56,7 @@ suite('Configuration', () => {
|
|||
assert.deepEqual(target, {});
|
||||
});
|
||||
|
||||
test('removeFromValueTree: remove a multi segemented key when its value is array', () => {
|
||||
test('removeFromValueTree: remove a multi segmented key when its value is array', () => {
|
||||
let target = { 'a': { 'b': [1] } };
|
||||
|
||||
removeFromValueTree(target, 'a.b');
|
||||
|
@ -64,7 +64,7 @@ suite('Configuration', () => {
|
|||
assert.deepEqual(target, {});
|
||||
});
|
||||
|
||||
test('removeFromValueTree: remove a multi segemented key first segment value is array', () => {
|
||||
test('removeFromValueTree: remove a multi segmented key first segment value is array', () => {
|
||||
let target = { 'a': [1] };
|
||||
|
||||
removeFromValueTree(target, 'a.0');
|
||||
|
@ -80,7 +80,7 @@ suite('Configuration', () => {
|
|||
assert.deepEqual(target, {});
|
||||
});
|
||||
|
||||
test('removeFromValueTree: remove a multi segemented key when the first node has more values', () => {
|
||||
test('removeFromValueTree: remove a multi segmented key when the first node has more values', () => {
|
||||
let target = { 'a': { 'b': { 'c': 1 }, 'd': 1 } };
|
||||
|
||||
removeFromValueTree(target, 'a.b.c');
|
||||
|
@ -88,7 +88,7 @@ suite('Configuration', () => {
|
|||
assert.deepEqual(target, { 'a': { 'd': 1 } });
|
||||
});
|
||||
|
||||
test('removeFromValueTree: remove a multi segemented key when in between node has more values', () => {
|
||||
test('removeFromValueTree: remove a multi segmented key when in between node has more values', () => {
|
||||
let target = { 'a': { 'b': { 'c': { 'd': 1 }, 'd': 1 } } };
|
||||
|
||||
removeFromValueTree(target, 'a.b.c.d');
|
||||
|
@ -96,7 +96,7 @@ suite('Configuration', () => {
|
|||
assert.deepEqual(target, { 'a': { 'b': { 'd': 1 } } });
|
||||
});
|
||||
|
||||
test('removeFromValueTree: remove a multi segemented key when the last but one node has more values', () => {
|
||||
test('removeFromValueTree: remove a multi segmented key when the last but one node has more values', () => {
|
||||
let target = { 'a': { 'b': { 'c': 1, 'd': 1 } } };
|
||||
|
||||
removeFromValueTree(target, 'a.b.c');
|
||||
|
|
|
@ -82,7 +82,7 @@ suite('ConfigurationModel', () => {
|
|||
assert.deepEqual(testObject.keys, ['a.b']);
|
||||
});
|
||||
|
||||
test('removeValue: remove a single segemented key', () => {
|
||||
test('removeValue: remove a single segmented key', () => {
|
||||
let testObject = new ConfigurationModel({ 'a': 1 }, ['a']);
|
||||
|
||||
testObject.removeValue('a');
|
||||
|
@ -91,7 +91,7 @@ suite('ConfigurationModel', () => {
|
|||
assert.deepEqual(testObject.keys, []);
|
||||
});
|
||||
|
||||
test('removeValue: remove a multi segemented key', () => {
|
||||
test('removeValue: remove a multi segmented key', () => {
|
||||
let testObject = new ConfigurationModel({ 'a': { 'b': 1 } }, ['a.b']);
|
||||
|
||||
testObject.removeValue('a.b');
|
||||
|
|
|
@ -36,7 +36,7 @@ export interface IConfirmationResult {
|
|||
|
||||
/**
|
||||
* This will only be defined if the confirmation was created
|
||||
* with the checkox option defined.
|
||||
* with the checkbox option defined.
|
||||
*/
|
||||
checkboxChecked?: boolean;
|
||||
}
|
||||
|
|
|
@ -287,6 +287,7 @@ export function parseDebugPort(debugArg: string | undefined, debugBrkArg: string
|
|||
const portStr = debugBrkArg || debugArg;
|
||||
const port = Number(portStr) || (!isBuild ? defaultBuildPort : null);
|
||||
const brk = port ? Boolean(!!debugBrkArg) : false;
|
||||
|
||||
return { port, break: brk, debugId };
|
||||
}
|
||||
|
||||
|
@ -301,9 +302,9 @@ function parsePathArg(arg: string | undefined, process: NodeJS.Process): string
|
|||
|
||||
if (path.normalize(arg) === resolved) {
|
||||
return resolved;
|
||||
} else {
|
||||
return path.resolve(process.env['VSCODE_CWD'] || process.cwd(), arg);
|
||||
}
|
||||
|
||||
return path.resolve(process.env['VSCODE_CWD'] || process.cwd(), arg);
|
||||
}
|
||||
|
||||
export function parseUserDataDir(args: ParsedArgs, process: NodeJS.Process): string {
|
||||
|
|
|
@ -480,12 +480,12 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
|||
e => Promise.reject(new ExtensionManagementError(this.joinErrors(e).message, INSTALL_ERROR_DELETING)));
|
||||
}
|
||||
|
||||
private rename(identfier: IExtensionIdentifier, extractPath: string, renamePath: string, retryUntil: number): Promise<void> {
|
||||
private rename(identifier: IExtensionIdentifier, extractPath: string, renamePath: string, retryUntil: number): Promise<void> {
|
||||
return pfs.rename(extractPath, renamePath)
|
||||
.then(undefined, error => {
|
||||
if (isWindows && error && error.code === 'EPERM' && Date.now() < retryUntil) {
|
||||
this.logService.info(`Failed renaming ${extractPath} to ${renamePath} with 'EPERM' error. Trying again...`, identfier.id);
|
||||
return this.rename(identfier, extractPath, renamePath, retryUntil);
|
||||
this.logService.info(`Failed renaming ${extractPath} to ${renamePath} with 'EPERM' error. Trying again...`, identifier.id);
|
||||
return this.rename(identifier, extractPath, renamePath, retryUntil);
|
||||
}
|
||||
return Promise.reject(new ExtensionManagementError(error.message || nls.localize('renameError', "Unknown error while renaming {0} to {1}", extractPath, renamePath), error.code || INSTALL_ERROR_RENAMING));
|
||||
});
|
||||
|
@ -835,8 +835,8 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
|||
return pfs.rimraf(extension.location.fsPath).then(() => this.logService.info('Deleted from disk', extension.identifier.id, extension.location.fsPath));
|
||||
}
|
||||
|
||||
private isUninstalled(identfier: ExtensionIdentifierWithVersion): Promise<boolean> {
|
||||
return this.filterUninstalled(identfier).then(uninstalled => uninstalled.length === 1);
|
||||
private isUninstalled(identifier: ExtensionIdentifierWithVersion): Promise<boolean> {
|
||||
return this.filterUninstalled(identifier).then(uninstalled => uninstalled.length === 1);
|
||||
}
|
||||
|
||||
private filterUninstalled(...identifiers: ExtensionIdentifierWithVersion[]): Promise<string[]> {
|
||||
|
|
|
@ -16,11 +16,11 @@ export class ExtensionsManifestCache extends Disposable {
|
|||
|
||||
constructor(
|
||||
private readonly environmentService: IEnvironmentService,
|
||||
extensionsManagementServuce: IExtensionManagementService
|
||||
extensionsManagementService: IExtensionManagementService
|
||||
) {
|
||||
super();
|
||||
this._register(extensionsManagementServuce.onDidInstallExtension(e => this.onDidInstallExtension(e)));
|
||||
this._register(extensionsManagementServuce.onDidUninstallExtension(e => this.onDidUnInstallExtension(e)));
|
||||
this._register(extensionsManagementService.onDidInstallExtension(e => this.onDidInstallExtension(e)));
|
||||
this._register(extensionsManagementService.onDidUninstallExtension(e => this.onDidUnInstallExtension(e)));
|
||||
}
|
||||
|
||||
private onDidInstallExtension(e: DidInstallExtensionEvent): void {
|
||||
|
|
|
@ -517,7 +517,7 @@ interface IBaseStat {
|
|||
resource: URI;
|
||||
|
||||
/**
|
||||
* The name which is the last segement
|
||||
* The name which is the last segment
|
||||
* of the {{path}}.
|
||||
*/
|
||||
name: string;
|
||||
|
@ -531,7 +531,7 @@ interface IBaseStat {
|
|||
size?: number;
|
||||
|
||||
/**
|
||||
* The last modifictaion date represented
|
||||
* The last modification date represented
|
||||
* as millis from unix epoch.
|
||||
*
|
||||
* The value may or may not be resolved as
|
||||
|
|
|
@ -22,6 +22,8 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
|
|||
import { getSimpleWorkspaceLabel } from 'vs/platform/label/common/label';
|
||||
import { toStoreData, restoreRecentlyOpened, RecentlyOpenedStorageData } from 'vs/platform/history/electron-main/historyStorage';
|
||||
import { exists } from 'vs/base/node/pfs';
|
||||
import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ILifecycleService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
|
||||
|
||||
export class HistoryMainService implements IHistoryMainService {
|
||||
|
||||
|
@ -37,10 +39,10 @@ export class HistoryMainService implements IHistoryMainService {
|
|||
|
||||
private static readonly recentlyOpenedStorageKey = 'openedPathsList';
|
||||
|
||||
_serviceBrand: any;
|
||||
_serviceBrand: ServiceIdentifier<IHistoryMainService>;
|
||||
|
||||
private _onRecentlyOpenedChange = new Emitter<void>();
|
||||
onRecentlyOpenedChange: CommonEvent<void> = this._onRecentlyOpenedChange.event;
|
||||
readonly onRecentlyOpenedChange: CommonEvent<void> = this._onRecentlyOpenedChange.event;
|
||||
|
||||
private macOSRecentDocumentsUpdater: ThrottledDelayer<void>;
|
||||
|
||||
|
@ -48,9 +50,21 @@ export class HistoryMainService implements IHistoryMainService {
|
|||
@IStateService private readonly stateService: IStateService,
|
||||
@ILogService private readonly logService: ILogService,
|
||||
@IWorkspacesMainService private readonly workspacesMainService: IWorkspacesMainService,
|
||||
@IEnvironmentService private readonly environmentService: IEnvironmentService
|
||||
@IEnvironmentService private readonly environmentService: IEnvironmentService,
|
||||
@ILifecycleService lifecycleService: ILifecycleService
|
||||
) {
|
||||
this.macOSRecentDocumentsUpdater = new ThrottledDelayer<void>(800);
|
||||
|
||||
lifecycleService.when(LifecycleMainPhase.AfterWindowOpen).then(() => this.handleWindowsJumpList());
|
||||
}
|
||||
|
||||
private handleWindowsJumpList(): void {
|
||||
if (!isWindows) {
|
||||
return; // only on windows
|
||||
}
|
||||
|
||||
this.updateWindowsJumpList();
|
||||
this.onRecentlyOpenedChange(() => this.updateWindowsJumpList());
|
||||
}
|
||||
|
||||
addRecentlyOpened(newlyAdded: IRecent[]): void {
|
||||
|
|
|
@ -8,7 +8,7 @@ import { ILogService } from 'vs/platform/log/common/log';
|
|||
import { IURLService } from 'vs/platform/url/common/url';
|
||||
import { IProcessEnvironment, isMacintosh } from 'vs/base/common/platform';
|
||||
import { ParsedArgs, IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { OpenContext, IWindowSettings } from 'vs/platform/windows/common/windows';
|
||||
import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-main/windows';
|
||||
import { whenDeleted } from 'vs/base/node/pfs';
|
||||
|
@ -107,7 +107,7 @@ export class LaunchChannel implements IServerChannel {
|
|||
|
||||
export class LaunchChannelClient implements ILaunchService {
|
||||
|
||||
_serviceBrand: any;
|
||||
_serviceBrand: ServiceIdentifier<ILaunchService>;
|
||||
|
||||
constructor(private channel: IChannel) { }
|
||||
|
||||
|
@ -134,7 +134,7 @@ export class LaunchChannelClient implements ILaunchService {
|
|||
|
||||
export class LaunchService implements ILaunchService {
|
||||
|
||||
_serviceBrand: any;
|
||||
_serviceBrand: ServiceIdentifier<ILaunchService>;
|
||||
|
||||
constructor(
|
||||
@ILogService private readonly logService: ILogService,
|
||||
|
|
|
@ -29,7 +29,7 @@ export interface BeforeShutdownEvent {
|
|||
/**
|
||||
* The reason why the application will be shutting down.
|
||||
*/
|
||||
reason: ShutdownReason;
|
||||
readonly reason: ShutdownReason;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -51,7 +51,7 @@ export interface WillShutdownEvent {
|
|||
/**
|
||||
* The reason why the application is shutting down.
|
||||
*/
|
||||
reason: ShutdownReason;
|
||||
readonly reason: ShutdownReason;
|
||||
}
|
||||
|
||||
export const enum ShutdownReason {
|
||||
|
|
|
@ -7,11 +7,12 @@ import { ipcMain as ipc, app } from 'electron';
|
|||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IStateService } from 'vs/platform/state/common/state';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ICodeWindow } from 'vs/platform/windows/electron-main/windows';
|
||||
import { handleVetos } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { isMacintosh, isWindows } from 'vs/base/common/platform';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Barrier } from 'vs/base/common/async';
|
||||
|
||||
export const ILifecycleService = createDecorator<ILifecycleService>('lifecycleService');
|
||||
|
||||
|
@ -38,42 +39,48 @@ export interface ShutdownEvent {
|
|||
}
|
||||
|
||||
export interface ILifecycleService {
|
||||
_serviceBrand: any;
|
||||
|
||||
_serviceBrand: ServiceIdentifier<ILifecycleService>;
|
||||
|
||||
/**
|
||||
* Will be true if the program was restarted (e.g. due to explicit request or update).
|
||||
*/
|
||||
wasRestarted: boolean;
|
||||
readonly wasRestarted: boolean;
|
||||
|
||||
/**
|
||||
* Will be true if the program was requested to quit.
|
||||
*/
|
||||
quitRequested: boolean;
|
||||
readonly quitRequested: boolean;
|
||||
|
||||
/**
|
||||
* A flag indicating in what phase of the lifecycle we currently are.
|
||||
*/
|
||||
phase: LifecycleMainPhase;
|
||||
|
||||
/**
|
||||
* An event that fires when the application is about to shutdown before any window is closed.
|
||||
* The shutdown can still be prevented by any window that vetos this event.
|
||||
*/
|
||||
onBeforeShutdown: Event<void>;
|
||||
readonly onBeforeShutdown: Event<void>;
|
||||
|
||||
/**
|
||||
* An event that fires after the onBeforeShutdown event has been fired and after no window has
|
||||
* vetoed the shutdown sequence. At this point listeners are ensured that the application will
|
||||
* quit without veto.
|
||||
*/
|
||||
onWillShutdown: Event<ShutdownEvent>;
|
||||
readonly onWillShutdown: Event<ShutdownEvent>;
|
||||
|
||||
/**
|
||||
* An event that fires before a window closes. This event is fired after any veto has been dealt
|
||||
* with so that listeners know for sure that the window will close without veto.
|
||||
*/
|
||||
onBeforeWindowClose: Event<ICodeWindow>;
|
||||
readonly onBeforeWindowClose: Event<ICodeWindow>;
|
||||
|
||||
/**
|
||||
* An event that fires before a window is about to unload. Listeners can veto this event to prevent
|
||||
* the window from unloading.
|
||||
*/
|
||||
onBeforeWindowUnload: Event<IWindowUnloadEvent>;
|
||||
readonly onBeforeWindowUnload: Event<IWindowUnloadEvent>;
|
||||
|
||||
/**
|
||||
* Unload a window for the provided reason. All lifecycle event handlers are triggered.
|
||||
|
@ -94,6 +101,32 @@ export interface ILifecycleService {
|
|||
* Forcefully shutdown the application. No livecycle event handlers are triggered.
|
||||
*/
|
||||
kill(code?: number): void;
|
||||
|
||||
/**
|
||||
* Returns a promise that resolves when a certain lifecycle phase
|
||||
* has started.
|
||||
*/
|
||||
when(phase: LifecycleMainPhase): Promise<void>;
|
||||
}
|
||||
|
||||
export const enum LifecycleMainPhase {
|
||||
|
||||
/**
|
||||
* The first phase signals that we are about to startup.
|
||||
*/
|
||||
Starting = 1,
|
||||
|
||||
/**
|
||||
* Services are ready and first window is about to open.
|
||||
*/
|
||||
Ready = 2,
|
||||
|
||||
/**
|
||||
* This phase signals a point in time after the window has opened
|
||||
* and is typically the best place to do work that is not required
|
||||
* for the window to open.
|
||||
*/
|
||||
AfterWindowOpen = 3
|
||||
}
|
||||
|
||||
export class LifecycleService extends Disposable implements ILifecycleService {
|
||||
|
@ -129,6 +162,11 @@ export class LifecycleService extends Disposable implements ILifecycleService {
|
|||
private readonly _onBeforeWindowUnload = this._register(new Emitter<IWindowUnloadEvent>());
|
||||
readonly onBeforeWindowUnload: Event<IWindowUnloadEvent> = this._onBeforeWindowUnload.event;
|
||||
|
||||
private _phase: LifecycleMainPhase = LifecycleMainPhase.Starting;
|
||||
get phase(): LifecycleMainPhase { return this._phase; }
|
||||
|
||||
private phaseWhen = new Map<LifecycleMainPhase, Barrier>();
|
||||
|
||||
constructor(
|
||||
@ILogService private readonly logService: ILogService,
|
||||
@IStateService private readonly stateService: IStateService
|
||||
|
@ -136,6 +174,7 @@ export class LifecycleService extends Disposable implements ILifecycleService {
|
|||
super();
|
||||
|
||||
this.handleRestarted();
|
||||
this.when(LifecycleMainPhase.Ready).then(() => this.registerListeners());
|
||||
}
|
||||
|
||||
private handleRestarted(): void {
|
||||
|
@ -146,10 +185,6 @@ export class LifecycleService extends Disposable implements ILifecycleService {
|
|||
}
|
||||
}
|
||||
|
||||
ready(): void {
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
|
||||
// before-quit: an event that is fired if application quit was
|
||||
|
@ -238,6 +273,40 @@ export class LifecycleService extends Disposable implements ILifecycleService {
|
|||
return this.pendingWillShutdownPromise;
|
||||
}
|
||||
|
||||
set phase(value: LifecycleMainPhase) {
|
||||
if (value < this.phase) {
|
||||
throw new Error('Lifecycle cannot go backwards');
|
||||
}
|
||||
|
||||
if (this._phase === value) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.logService.trace(`lifecycle (main): phase changed (value: ${value})`);
|
||||
|
||||
this._phase = value;
|
||||
|
||||
const barrier = this.phaseWhen.get(this._phase);
|
||||
if (barrier) {
|
||||
barrier.open();
|
||||
this.phaseWhen.delete(this._phase);
|
||||
}
|
||||
}
|
||||
|
||||
async when(phase: LifecycleMainPhase): Promise<void> {
|
||||
if (phase <= this._phase) {
|
||||
return;
|
||||
}
|
||||
|
||||
let barrier = this.phaseWhen.get(phase);
|
||||
if (!barrier) {
|
||||
barrier = new Barrier();
|
||||
this.phaseWhen.set(phase, barrier);
|
||||
}
|
||||
|
||||
await barrier.wait();
|
||||
}
|
||||
|
||||
registerWindow(window: ICodeWindow): void {
|
||||
|
||||
// track window count
|
||||
|
@ -390,10 +459,6 @@ export class LifecycleService extends Disposable implements ILifecycleService {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* A promise that completes to indicate if the quit request has been veto'd
|
||||
* by the user or not.
|
||||
*/
|
||||
quit(fromUpdate?: boolean): Promise<boolean /* veto */> {
|
||||
if (this.pendingQuitPromise) {
|
||||
return this.pendingQuitPromise;
|
||||
|
|
|
@ -27,22 +27,17 @@ export class FileStorage {
|
|||
}
|
||||
|
||||
async init(): Promise<void> {
|
||||
try {
|
||||
const contents = await readFile(this.dbPath);
|
||||
|
||||
try {
|
||||
this.lastFlushedSerializedDatabase = contents.toString();
|
||||
this._database = JSON.parse(this.lastFlushedSerializedDatabase);
|
||||
} catch (error) {
|
||||
this._database = {};
|
||||
}
|
||||
} catch (error) {
|
||||
if (error.code !== 'ENOENT') {
|
||||
this.onError(error);
|
||||
}
|
||||
|
||||
this._database = {};
|
||||
if (this._database) {
|
||||
return; // return if database was already loaded
|
||||
}
|
||||
|
||||
const database = await this.loadAsync();
|
||||
|
||||
if (this._database) {
|
||||
return; // return if database was already loaded
|
||||
}
|
||||
|
||||
this._database = database;
|
||||
}
|
||||
|
||||
private loadSync(): object {
|
||||
|
@ -59,6 +54,20 @@ export class FileStorage {
|
|||
}
|
||||
}
|
||||
|
||||
private async loadAsync(): Promise<object> {
|
||||
try {
|
||||
this.lastFlushedSerializedDatabase = (await readFile(this.dbPath)).toString();
|
||||
|
||||
return JSON.parse(this.lastFlushedSerializedDatabase);
|
||||
} catch (error) {
|
||||
if (error.code !== 'ENOENT') {
|
||||
this.onError(error);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
getItem<T>(key: string, defaultValue: T): T;
|
||||
getItem<T>(key: string, defaultValue?: T): T | undefined;
|
||||
getItem<T>(key: string, defaultValue?: T): T | undefined {
|
||||
|
|
|
@ -92,7 +92,6 @@ export interface IWindowsMainService {
|
|||
readonly onWindowClose: Event<number>;
|
||||
|
||||
// methods
|
||||
ready(initialUserEnv: IProcessEnvironment): void;
|
||||
reload(win: ICodeWindow, cli?: ParsedArgs): void;
|
||||
enterWorkspace(win: ICodeWindow, path: URI): Promise<IEnterWorkspaceResult | undefined>;
|
||||
closeWorkspace(win: ICodeWindow): void;
|
||||
|
|
4
src/vs/vscode.d.ts
vendored
4
src/vs/vscode.d.ts
vendored
|
@ -3515,6 +3515,10 @@ declare module 'vscode' {
|
|||
*
|
||||
* The editor will only resolve a completion item once.
|
||||
*
|
||||
* *Note* that accepting a completion item will not wait for it to be resolved. Because of that [`insertText`](#CompletionItem.insertText),
|
||||
* [`additionalTextEdits`](#CompletionItem.additionalTextEdits), and [`command`](#CompletionItem.command) should not
|
||||
* be changed when resolving an item.
|
||||
*
|
||||
* @param item A completion item currently active in the UI.
|
||||
* @param token A cancellation token.
|
||||
* @return The resolved completion item or a thenable that resolves to of such. It is OK to return the given
|
||||
|
|
|
@ -469,6 +469,9 @@ export class MainThreadComments extends Disposable implements MainThreadComments
|
|||
private _handlers = new Map<number, string>();
|
||||
private _commentControllers = new Map<number, MainThreadCommentController>();
|
||||
|
||||
private _activeCommentThread?: MainThreadCommentThread;
|
||||
private _input?: modes.CommentInput;
|
||||
|
||||
private _openPanelListener: IDisposable | null;
|
||||
|
||||
constructor(
|
||||
|
@ -483,6 +486,26 @@ export class MainThreadComments extends Disposable implements MainThreadComments
|
|||
this._disposables = [];
|
||||
this._activeCommentThreadDisposables = [];
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostComments);
|
||||
|
||||
this._disposables.push(this._commentService.onDidChangeActiveCommentThread(async thread => {
|
||||
let handle = (thread as MainThreadCommentThread).controllerHandle;
|
||||
let controller = this._commentControllers.get(handle);
|
||||
|
||||
if (!controller) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._activeCommentThreadDisposables = dispose(this._activeCommentThreadDisposables);
|
||||
this._activeCommentThread = thread as MainThreadCommentThread;
|
||||
controller.activeCommentThread = this._activeCommentThread;
|
||||
|
||||
this._activeCommentThreadDisposables.push(this._activeCommentThread.onDidChangeInput(input => { // todo, dispose
|
||||
this._input = input;
|
||||
this._proxy.$onCommentWidgetInputChange(handle, URI.parse(this._activeCommentThread!.resource), this._activeCommentThread!.range, this._input ? this._input.value : undefined);
|
||||
}));
|
||||
|
||||
await this._proxy.$onCommentWidgetInputChange(controller.handle, URI.parse(this._activeCommentThread!.resource), this._activeCommentThread.range, this._input ? this._input.value : undefined);
|
||||
}));
|
||||
}
|
||||
|
||||
$registerCommentController(handle: number, id: string, label: string): void {
|
||||
|
|
|
@ -236,7 +236,7 @@ export function createApiFactory(
|
|||
};
|
||||
|
||||
// namespace: env
|
||||
const env: typeof vscode.env = Object.freeze<typeof vscode.env>({
|
||||
const env: typeof vscode.env = {
|
||||
get machineId() { return initData.telemetryInfo.machineId; },
|
||||
get sessionId() { return initData.telemetryInfo.sessionId; },
|
||||
get language() { return initData.environment.appLanguage; },
|
||||
|
@ -257,7 +257,11 @@ export function createApiFactory(
|
|||
openExternal(uri: URI) {
|
||||
return extHostWindow.openUri(uri, { allowTunneling: !!initData.remoteAuthority });
|
||||
}
|
||||
});
|
||||
};
|
||||
if (!initData.environment.extensionTestsLocationURI) {
|
||||
// allow to patch env-function when running tests
|
||||
Object.freeze(env);
|
||||
}
|
||||
|
||||
// namespace: extensions
|
||||
const extensions: typeof vscode.extensions = {
|
||||
|
|
|
@ -619,6 +619,7 @@ export class SimpleCommentService implements ICommentService {
|
|||
onDidSetAllCommentThreads: Event<IWorkspaceCommentThreadsEvent> = Event.None;
|
||||
onDidUpdateCommentThreads: Event<ICommentThreadChangedEvent> = Event.None;
|
||||
onDidChangeActiveCommentingRange: Event<{ range: Range; commentingRangesInfo: CommentingRanges; }> = Event.None;
|
||||
onDidChangeActiveCommentThread: Event<any> = Event.None;
|
||||
onDidSetDataProvider: Event<void> = Event.None;
|
||||
onDidDeleteDataProvider: Event<string> = Event.None;
|
||||
setDocumentComments: any;
|
||||
|
@ -649,6 +650,7 @@ export class SimpleCommentService implements ICommentService {
|
|||
deleteReaction: any;
|
||||
getReactionGroup: any;
|
||||
toggleReaction: any;
|
||||
setActiveCommentThread: any;
|
||||
}
|
||||
registerSingleton(ICommentService, SimpleCommentService, true);
|
||||
//#endregion
|
||||
|
|
|
@ -424,12 +424,14 @@ export class CommentNode extends Disposable {
|
|||
uri: this._commentEditor.getModel()!.uri,
|
||||
value: this.comment.body.value
|
||||
};
|
||||
this.commentService.setActiveCommentThread(commentThread);
|
||||
|
||||
this._commentEditorDisposables.push(this._commentEditor.onDidFocusEditorWidget(() => {
|
||||
commentThread.input = {
|
||||
uri: this._commentEditor!.getModel()!.uri,
|
||||
value: this.comment.body.value
|
||||
};
|
||||
this.commentService.setActiveCommentThread(commentThread);
|
||||
}));
|
||||
|
||||
this._commentEditorDisposables.push(this._commentEditor.onDidChangeModelContent(e => {
|
||||
|
@ -439,6 +441,7 @@ export class CommentNode extends Disposable {
|
|||
let input = commentThread.input;
|
||||
input.value = newVal;
|
||||
commentThread.input = input;
|
||||
this.commentService.setActiveCommentThread(commentThread);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
@ -486,6 +489,7 @@ export class CommentNode extends Disposable {
|
|||
uri: this._commentEditor.getModel()!.uri,
|
||||
value: newBody
|
||||
};
|
||||
this.commentService.setActiveCommentThread(commentThread);
|
||||
let commandId = this.comment.editCommand.id;
|
||||
let args = this.comment.editCommand.arguments || [];
|
||||
|
||||
|
@ -523,6 +527,7 @@ export class CommentNode extends Disposable {
|
|||
if (result.confirmed) {
|
||||
try {
|
||||
if (this.comment.deleteCommand) {
|
||||
this.commentService.setActiveCommentThread(this.commentThread);
|
||||
let commandId = this.comment.deleteCommand.id;
|
||||
let args = this.comment.deleteCommand.arguments || [];
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ export interface ICommentService {
|
|||
readonly onDidSetResourceCommentInfos: Event<IResourceCommentThreadEvent>;
|
||||
readonly onDidSetAllCommentThreads: Event<IWorkspaceCommentThreadsEvent>;
|
||||
readonly onDidUpdateCommentThreads: Event<ICommentThreadChangedEvent>;
|
||||
readonly onDidChangeActiveCommentThread: Event<CommentThread | null>;
|
||||
readonly onDidChangeActiveCommentingRange: Event<{ range: Range, commentingRangesInfo: CommentingRanges }>;
|
||||
readonly onDidSetDataProvider: Event<void>;
|
||||
readonly onDidDeleteDataProvider: Event<string>;
|
||||
|
@ -69,6 +70,7 @@ export interface ICommentService {
|
|||
deleteReaction(owner: string, resource: URI, comment: Comment, reaction: CommentReaction): Promise<void>;
|
||||
getReactionGroup(owner: string): CommentReaction[] | undefined;
|
||||
toggleReaction(owner: string, resource: URI, thread: CommentThread2, comment: Comment, reaction: CommentReaction): Promise<void>;
|
||||
setActiveCommentThread(commentThread: CommentThread | null): void;
|
||||
}
|
||||
|
||||
export class CommentService extends Disposable implements ICommentService {
|
||||
|
@ -89,6 +91,9 @@ export class CommentService extends Disposable implements ICommentService {
|
|||
private readonly _onDidUpdateCommentThreads: Emitter<ICommentThreadChangedEvent> = this._register(new Emitter<ICommentThreadChangedEvent>());
|
||||
readonly onDidUpdateCommentThreads: Event<ICommentThreadChangedEvent> = this._onDidUpdateCommentThreads.event;
|
||||
|
||||
private readonly _onDidChangeActiveCommentThread = this._register(new Emitter<CommentThread | null>());
|
||||
readonly onDidChangeActiveCommentThread = this._onDidChangeActiveCommentThread.event;
|
||||
|
||||
private readonly _onDidChangeActiveCommentingRange: Emitter<{
|
||||
range: Range, commentingRangesInfo:
|
||||
CommentingRanges
|
||||
|
@ -109,6 +114,10 @@ export class CommentService extends Disposable implements ICommentService {
|
|||
super();
|
||||
}
|
||||
|
||||
setActiveCommentThread(commentThread: CommentThread | null) {
|
||||
this._onDidChangeActiveCommentThread.fire(commentThread);
|
||||
}
|
||||
|
||||
setDocumentComments(resource: URI, commentInfos: ICommentInfo[]): void {
|
||||
this._onDidSetResourceCommentInfos.fire({ resource, commentInfos });
|
||||
}
|
||||
|
|
|
@ -211,6 +211,10 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
|
|||
|
||||
this._bodyElement = <HTMLDivElement>dom.$('.body');
|
||||
container.appendChild(this._bodyElement);
|
||||
|
||||
dom.addDisposableListener(this._bodyElement, dom.EventType.FOCUS_IN, e => {
|
||||
this.commentService.setActiveCommentThread(this._commentThread);
|
||||
});
|
||||
}
|
||||
|
||||
protected _fillHead(container: HTMLElement): void {
|
||||
|
@ -265,6 +269,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
|
|||
} else {
|
||||
const deleteCommand = (this._commentThread as modes.CommentThread2).deleteCommand;
|
||||
if (deleteCommand) {
|
||||
this.commentService.setActiveCommentThread(this._commentThread);
|
||||
return this.commandService.executeCommand(deleteCommand.id, ...(deleteCommand.arguments || []));
|
||||
} else if (this._commentEditor.getValue() === '') {
|
||||
this.commentService.disposeCommentThread(this._owner, this._commentThread.threadId!);
|
||||
|
@ -516,6 +521,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
|
|||
uri: this._commentEditor.getModel()!.uri,
|
||||
value: this._commentEditor.getValue()
|
||||
};
|
||||
this.commentService.setActiveCommentThread(this._commentThread);
|
||||
}));
|
||||
|
||||
this._commentThreadDisposables.push(this._commentEditor.getModel()!.onDidChangeContent(() => {
|
||||
|
@ -526,6 +532,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
|
|||
newInput.value = modelContent;
|
||||
thread.input = newInput;
|
||||
}
|
||||
this.commentService.setActiveCommentThread(this._commentThread);
|
||||
}));
|
||||
|
||||
this._commentThreadDisposables.push((this._commentThread as modes.CommentThread2).onDidChangeInput(input => {
|
||||
|
@ -727,6 +734,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
|
|||
uri: this._commentEditor.getModel()!.uri,
|
||||
value: this._commentEditor.getValue()
|
||||
};
|
||||
this.commentService.setActiveCommentThread(this._commentThread);
|
||||
await this.commandService.executeCommand(acceptInputCommand.id, ...(acceptInputCommand.arguments || []));
|
||||
}));
|
||||
|
||||
|
@ -751,6 +759,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
|
|||
uri: this._commentEditor.getModel()!.uri,
|
||||
value: this._commentEditor.getValue()
|
||||
};
|
||||
this.commentService.setActiveCommentThread(this._commentThread);
|
||||
await this.commandService.executeCommand(command.id, ...(command.arguments || []));
|
||||
}));
|
||||
});
|
||||
|
@ -821,6 +830,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
|
|||
uri: this._commentEditor.getModel()!.uri,
|
||||
value: this._commentEditor.getValue()
|
||||
};
|
||||
this.commentService.setActiveCommentThread(this._commentThread);
|
||||
let commandId = commentThread.acceptInputCommand.id;
|
||||
let args = commentThread.acceptInputCommand.arguments || [];
|
||||
|
||||
|
|
|
@ -303,7 +303,7 @@ export class Scope extends ExpressionContainer implements IScope {
|
|||
indexedVariables?: number,
|
||||
public range?: IRange
|
||||
) {
|
||||
super(stackFrame.thread.session, reference, `scope:${stackFrame.getId()}:${name}:${index}`, namedVariables, indexedVariables);
|
||||
super(stackFrame.thread.session, reference, `scope:${name}:${index}`, namedVariables, indexedVariables);
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
|
|
|
@ -26,7 +26,6 @@ export const UNKNOWN_SOURCE_LABEL = nls.localize('unknownSource', "Unknown Sourc
|
|||
* | | | |
|
||||
* scheme source.path session id source.reference
|
||||
*
|
||||
* the arbitrary_path and the session id are encoded with 'encodeURIComponent'
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -49,7 +48,11 @@ export class Source {
|
|||
}
|
||||
|
||||
if (typeof this.raw.sourceReference === 'number' && this.raw.sourceReference > 0) {
|
||||
this.uri = uri.parse(`${DEBUG_SCHEME}:${encodeURIComponent(path)}?session=${encodeURIComponent(sessionId)}&ref=${this.raw.sourceReference}`);
|
||||
this.uri = uri.from({
|
||||
scheme: DEBUG_SCHEME,
|
||||
path,
|
||||
query: `session=${sessionId}&ref=${this.raw.sourceReference}`
|
||||
});
|
||||
} else {
|
||||
if (isUri(path)) { // path looks like a uri
|
||||
this.uri = uri.parse(path);
|
||||
|
@ -60,7 +63,11 @@ export class Source {
|
|||
} else {
|
||||
// path is relative: since VS Code cannot deal with this by itself
|
||||
// create a debug url that will result in a DAP 'source' request when the url is resolved.
|
||||
this.uri = uri.parse(`${DEBUG_SCHEME}:${encodeURIComponent(path)}?session=${encodeURIComponent(sessionId)}`);
|
||||
this.uri = uri.from({
|
||||
scheme: DEBUG_SCHEME,
|
||||
path,
|
||||
query: `session=${sessionId}`
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -118,7 +125,7 @@ export class Source {
|
|||
if (pair.length === 2) {
|
||||
switch (pair[0]) {
|
||||
case 'session':
|
||||
sessionId = decodeURIComponent(pair[1]);
|
||||
sessionId = pair[1];
|
||||
break;
|
||||
case 'ref':
|
||||
sourceReference = parseInt(pair[1]);
|
||||
|
|
|
@ -5,14 +5,11 @@
|
|||
|
||||
import * as nls from 'vs/nls';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IInstantiationService, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IExtensionHostProfile, ProfileSession, IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { append, $, addDisposableListener } from 'vs/base/browser/dom';
|
||||
import { IStatusbarRegistry, StatusbarItemDescriptor, Extensions, IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar';
|
||||
import { StatusbarAlignment } from 'vs/platform/statusbar/common/statusbar';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { StatusbarAlignment, IStatusbarService, IStatusbarEntryAccessor, IStatusbarEntry } from 'vs/platform/statusbar/common/statusbar';
|
||||
import { IExtensionHostProfileService, ProfileSessionState } from 'vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IWindowsService } from 'vs/platform/windows/common/windows';
|
||||
|
@ -22,10 +19,11 @@ import product from 'vs/platform/product/node/product';
|
|||
import { RuntimeExtensionsInput } from 'vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsInput';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { ExtensionHostProfiler } from 'vs/workbench/services/extensions/electron-browser/extensionHostProfiler';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
|
||||
export class ExtensionHostProfileService extends Disposable implements IExtensionHostProfileService {
|
||||
|
||||
_serviceBrand: any;
|
||||
_serviceBrand: ServiceIdentifier<IExtensionHostProfileService>;
|
||||
|
||||
private readonly _onDidChangeState: Emitter<void> = this._register(new Emitter<void>());
|
||||
public readonly onDidChangeState: Event<void> = this._onDidChangeState.event;
|
||||
|
@ -38,6 +36,9 @@ export class ExtensionHostProfileService extends Disposable implements IExtensio
|
|||
private _profileSession: ProfileSession | null;
|
||||
private _state: ProfileSessionState;
|
||||
|
||||
private profilingStatusBarIndicator: IStatusbarEntryAccessor | undefined;
|
||||
private profilingStatusBarIndicatorLabelUpdater: IDisposable | undefined;
|
||||
|
||||
public get state() { return this._state; }
|
||||
public get lastProfile() { return this._profile; }
|
||||
|
||||
|
@ -46,12 +47,18 @@ export class ExtensionHostProfileService extends Disposable implements IExtensio
|
|||
@IEditorService private readonly _editorService: IEditorService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@IWindowsService private readonly _windowsService: IWindowsService,
|
||||
@IDialogService private readonly _dialogService: IDialogService
|
||||
@IDialogService private readonly _dialogService: IDialogService,
|
||||
@IStatusbarService private readonly _statusbarService: IStatusbarService,
|
||||
) {
|
||||
super();
|
||||
this._profile = null;
|
||||
this._profileSession = null;
|
||||
this._setState(ProfileSessionState.None);
|
||||
|
||||
CommandsRegistry.registerCommand('workbench.action.extensionHostProfilder.stop', () => {
|
||||
this.stopProfiling();
|
||||
this._editorService.openEditor(this._instantiationService.createInstance(RuntimeExtensionsInput), { revealIfOpened: true });
|
||||
});
|
||||
}
|
||||
|
||||
private _setState(state: ProfileSessionState): void {
|
||||
|
@ -61,17 +68,48 @@ export class ExtensionHostProfileService extends Disposable implements IExtensio
|
|||
this._state = state;
|
||||
|
||||
if (this._state === ProfileSessionState.Running) {
|
||||
ProfileExtHostStatusbarItem.instance.show(() => {
|
||||
this.stopProfiling();
|
||||
this._editorService.openEditor(this._instantiationService.createInstance(RuntimeExtensionsInput), { revealIfOpened: true });
|
||||
});
|
||||
this.updateProfilingStatusBarIndicator(true);
|
||||
} else if (this._state === ProfileSessionState.Stopping) {
|
||||
ProfileExtHostStatusbarItem.instance.hide();
|
||||
this.updateProfilingStatusBarIndicator(false);
|
||||
}
|
||||
|
||||
this._onDidChangeState.fire(undefined);
|
||||
}
|
||||
|
||||
private updateProfilingStatusBarIndicator(visible: boolean): void {
|
||||
if (this.profilingStatusBarIndicatorLabelUpdater) {
|
||||
this.profilingStatusBarIndicatorLabelUpdater.dispose();
|
||||
this.profilingStatusBarIndicatorLabelUpdater = undefined;
|
||||
}
|
||||
|
||||
if (visible) {
|
||||
const indicator: IStatusbarEntry = {
|
||||
text: nls.localize('profilingExtensionHost', "$(sync~spin) Profiling Extension Host"),
|
||||
tooltip: nls.localize('selectAndStartDebug', "Click to stop profiling."),
|
||||
command: 'workbench.action.extensionHostProfilder.stop'
|
||||
};
|
||||
|
||||
const timeStarted = Date.now();
|
||||
const handle = setInterval(() => {
|
||||
if (this.profilingStatusBarIndicator) {
|
||||
this.profilingStatusBarIndicator.update({ ...indicator, text: nls.localize('profilingExtensionHostTime', "$(sync~spin) Profiling Extension Host ({0} sec)", Math.round((new Date().getTime() - timeStarted) / 1000)), });
|
||||
}
|
||||
}, 1000);
|
||||
this.profilingStatusBarIndicatorLabelUpdater = toDisposable(() => clearInterval(handle));
|
||||
|
||||
if (!this.profilingStatusBarIndicator) {
|
||||
this.profilingStatusBarIndicator = this._statusbarService.addEntry(indicator, StatusbarAlignment.RIGHT);
|
||||
} else {
|
||||
this.profilingStatusBarIndicator.update(indicator);
|
||||
}
|
||||
} else {
|
||||
if (this.profilingStatusBarIndicator) {
|
||||
this.profilingStatusBarIndicator.dispose();
|
||||
this.profilingStatusBarIndicator = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public startProfiling(): Promise<any> | null {
|
||||
if (this._state !== ProfileSessionState.None) {
|
||||
return null;
|
||||
|
@ -134,76 +172,3 @@ export class ExtensionHostProfileService extends Disposable implements IExtensio
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
export class ProfileExtHostStatusbarItem implements IStatusbarItem {
|
||||
|
||||
public static instance: ProfileExtHostStatusbarItem;
|
||||
|
||||
private toDispose: IDisposable[];
|
||||
private statusBarItem: HTMLElement;
|
||||
private label: HTMLElement;
|
||||
private timeStarted: number;
|
||||
private labelUpdater: any;
|
||||
private clickHandler: (() => void) | null;
|
||||
|
||||
constructor() {
|
||||
ProfileExtHostStatusbarItem.instance = this;
|
||||
this.toDispose = [];
|
||||
this.timeStarted = 0;
|
||||
}
|
||||
|
||||
public show(clickHandler: () => void) {
|
||||
this.clickHandler = clickHandler;
|
||||
if (this.timeStarted === 0) {
|
||||
this.timeStarted = new Date().getTime();
|
||||
this.statusBarItem.hidden = false;
|
||||
this.labelUpdater = setInterval(() => {
|
||||
this.updateLabel();
|
||||
}, 1000);
|
||||
this.updateLabel();
|
||||
}
|
||||
}
|
||||
|
||||
public hide() {
|
||||
this.clickHandler = null;
|
||||
this.statusBarItem.hidden = true;
|
||||
this.timeStarted = 0;
|
||||
clearInterval(this.labelUpdater);
|
||||
this.labelUpdater = null;
|
||||
}
|
||||
|
||||
public render(container: HTMLElement): IDisposable {
|
||||
if (!this.statusBarItem && container) {
|
||||
this.statusBarItem = append(container, $('.profileExtHost-statusbar-item'));
|
||||
this.toDispose.push(addDisposableListener(this.statusBarItem, 'click', () => {
|
||||
if (this.clickHandler) {
|
||||
this.clickHandler();
|
||||
}
|
||||
}));
|
||||
this.statusBarItem.title = nls.localize('selectAndStartDebug', "Click to stop profiling.");
|
||||
const a = append(this.statusBarItem, $('a'));
|
||||
append(a, $('.icon'));
|
||||
this.label = append(a, $('span.label'));
|
||||
this.updateLabel();
|
||||
this.statusBarItem.hidden = true;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private updateLabel() {
|
||||
let label = 'Profiling Extension Host';
|
||||
if (this.timeStarted > 0) {
|
||||
let secondsRecoreded = (new Date().getTime() - this.timeStarted) / 1000;
|
||||
label = `Profiling Extension Host (${Math.round(secondsRecoreded)} sec)`;
|
||||
}
|
||||
this.label.textContent = label;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.toDispose = dispose(this.toDispose);
|
||||
}
|
||||
}
|
||||
|
||||
Registry.as<IStatusbarRegistry>(Extensions.Statusbar).registerStatusbarItem(
|
||||
new StatusbarItemDescriptor(ProfileExtHostStatusbarItem, StatusbarAlignment.RIGHT)
|
||||
);
|
||||
|
|
|
@ -7,7 +7,7 @@ import { localize } from 'vs/nls';
|
|||
import { dispose, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { isPromiseCanceledError } from 'vs/base/common/errors';
|
||||
import { isPromiseCanceledError, getErrorMessage } from 'vs/base/common/errors';
|
||||
import { PagedModel, IPagedModel, IPager, DelayedPagedModel } from 'vs/base/common/paging';
|
||||
import { SortBy, SortOrder, IQueryOptions, IExtensionTipsService, IExtensionRecommendation, IExtensionManagementServer, IExtensionManagementServerService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
|
@ -69,9 +69,13 @@ export interface ExtensionsListViewOptions extends IViewletViewOptions {
|
|||
server?: IExtensionManagementServer;
|
||||
}
|
||||
|
||||
class ExtensionListViewWarning extends Error { }
|
||||
|
||||
export class ExtensionsListView extends ViewletPanel {
|
||||
|
||||
private readonly server: IExtensionManagementServer | undefined;
|
||||
private messageContainer: HTMLElement;
|
||||
private messageStatus: HTMLElement;
|
||||
private messageBox: HTMLElement;
|
||||
private extensionsList: HTMLElement;
|
||||
private badge: CountBadge;
|
||||
|
@ -117,7 +121,9 @@ export class ExtensionsListView extends ViewletPanel {
|
|||
|
||||
renderBody(container: HTMLElement): void {
|
||||
this.extensionsList = append(container, $('.extensions-list'));
|
||||
this.messageBox = append(container, $('.message'));
|
||||
this.messageContainer = append(container, $('.message-container'));
|
||||
this.messageStatus = append(this.messageContainer, $(''));
|
||||
this.messageBox = append(this.messageContainer, $('.message'));
|
||||
const delegate = new Delegate();
|
||||
const extensionsViewState = new ExtensionsViewState();
|
||||
const renderer = this.instantiationService.createInstance(Renderer, extensionsViewState);
|
||||
|
@ -178,12 +184,11 @@ export class ExtensionsListView extends ViewletPanel {
|
|||
};
|
||||
|
||||
|
||||
const errorCallback = (e: Error) => {
|
||||
const errorCallback = (e: any) => {
|
||||
const model = new PagedModel([]);
|
||||
if (!isPromiseCanceledError(e)) {
|
||||
this.queryRequest = null;
|
||||
console.warn('Error querying extensions gallery', e);
|
||||
this.setModel(model, true);
|
||||
this.setModel(model, e);
|
||||
}
|
||||
return this.list ? this.list.model : model;
|
||||
};
|
||||
|
@ -238,7 +243,11 @@ export class ExtensionsListView extends ViewletPanel {
|
|||
if (ExtensionsListView.isLocalExtensionsQuery(query.value) || /@builtin/.test(query.value)) {
|
||||
return this.queryLocal(query, options);
|
||||
}
|
||||
return this.queryGallery(query, options, token);
|
||||
return this.queryGallery(query, options, token)
|
||||
.then(null, e => {
|
||||
console.warn('Error querying extensions gallery', getErrorMessage(e));
|
||||
return Promise.reject(new ExtensionListViewWarning(localize('galleryError', "We cannot connect to the Extensions Marketplace at this time, please try again later.")));
|
||||
});
|
||||
}
|
||||
|
||||
private async queryByIds(ids: string[], options: IQueryOptions, token: CancellationToken): Promise<IPagedModel<IExtension>> {
|
||||
|
@ -696,23 +705,30 @@ export class ExtensionsListView extends ViewletPanel {
|
|||
});
|
||||
}
|
||||
|
||||
private setModel(model: IPagedModel<IExtension>, isGalleryError?: boolean) {
|
||||
private setModel(model: IPagedModel<IExtension>, error?: any) {
|
||||
if (this.list) {
|
||||
this.list.model = new DelayedPagedModel(model);
|
||||
this.list.scrollTop = 0;
|
||||
const count = this.count();
|
||||
|
||||
toggleClass(this.extensionsList, 'hidden', count === 0);
|
||||
toggleClass(this.messageBox, 'hidden', count > 0);
|
||||
toggleClass(this.messageContainer, 'hidden', count > 0);
|
||||
this.badge.setCount(count);
|
||||
|
||||
if (count === 0 && this.isBodyVisible()) {
|
||||
this.messageBox.textContent = isGalleryError ? localize('galleryError', "We cannot connect to the Extensions Marketplace at this time, please try again later.") : localize('no extensions found', "No extensions found.");
|
||||
if (isGalleryError) {
|
||||
alert(this.messageBox.textContent);
|
||||
if (error) {
|
||||
if (error instanceof ExtensionListViewWarning) {
|
||||
this.messageStatus.className = 'message-status warning';
|
||||
this.messageBox.textContent = getErrorMessage(error);
|
||||
} else {
|
||||
this.messageStatus.className = 'message-status error';
|
||||
this.messageBox.textContent = localize('error', "Error while loading extensions. {0}", getErrorMessage(error));
|
||||
}
|
||||
} else {
|
||||
this.messageStatus.className = '';
|
||||
this.messageBox.textContent = localize('no extensions found', "No extensions found.");
|
||||
}
|
||||
} else {
|
||||
this.messageBox.textContent = '';
|
||||
alert(this.messageBox.textContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
}
|
||||
|
||||
.extensions-viewlet > .extensions .extensions-list.hidden,
|
||||
.extensions-viewlet > .extensions .message.hidden {
|
||||
.extensions-viewlet > .extensions .message-container.hidden {
|
||||
display: none;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
@ -51,9 +51,35 @@
|
|||
flex: 1;
|
||||
}
|
||||
|
||||
.extensions-viewlet > .extensions .message {
|
||||
.extensions-viewlet > .extensions .message-container {
|
||||
padding: 5px 9px 5px 16px;
|
||||
cursor: default;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.extensions-viewlet > .extensions .message-container .message-status {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
.extensions-viewlet > .extensions .message-container .message-status.warning {
|
||||
background: url('status-warning.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.extensions-viewlet > .extensions .message-container .message-status.error {
|
||||
background: url('status-error.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs-dark .extensions-viewlet > .extensions .message-container .message-status.warning {
|
||||
background: url('status-warning-inverse.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs-dark .extensions-viewlet > .extensions .message-container .message-status.error {
|
||||
background: url('status-error-inverse.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.extensions-viewlet > .extensions .message-container .message {
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.extensions-viewlet > .extensions .monaco-list-row > .bookmark {
|
||||
|
|
|
@ -36,20 +36,3 @@
|
|||
.runtime-extensions-editor .monaco-action-bar .actions-container {
|
||||
justify-content: left;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.statusbar .profileExtHost-statusbar-item .icon {
|
||||
background: url('profile-stop.svg') no-repeat;
|
||||
display: inline-block;
|
||||
padding-right: 2px;
|
||||
padding-bottom: 2px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
vertical-align: middle;
|
||||
animation:fade 1000ms infinite;
|
||||
}
|
||||
|
||||
@keyframes fade {
|
||||
from { opacity: 1.0; }
|
||||
50% { opacity: 0.5; }
|
||||
to { opacity: 1.0; }
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" height="16" width="16"><circle cx="8" cy="8" r="6" fill="#1E1E1E"/><path d="M8 3C5.238 3 3 5.238 3 8s2.238 5 5 5 5-2.238 5-5-2.238-5-5-5zm3 7l-1 1-2-2-2 2-1-1 2-2.027L5 6l1-1 2 2 2-2 1 1-2 1.973L11 10z" fill="#F48771"/><path fill="#252526" d="M11 6l-1-1-2 2-2-2-1 1 2 1.973L5 10l1 1 2-2 2 2 1-1-2-2.027z"/></svg>
|
After Width: | Height: | Size: 372 B |
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" enable-background="new 0 0 16 16" height="16" width="16"><circle cx="8" cy="8" r="6" fill="#F6F6F6"/><path d="M8 3C5.238 3 3 5.238 3 8s2.238 5 5 5 5-2.238 5-5-2.238-5-5-5zm3 7l-1 1-2-2-2 2-1-1 2-2.027L5 6l1-1 2 2 2-2 1 1-2 1.973L11 10z" fill="#E51400"/><path fill="#fff" d="M11 6l-1-1-2 2-2-2-1 1 2 1.973L5 10l1 1 2-2 2 2 1-1-2-2.027z"/></svg>
|
After Width: | Height: | Size: 403 B |
|
@ -24,6 +24,7 @@ import { URI } from 'vs/base/common/uri';
|
|||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import * as perf from 'vs/base/common/performance';
|
||||
|
||||
class PartsSplash {
|
||||
|
||||
|
@ -45,14 +46,20 @@ class PartsSplash {
|
|||
@IEditorGroupsService editorGroupsService: IEditorGroupsService,
|
||||
@IConfigurationService configService: IConfigurationService,
|
||||
) {
|
||||
lifecycleService.when(LifecyclePhase.Restored).then(_ => this._removePartsSplash());
|
||||
lifecycleService.when(LifecyclePhase.Restored).then(_ => {
|
||||
this._removePartsSplash();
|
||||
perf.mark('didRemovePartsSplash');
|
||||
});
|
||||
Event.debounce(Event.any<any>(
|
||||
onDidChangeFullscreen,
|
||||
editorGroupsService.onDidLayout
|
||||
), () => { }, 800)(this._savePartsSplash, this, this._disposables);
|
||||
|
||||
configService.onDidChangeConfiguration(e => {
|
||||
this._didChangeTitleBarStyle = e.affectsConfiguration('window.titleBarStyle');
|
||||
if (e.affectsConfiguration('window.titleBarStyle')) {
|
||||
this._didChangeTitleBarStyle = true;
|
||||
this._savePartsSplash();
|
||||
}
|
||||
}, this, this._disposables);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,9 @@ yarn smoketest
|
|||
|
||||
# Build
|
||||
yarn smoketest --build PATH_TO_NEW_BUILD_PARENT_FOLDER --stable-build PATH_TO_LAST_STABLE_BUILD_PARENT_FOLDER
|
||||
|
||||
# Remote
|
||||
yarn smoketest --build PATH_TO_NEW_BUILD_PARENT_FOLDER --remote
|
||||
```
|
||||
|
||||
### Run for a release
|
||||
|
|
|
@ -10,7 +10,7 @@ export class References {
|
|||
private static readonly REFERENCES_WIDGET = '.monaco-editor .zone-widget .zone-widget-container.peekview-widget.reference-zone-widget.results-loaded';
|
||||
private static readonly REFERENCES_TITLE_FILE_NAME = `${References.REFERENCES_WIDGET} .head .peekview-title .filename`;
|
||||
private static readonly REFERENCES_TITLE_COUNT = `${References.REFERENCES_WIDGET} .head .peekview-title .meta`;
|
||||
private static readonly REFERENCES = `${References.REFERENCES_WIDGET} .body .ref-tree.inline .monaco-list-row .reference`;
|
||||
private static readonly REFERENCES = `${References.REFERENCES_WIDGET} .body .ref-tree.inline .monaco-list-row .highlight`;
|
||||
|
||||
constructor(private code: Code) { }
|
||||
|
||||
|
|
|
@ -541,7 +541,6 @@
|
|||
"**/vs/base/parts/**/{common,browser,node,electron-main}/**",
|
||||
"**/vs/platform/**/{common,browser,node,electron-main}/**",
|
||||
"**/vs/code/**/{common,browser,node,electron-main}/**",
|
||||
"**/vs/code/code.main",
|
||||
"*" // node modules
|
||||
]
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue