Merge remote-tracking branch 'origin/master' into misolori/new-icons

This commit is contained in:
Miguel Solorio 2019-06-28 11:05:33 -07:00
commit 05ee90f7fd
614 changed files with 10271 additions and 9127 deletions

View file

@ -105,7 +105,7 @@
{
type: 'comment',
name: 'a11ymas',
allowUsers: ['AccessibilityTestingTeam-TCS'],
allowUsers: ['AccessibilityTestingTeam-TCS', 'dixitsonali95', 'Mohini78', 'ChitrarupaSharma', 'mspatil110', 'umasarath52', 'v-umnaik'],
action: 'updateLabels',
addLabel: 'a11ymas'
},

3
.gitignore vendored
View file

@ -18,6 +18,9 @@ out-vscode-min/
out-vscode-reh/
out-vscode-reh-min/
out-vscode-reh-pkg/
out-vscode-web/
out-vscode-web-min/
out-vscode-web-pkg/
src/vs/server
resources/server
build/node_modules

View file

@ -56,5 +56,9 @@
"url": "./.vscode/cglicenses.schema.json"
}
],
"git.ignoreLimitWarning": true
}
"git.ignoreLimitWarning": true,
"remote.extensionKind": {
"msjsdiag.debugger-for-chrome": "workspace"
},
"typescript.experimental.useSeparateSyntaxServer": true
}

View file

@ -1,3 +1,3 @@
disturl "https://atom.io/download/electron"
target "4.2.4"
target "4.2.5"
runtime "electron"

View file

@ -5,108 +5,70 @@ Do Not Translate or Localize
This project incorporates components from the projects listed below. The original copyright notices and the licenses under which Microsoft received such components are set forth below. Microsoft reserves all rights not expressly granted herein, whether by implication, estoppel or otherwise.
1. atom/language-c version 0.58.1 (https://github.com/atom/language-c)
2. atom/language-clojure version 0.22.7 (https://github.com/atom/language-clojure)
3. atom/language-coffee-script version 0.49.3 (https://github.com/atom/language-coffee-script)
4. atom/language-java version 0.31.2 (https://github.com/atom/language-java)
5. atom/language-objective-c version 0.15.0 (https://github.com/atom/language-objective-c)
6. atom/language-sass version 0.61.4 (https://github.com/atom/language-sass)
7. atom/language-shellscript version 0.26.0 (https://github.com/atom/language-shellscript)
8. atom/language-xml version 0.35.2 (https://github.com/atom/language-xml)
9. Colorsublime-Themes version 0.1.0 (https://github.com/Colorsublime/Colorsublime-Themes)
10. daaain/Handlebars version 1.8.0 (https://github.com/daaain/Handlebars)
11. davidrios/pug-tmbundle (https://github.com/davidrios/pug-tmbundle)
12. definitelytyped (https://github.com/DefinitelyTyped/DefinitelyTyped)
13. demyte/language-cshtml version 0.3.0 (https://github.com/demyte/language-cshtml)
14. Document Object Model version 4.0.0 (https://www.w3.org/DOM/)
15. dotnet/csharp-tmLanguage version 0.1.0 (https://github.com/dotnet/csharp-tmLanguage)
16. expand-abbreviation version 0.5.8 (https://github.com/emmetio/expand-abbreviation)
17. fadeevab/make.tmbundle (https://github.com/fadeevab/make.tmbundle)
18. freebroccolo/atom-language-swift (https://github.com/freebroccolo/atom-language-swift)
19. HTML 5.1 W3C Working Draft version 08 October 2015 (http://www.w3.org/TR/2015/WD-html51-20151008/)
20. Ikuyadeu/vscode-R version 0.5.5 (https://github.com/Ikuyadeu/vscode-R)
21. Ionic documentation version 1.2.4 (https://github.com/ionic-team/ionic-site)
22. ionide/ionide-fsgrammar (https://github.com/ionide/ionide-fsgrammar)
23. jeff-hykin/cpp-textmate-grammar version 1.8.15 (https://github.com/jeff-hykin/cpp-textmate-grammar)
24. js-beautify version 1.6.8 (https://github.com/beautify-web/js-beautify)
25. Jxck/assert version 1.0.0 (https://github.com/Jxck/assert)
26. language-docker (https://github.com/moby/moby)
27. language-go version 0.44.3 (https://github.com/atom/language-go)
28. language-less version 0.34.2 (https://github.com/atom/language-less)
29. language-php version 0.44.1 (https://github.com/atom/language-php)
30. language-rust version 0.4.12 (https://github.com/zargony/atom-language-rust)
31. MagicStack/MagicPython version 1.1.1 (https://github.com/MagicStack/MagicPython)
32. marked version 0.6.2 (https://github.com/markedjs/marked)
33. mdn-data version 1.1.12 (https://github.com/mdn/data)
34. Microsoft/TypeScript-TmLanguage version 0.0.1 (https://github.com/Microsoft/TypeScript-TmLanguage)
35. Microsoft/vscode-JSON.tmLanguage (https://github.com/Microsoft/vscode-JSON.tmLanguage)
36. Microsoft/vscode-mssql version 1.4.0 (https://github.com/Microsoft/vscode-mssql)
37. mmims/language-batchfile version 0.7.5 (https://github.com/mmims/language-batchfile)
38. octicons version 8.3.0 (https://github.com/primer/octicons)
39. octref/language-css version 0.42.11 (https://github.com/octref/language-css)
40. PowerShell/EditorSyntax (https://github.com/powershell/editorsyntax)
41. promise-polyfill version 8.0.0 (https://github.com/taylorhakes/promise-polyfill)
42. seti-ui version 0.1.0 (https://github.com/jesseweed/seti-ui)
43. shaders-tmLanguage version 0.1.0 (https://github.com/tgjones/shaders-tmLanguage)
44. textmate/asp.vb.net.tmbundle (https://github.com/textmate/asp.vb.net.tmbundle)
45. textmate/c.tmbundle (https://github.com/textmate/c.tmbundle)
46. textmate/diff.tmbundle (https://github.com/textmate/diff.tmbundle)
47. textmate/git.tmbundle (https://github.com/textmate/git.tmbundle)
48. textmate/groovy.tmbundle (https://github.com/textmate/groovy.tmbundle)
49. textmate/html.tmbundle (https://github.com/textmate/html.tmbundle)
50. textmate/ini.tmbundle (https://github.com/textmate/ini.tmbundle)
51. textmate/javascript.tmbundle (https://github.com/textmate/javascript.tmbundle)
52. textmate/lua.tmbundle (https://github.com/textmate/lua.tmbundle)
53. textmate/markdown.tmbundle (https://github.com/textmate/markdown.tmbundle)
54. textmate/perl.tmbundle (https://github.com/textmate/perl.tmbundle)
55. textmate/ruby.tmbundle (https://github.com/textmate/ruby.tmbundle)
56. textmate/yaml.tmbundle (https://github.com/textmate/yaml.tmbundle)
57. TypeScript-TmLanguage version 0.1.8 (https://github.com/Microsoft/TypeScript-TmLanguage)
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.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. atom/language-clojure version 0.22.7 (https://github.com/atom/language-clojure)
2. atom/language-coffee-script version 0.49.3 (https://github.com/atom/language-coffee-script)
3. atom/language-java version 0.31.2 (https://github.com/atom/language-java)
4. atom/language-sass version 0.61.4 (https://github.com/atom/language-sass)
5. atom/language-shellscript version 0.26.0 (https://github.com/atom/language-shellscript)
6. atom/language-xml version 0.35.2 (https://github.com/atom/language-xml)
7. Colorsublime-Themes version 0.1.0 (https://github.com/Colorsublime/Colorsublime-Themes)
8. daaain/Handlebars version 1.8.0 (https://github.com/daaain/Handlebars)
9. davidrios/pug-tmbundle (https://github.com/davidrios/pug-tmbundle)
10. definitelytyped (https://github.com/DefinitelyTyped/DefinitelyTyped)
11. demyte/language-cshtml version 0.3.0 (https://github.com/demyte/language-cshtml)
12. Document Object Model version 4.0.0 (https://www.w3.org/DOM/)
13. dotnet/csharp-tmLanguage version 0.1.0 (https://github.com/dotnet/csharp-tmLanguage)
14. expand-abbreviation version 0.5.8 (https://github.com/emmetio/expand-abbreviation)
15. fadeevab/make.tmbundle (https://github.com/fadeevab/make.tmbundle)
16. freebroccolo/atom-language-swift (https://github.com/freebroccolo/atom-language-swift)
17. HTML 5.1 W3C Working Draft version 08 October 2015 (http://www.w3.org/TR/2015/WD-html51-20151008/)
18. Ikuyadeu/vscode-R version 0.5.5 (https://github.com/Ikuyadeu/vscode-R)
19. Ionic documentation version 1.2.4 (https://github.com/ionic-team/ionic-site)
20. ionide/ionide-fsgrammar (https://github.com/ionide/ionide-fsgrammar)
21. jeff-hykin/cpp-textmate-grammar version 1.11.0 (https://github.com/jeff-hykin/cpp-textmate-grammar)
22. jeff-hykin/cpp-textmate-grammar version 1.11.7 (https://github.com/jeff-hykin/cpp-textmate-grammar)
23. js-beautify version 1.6.8 (https://github.com/beautify-web/js-beautify)
24. Jxck/assert version 1.0.0 (https://github.com/Jxck/assert)
25. language-docker (https://github.com/moby/moby)
26. language-go version 0.44.3 (https://github.com/atom/language-go)
27. language-less version 0.34.2 (https://github.com/atom/language-less)
28. language-php version 0.44.1 (https://github.com/atom/language-php)
29. language-rust version 0.4.12 (https://github.com/zargony/atom-language-rust)
30. MagicStack/MagicPython version 1.1.1 (https://github.com/MagicStack/MagicPython)
31. marked version 0.6.2 (https://github.com/markedjs/marked)
32. mdn-data version 1.1.12 (https://github.com/mdn/data)
33. Microsoft/TypeScript-TmLanguage version 0.0.1 (https://github.com/Microsoft/TypeScript-TmLanguage)
34. Microsoft/vscode-JSON.tmLanguage (https://github.com/Microsoft/vscode-JSON.tmLanguage)
35. Microsoft/vscode-mssql version 1.4.0 (https://github.com/Microsoft/vscode-mssql)
36. mmims/language-batchfile version 0.7.5 (https://github.com/mmims/language-batchfile)
37. octicons version 8.3.0 (https://github.com/primer/octicons)
38. octref/language-css version 0.42.11 (https://github.com/octref/language-css)
39. PowerShell/EditorSyntax (https://github.com/powershell/editorsyntax)
40. promise-polyfill version 8.0.0 (https://github.com/taylorhakes/promise-polyfill)
41. seti-ui version 0.1.0 (https://github.com/jesseweed/seti-ui)
42. shaders-tmLanguage version 0.1.0 (https://github.com/tgjones/shaders-tmLanguage)
43. textmate/asp.vb.net.tmbundle (https://github.com/textmate/asp.vb.net.tmbundle)
44. textmate/c.tmbundle (https://github.com/textmate/c.tmbundle)
45. textmate/diff.tmbundle (https://github.com/textmate/diff.tmbundle)
46. textmate/git.tmbundle (https://github.com/textmate/git.tmbundle)
47. textmate/groovy.tmbundle (https://github.com/textmate/groovy.tmbundle)
48. textmate/html.tmbundle (https://github.com/textmate/html.tmbundle)
49. textmate/ini.tmbundle (https://github.com/textmate/ini.tmbundle)
50. textmate/javascript.tmbundle (https://github.com/textmate/javascript.tmbundle)
51. textmate/lua.tmbundle (https://github.com/textmate/lua.tmbundle)
52. textmate/markdown.tmbundle (https://github.com/textmate/markdown.tmbundle)
53. textmate/perl.tmbundle (https://github.com/textmate/perl.tmbundle)
54. textmate/ruby.tmbundle (https://github.com/textmate/ruby.tmbundle)
55. textmate/yaml.tmbundle (https://github.com/textmate/yaml.tmbundle)
56. TypeScript-TmLanguage version 0.1.8 (https://github.com/Microsoft/TypeScript-TmLanguage)
57. TypeScript-TmLanguage version 1.0.0 (https://github.com/Microsoft/TypeScript-TmLanguage)
58. Unicode version 12.0.0 (http://www.unicode.org/)
59. vscode-logfile-highlighter version 2.4.1 (https://github.com/emilast/vscode-logfile-highlighter)
60. vscode-octicons-font version 1.3.1 (https://github.com/Microsoft/vscode-octicons-font)
61. vscode-swift version 0.0.1 (https://github.com/owensd/vscode-swift)
62. Web Background Synchronization (https://github.com/WICG/BackgroundSync)
%% atom/language-c NOTICES AND INFORMATION BEGIN HERE
=========================================
The MIT License (MIT)
Copyright (c) 2014 GitHub Inc.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
This package was derived from a TextMate bundle located at
https://github.com/textmate/c.tmbundle and distributed under the following
license, located in `README.mdown`:
Permission to copy, use, modify, sell and distribute this
software is granted. This software is provided "as is" without
express or implied warranty, and with no claim as to its
suitability for any purpose.
=========================================
END OF atom/language-c NOTICES AND INFORMATION
%% atom/language-clojure NOTICES AND INFORMATION BEGIN HERE
=========================================
Copyright (c) 2014 GitHub Inc.
@ -251,43 +213,6 @@ suitability for any purpose.
=========================================
END OF atom/language-java NOTICES AND INFORMATION
%% atom/language-objective-c NOTICES AND INFORMATION BEGIN HERE
=========================================
The MIT License (MIT)
Copyright (c) 2014 GitHub Inc.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
This package was derived from a TextMate bundle located at
https://github.com/textmate/objective-c.tmbundle and distributed under the following
license, located in `README.mdown`:
Permission to copy, use, modify, sell and distribute this
software is granted. This software is provided "as is" without
express or implied warranty, and with no claim as to its
suitability for any purpose.
=========================================
END OF atom/language-objective-c NOTICES AND INFORMATION
%% atom/language-sass NOTICES AND INFORMATION BEGIN HERE
=========================================
The MIT License (MIT)

View file

@ -151,13 +151,6 @@ async function publish(commit: string, quality: string, platform: string, type:
const queuedBy = process.env['BUILD_QUEUEDBY']!;
const sourceBranch = process.env['BUILD_SOURCEBRANCH']!;
const isReleased = (
// Insiders: nightly build from master
(quality === 'insider' && /^master$|^refs\/heads\/master$/.test(sourceBranch) && /Project Collection Service Accounts|Microsoft.VisualStudio.Services.TFS/.test(queuedBy)) ||
// Exploration: any build from electron-4.0.x branch
(quality === 'exploration' && /^electron-4.0.x$|^refs\/heads\/electron-4.0.x$/.test(sourceBranch))
);
console.log('Publishing...');
console.log('Quality:', quality);
@ -167,7 +160,6 @@ async function publish(commit: string, quality: string, platform: string, type:
console.log('Version:', version);
console.log('Commit:', commit);
console.log('Is Update:', isUpdate);
console.log('Is Released:', isReleased);
console.log('File:', file);
const stat = await new Promise<fs.Stats>((c, e) => fs.stat(file, (err, stat) => err ? e(err) : c(stat)));
@ -226,7 +218,7 @@ async function publish(commit: string, quality: string, platform: string, type:
id: commit,
timestamp: (new Date()).getTime(),
version,
isReleased: config.frozen ? false : isReleased,
isReleased: false,
sourceBranch,
queuedBy,
assets: [] as Array<Asset>,
@ -245,11 +237,6 @@ async function publish(commit: string, quality: string, platform: string, type:
}
function main(): void {
if (process.env['VSCODE_BUILD_SKIP_PUBLISH']) {
console.warn('Skipping publish due to VSCODE_BUILD_SKIP_PUBLISH');
return;
}
const commit = process.env['BUILD_SOURCEVERSION'];
if (!commit) {

View file

@ -0,0 +1,109 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { DocumentClient } from 'documentdb';
interface Config {
id: string;
frozen: boolean;
}
function createDefaultConfig(quality: string): Config {
return {
id: quality,
frozen: false
};
}
function getConfig(quality: string): Promise<Config> {
const client = new DocumentClient(process.env['AZURE_DOCUMENTDB_ENDPOINT']!, { masterKey: process.env['AZURE_DOCUMENTDB_MASTERKEY'] });
const collection = 'dbs/builds/colls/config';
const query = {
query: `SELECT TOP 1 * FROM c WHERE c.id = @quality`,
parameters: [
{ name: '@quality', value: quality }
]
};
return new Promise<Config>((c, e) => {
client.queryDocuments(collection, query).toArray((err, results) => {
if (err && err.code !== 409) { return e(err); }
c(!results || results.length === 0 ? createDefaultConfig(quality) : results[0] as any as Config);
});
});
}
function doRelease(commit: string, quality: string): Promise<void> {
const client = new DocumentClient(process.env['AZURE_DOCUMENTDB_ENDPOINT']!, { masterKey: process.env['AZURE_DOCUMENTDB_MASTERKEY'] });
const collection = 'dbs/builds/colls/' + quality;
const query = {
query: 'SELECT TOP 1 * FROM c WHERE c.id = @id',
parameters: [{ name: '@id', value: commit }]
};
let updateTries = 0;
function update(): Promise<void> {
updateTries++;
return new Promise<void>((c, e) => {
client.queryDocuments(collection, query).toArray((err, results) => {
if (err) { return e(err); }
if (results.length !== 1) { return e(new Error('No documents')); }
const release = results[0];
release.isReleased = true;
client.replaceDocument(release._self, release, err => {
if (err && err.code === 409 && updateTries < 5) { return c(update()); }
if (err) { return e(err); }
console.log('Build successfully updated.');
c();
});
});
});
}
return update();
}
async function release(commit: string, quality: string): Promise<void> {
const config = await getConfig(quality);
console.log('Quality config:', config);
if (config.frozen) {
console.log(`Skipping release because quality ${quality} is frozen.`);
return;
}
await doRelease(commit, quality);
}
function env(name: string): string {
const result = process.env[name];
if (!result) {
throw new Error(`Skipping release due to missing env: ${name}`);
}
return result;
}
async function main(): Promise<void> {
const commit = env('BUILD_SOURCEVERSION');
const quality = env('VSCODE_QUALITY');
await release(commit, quality);
}
main().catch(err => {
console.error(err);
process.exit(1);
});

View file

@ -146,6 +146,10 @@ async function ensureVersionAndSymbols(options: IOptions) {
// Check version does not exist
console.log(`HockeyApp: checking for existing version ${options.versions.code} (${options.platform})`);
const versions = await getVersions({ accessToken: options.access.hockeyAppToken, appId: options.access.hockeyAppId });
if (!Array.isArray(versions.app_versions)) {
throw new Error(`Unexpected response: ${JSON.stringify(versions)}`);
}
if (versions.app_versions.some(v => v.version === options.versions.code)) {
console.log(`HockeyApp: Returning without uploading symbols because version ${options.versions.code} (${options.platform}) was already found`);
return;
@ -184,6 +188,10 @@ const hockeyAppToken = process.argv[3];
const is64 = process.argv[4] === 'x64';
const hockeyAppId = process.argv[5];
if (process.argv.length !== 6) {
throw new Error(`HockeyApp: Unexpected number of arguments. Got ${process.argv}`);
}
let platform: Platform;
if (process.platform === 'darwin') {
platform = Platform.MAC_OS;
@ -211,7 +219,9 @@ if (repository && codeVersion && electronVersion && (product.quality === 'stable
}).then(() => {
console.log('HockeyApp: done');
}).catch(error => {
console.error(`HockeyApp: error (${error})`);
console.error(`HockeyApp: error ${error} (AppID: ${hockeyAppId})`);
return process.exit(1);
});
} else {
console.log(`HockeyApp: skipping due to unexpected context (repository: ${repository}, codeVersion: ${codeVersion}, electronVersion: ${electronVersion}, quality: ${product.quality})`);

View file

@ -153,11 +153,6 @@ async function sync(commit: string, quality: string): Promise<void> {
}
function main(): void {
if (process.env['VSCODE_BUILD_SKIP_PUBLISH']) {
error('Skipping publish due to VSCODE_BUILD_SKIP_PUBLISH');
return;
}
const commit = process.env['BUILD_SOURCEVERSION'];
if (!commit) {

View file

@ -11,9 +11,9 @@ steps:
inputs:
versionSpec: "1.10.1"
- script: |
yarn
yarn --frozen-lockfile
displayName: Install Dependencies
condition: ne(variables['CacheRestored'], 'true')
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1
inputs:
keyfile: '**/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock'

View file

@ -26,18 +26,53 @@ steps:
git config user.email "vscode@microsoft.com"
git config user.name "VSCode"
displayName: Prepare tooling
- script: |
set -e
git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git"
git fetch distro
git merge $(node -p "require('./package.json').distro")
displayName: Merge distro
yarn
- script: |
set -e
yarn --frozen-lockfile
displayName: Install dependencies
- script: |
set -e
yarn gulp mixin
displayName: Mix in quality
- script: |
set -e
yarn gulp hygiene
yarn monaco-compile-check
displayName: Run hygiene checks
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- script: |
set -e
node build/azure-pipelines/common/installDistroDependencies.js
node build/azure-pipelines/common/installDistroDependencies.js remote
node build/lib/builtInExtensions.js
displayName: Prepare build
displayName: Install distro dependencies and extensions
- script: |
set -e
cd $BUILD_STAGINGDIRECTORY
git clone https://github.com/microsoft/vscode-telemetry-extractor.git
cd vscode-telemetry-extractor
git checkout 3b04aba5bfdfcca1a5426cd2c51a90d18740d0bc
npm i
npm run setup-extension-repos
node ./out/cli-extract.js --sourceDir $BUILD_SOURCESDIRECTORY --excludedDirPattern extensions --outputDir . --applyEndpoints --includeIsMeasurement --patchWebsiteEvents
node ./out/cli-extract-extensions.js --sourceDir ./src/telemetry-sources --outputDir . --applyEndpoints --includeIsMeasurement
mkdir -p $BUILD_SOURCESDIRECTORY/.build/telemetry
mv declarations-resolved.json $BUILD_SOURCESDIRECTORY/.build/telemetry/telemetry-core.json
mv declarations-extensions-resolved.json $BUILD_SOURCESDIRECTORY/.build/telemetry/telemetry-extensions.json
displayName: Extract Telemetry
- script: |
set -e
@ -52,11 +87,13 @@ steps:
# APP_NAME="`ls $(agent.builddirectory)/VSCode-darwin | head -n 1`"
# yarn smoketest -- --build "$(agent.builddirectory)/VSCode-darwin/$APP_NAME"
displayName: Run unit tests
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- script: |
set -e
./scripts/test-integration.sh --build --tfs "Integration Tests"
displayName: Run integration tests
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- script: |
set -e

View file

@ -0,0 +1,3 @@
#!/usr/bin/env bash
set -e
echo 'noop'

View file

@ -19,9 +19,9 @@ steps:
inputs:
versionSpec: "1.10.1"
- script: |
yarn
yarn --frozen-lockfile
displayName: Install Dependencies
condition: ne(variables['CacheRestored'], 'true')
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1
inputs:
keyfile: '**/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock'

View file

@ -0,0 +1,3 @@
#!/usr/bin/env bash
set -e
echo 'noop'

View file

@ -0,0 +1,85 @@
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:alpine'
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"
displayName: Prepare tooling
- script: |
set -e
git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git"
git fetch distro
git merge $(node -p "require('./package.json').distro")
displayName: Merge distro
- script: |
set -e
CHILD_CONCURRENCY=1 yarn --frozen-lockfile
displayName: Install dependencies
- script: |
set -e
yarn gulp mixin
displayName: Mix in quality
- script: |
set -e
yarn gulp hygiene
yarn monaco-compile-check
displayName: Run hygiene checks
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- script: |
set -e
./build/azure-pipelines/linux/prebuild-alpine.sh
displayName: Prepare build
- script: |
set -e
./build/azure-pipelines/linux/build-alpine.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-alpine.sh
displayName: Publish
- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0
displayName: 'Component Detection'
continueOnError: true

View file

@ -35,16 +35,36 @@ steps:
git config user.email "vscode@microsoft.com"
git config user.name "VSCode"
displayName: Prepare tooling
- script: |
set -e
git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git"
git fetch distro
git merge $(node -p "require('./package.json').distro")
displayName: Merge distro
CHILD_CONCURRENCY=1 yarn
- script: |
set -e
CHILD_CONCURRENCY=1 yarn --frozen-lockfile
displayName: Install dependencies
- script: |
set -e
yarn gulp mixin
displayName: Mix in quality
- script: |
set -e
yarn gulp hygiene
yarn monaco-compile-check
displayName: Run hygiene checks
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- script: |
set -e
./build/azure-pipelines/linux/prebuild-arm.sh
displayName: Prepare build
displayName: Prebuild
- script: |
set -e

View file

@ -27,18 +27,53 @@ steps:
git config user.email "vscode@microsoft.com"
git config user.name "VSCode"
displayName: Prepare tooling
- script: |
set -e
git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git"
git fetch distro
git merge $(node -p "require('./package.json').distro")
displayName: Merge distro
CHILD_CONCURRENCY=1 yarn
- script: |
set -e
CHILD_CONCURRENCY=1 yarn --frozen-lockfile
displayName: Install dependencies
- script: |
set -e
yarn gulp mixin
displayName: Mix in quality
- script: |
set -e
yarn gulp hygiene
yarn monaco-compile-check
displayName: Run hygiene checks
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- script: |
set -e
node build/azure-pipelines/common/installDistroDependencies.js
node build/azure-pipelines/common/installDistroDependencies.js remote
node build/lib/builtInExtensions.js
displayName: Prepare build
displayName: Install distro dependencies and extensions
- script: |
set -e
cd $BUILD_STAGINGDIRECTORY
git clone https://github.com/microsoft/vscode-telemetry-extractor.git
cd vscode-telemetry-extractor
git checkout 3b04aba5bfdfcca1a5426cd2c51a90d18740d0bc
npm i
npm run setup-extension-repos
node ./out/cli-extract.js --sourceDir $BUILD_SOURCESDIRECTORY --excludedDirPattern extensions --outputDir . --applyEndpoints --includeIsMeasurement --patchWebsiteEvents
node ./out/cli-extract-extensions.js --sourceDir ./src/telemetry-sources --outputDir . --applyEndpoints --includeIsMeasurement
mkdir -p $BUILD_SOURCESDIRECTORY/.build/telemetry
mv declarations-resolved.json $BUILD_SOURCESDIRECTORY/.build/telemetry/telemetry-core.json
mv declarations-extensions-resolved.json $BUILD_SOURCESDIRECTORY/.build/telemetry/telemetry-extensions.json
displayName: Extract Telemetry
- script: |
set -e
@ -56,6 +91,7 @@ steps:
DISPLAY=:10 ./scripts/test.sh --build --tfs "Unit Tests"
# yarn smoketest -- --build "$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH)"
displayName: Run unit tests
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- script: |
set -e

View file

@ -0,0 +1,3 @@
#!/usr/bin/env bash
set -e
echo 'noop'

View file

@ -9,6 +9,7 @@ resources:
jobs:
- job: Windows
condition: eq(variables['VSCODE_BUILD_WIN32'], 'true')
timeoutInMinutes: 120
pool:
vmImage: VS2017-Win2016
variables:
@ -18,6 +19,7 @@ jobs:
- job: Windows32
condition: eq(variables['VSCODE_BUILD_WIN32_32BIT'], 'true')
timeoutInMinutes: 120
pool:
vmImage: VS2017-Win2016
variables:
@ -27,6 +29,7 @@ jobs:
- job: Linux
condition: eq(variables['VSCODE_BUILD_LINUX'], 'true')
timeoutInMinutes: 120
pool:
vmImage: 'Ubuntu-16.04'
variables:
@ -37,6 +40,7 @@ jobs:
- job: LinuxSnap
condition: eq(variables['VSCODE_BUILD_LINUX'], 'true')
timeoutInMinutes: 120
pool:
vmImage: 'Ubuntu-16.04'
variables:
@ -47,7 +51,8 @@ jobs:
- template: linux/snap-build-linux.yml
- job: LinuxArmhf
condition: eq(variables['VSCODE_BUILD_LINUX_ARMHF'], 'true')
condition: and(eq(variables['VSCODE_BUILD_LINUX_ARMHF'], 'true'), ne(variables['VSCODE_QUALITY'], 'stable'))
timeoutInMinutes: 120
pool:
vmImage: 'Ubuntu-16.04'
variables:
@ -55,13 +60,39 @@ jobs:
steps:
- template: linux/product-build-linux-arm.yml
- job: LinuxAlpine
condition: and(eq(variables['VSCODE_BUILD_LINUX_ALPINE'], 'true'), ne(variables['VSCODE_QUALITY'], 'stable'))
timeoutInMinutes: 120
pool:
vmImage: 'Ubuntu-16.04'
variables:
VSCODE_ARCH: alpine
steps:
- template: linux/product-build-linux-alpine.yml
- job: macOS
condition: eq(variables['VSCODE_BUILD_MACOS'], 'true')
timeoutInMinutes: 120
pool:
vmImage: macOS 10.13
steps:
- template: darwin/product-build-darwin.yml
- job: Release
condition: and(succeeded(), or(eq(variables['VSCODE_RELEASE'], 'true'), and(eq(variables['VSCODE_QUALITY'], 'insider'), eq(variables['Build.Reason'], 'Schedule')), and(eq(variables['VSCODE_QUALITY'], 'exploration'), eq(variables['Build.SourceBranch'], 'refs/heads/electron-4.0.x'))))
pool:
vmImage: 'Ubuntu-16.04'
dependsOn:
- Windows
- Windows32
- Linux
- LinuxSnap
- LinuxArmhf
- LinuxAlpine
- macOS
steps:
- template: release.yml
- job: Mooncake
pool:
vmImage: 'Ubuntu-16.04'
@ -72,6 +103,7 @@ jobs:
- Linux
- LinuxSnap
- LinuxArmhf
- LinuxAlpine
- macOS
steps:
- template: sync-mooncake.yml

View file

@ -0,0 +1,22 @@
steps:
- task: NodeTool@0
inputs:
versionSpec: "10.x"
- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2
inputs:
versionSpec: "1.x"
- task: AzureKeyVault@1
displayName: 'Azure Key Vault: Get Secrets'
inputs:
azureSubscription: 'vscode-builds-subscription'
KeyVaultName: vscode
- script: |
set -e
(cd build ; yarn)
AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \
node build/azure-pipelines/common/release.js

View file

@ -15,9 +15,9 @@ steps:
targetfolder: '**/node_modules, !**/node_modules/**/node_modules'
vstsFeed: '$(ArtifactFeed)'
- powershell: |
yarn
yarn --frozen-lockfile
displayName: Install Dependencies
condition: ne(variables['CacheRestored'], 'true')
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1
inputs:
keyfile: '**/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock'

View file

@ -22,23 +22,64 @@ steps:
. build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop"
"machine monacotools.visualstudio.com`npassword $(devops-pat)`nmachine github.com`nlogin vscode`npassword $(github-distro-mixin-password)" | Out-File "$env:USERPROFILE\_netrc" -Encoding ASCII
$env:npm_config_arch="$(VSCODE_ARCH)"
$env:CHILD_CONCURRENCY="1"
exec { git config user.email "vscode@microsoft.com" }
exec { git config user.name "VSCode" }
displayName: Prepare tooling
- powershell: |
. build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop"
exec { git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" }
exec { git fetch distro }
exec { git merge $(node -p "require('./package.json').distro") }
displayName: Merge distro
exec { yarn }
- powershell: |
. build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop"
$env:npm_config_arch="$(VSCODE_ARCH)"
$env:CHILD_CONCURRENCY="1"
exec { yarn --frozen-lockfile }
displayName: Install dependencies
- powershell: |
. build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop"
exec { yarn gulp mixin }
displayName: Mix in quality
- powershell: |
. build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop"
exec { yarn gulp hygiene }
exec { yarn monaco-compile-check }
displayName: Run hygiene checks
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- powershell: |
. build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop"
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
displayName: Install distro dependencies and extensions
- powershell: |
. build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop"
cd $env:BUILD_STAGINGDIRECTORY
git clone https://github.com/microsoft/vscode-telemetry-extractor.git
cd vscode-telemetry-extractor
git checkout 3b04aba5bfdfcca1a5426cd2c51a90d18740d0bc
npm i
npm run setup-extension-repos
node .\out\cli-extract.js --sourceDir $env:BUILD_SOURCESDIRECTORY --excludedDirPattern extensions --outputDir . --applyEndpoints --includeIsMeasurement --patchWebsiteEvents
node .\out\cli-extract-extensions.js --sourceDir .\src\telemetry-sources --outputDir . --applyEndpoints --includeIsMeasurement
mkdir $env:BUILD_SOURCESDIRECTORY\.build\telemetry -ea 0
mv declarations-resolved.json $env:BUILD_SOURCESDIRECTORY\.build\telemetry\telemetry-core.json
mv declarations-extensions-resolved.json $env:BUILD_SOURCESDIRECTORY\.build\telemetry\telemetry-extensions.json
displayName: Extract Telemetry
- powershell: |
. build/azure-pipelines/win32/exec.ps1
@ -52,8 +93,8 @@ steps:
$ErrorActionPreference = "Stop"
exec { yarn gulp "electron-$(VSCODE_ARCH)" }
exec { .\scripts\test.bat --build --tfs "Unit Tests" }
# yarn smoketest -- --build "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)"
displayName: Run unit tests
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- powershell: |
. build/azure-pipelines/win32/exec.ps1
@ -61,6 +102,7 @@ steps:
exec { yarn gulp "electron-$(VSCODE_ARCH)" }
exec { .\scripts\test-integration.bat --build --tfs "Integration Tests" }
displayName: Run integration tests
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1
inputs:
@ -142,6 +184,7 @@ steps:
$env:AZURE_STORAGE_ACCESS_KEY_2 = "$(vscode-storage-key)"
$env:AZURE_DOCUMENTDB_MASTERKEY = "$(builds-docdb-key-readwrite)"
$env:VSCODE_HOCKEYAPP_TOKEN = "$(vscode-hockeyapp-token)"
$env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)"
.\build\azure-pipelines\win32\publish.ps1
displayName: Publish

View file

@ -1,7 +1,7 @@
[
{
"name": "ms-vscode.node-debug",
"version": "1.35.2",
"version": "1.35.3",
"repo": "https://github.com/Microsoft/vscode-node-debug",
"metadata": {
"id": "b6ded8fb-a0a0-4c1c-acbd-ab2a3bc995a6",

View file

@ -175,6 +175,17 @@ gulp.task('tslint', () => {
function hygiene(some) {
let errorCount = 0;
const productJson = es.through(function (file) {
const product = JSON.parse(file.contents.toString('utf8'));
if (product.extensionsGallery) {
console.error('product.json: Contains "extensionsGallery"');
errorCount++;
}
this.emit('data', file);
});
const indentation = es.through(function (file) {
const lines = file.contents.toString('utf8').split(/\r\n|\r|\n/);
file.__lines = lines;
@ -258,8 +269,13 @@ function hygiene(some) {
input = some;
}
const productJsonFilter = filter('product.json', { restore: true });
const result = input
.pipe(filter(f => !f.stat.isDirectory()))
.pipe(productJsonFilter)
.pipe(process.env['BUILD_SOURCEVERSION'] ? es.through() : productJson)
.pipe(productJsonFilter.restore)
.pipe(filter(indentationFilter))
.pipe(indentation)
.pipe(filter(copyrightFilter))

View file

@ -19,6 +19,8 @@ const untar = require('gulp-untar');
const File = require('vinyl');
const fs = require('fs');
const cp = require('child_process');
const REPO_ROOT = path.dirname(__dirname);
const noop = () => { return Promise.resolve(); };
@ -28,6 +30,7 @@ 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-armhf-min', noop);
gulp.task('vscode-reh-linux-alpine-min', noop);
function getNodeVersion() {
@ -84,6 +87,18 @@ function nodejs(platform, arch) {
);
}
if (arch === 'alpine') {
return es.readArray([
new File({
path: 'node',
contents: cp.execSync(`docker run --rm node:${VERSION}-alpine /bin/sh -c 'cat \`which node\`'`, { maxBuffer: 100 * 1024 * 1024, encoding: 'buffer' }),
stat: {
mode: parseInt('755', 8)
}
})
]);
}
if (platform === 'darwin') {
arch = 'x64';
}
@ -115,3 +130,33 @@ function nodejs(platform, arch) {
}))
);
}
function mixinServer(watch) {
const packageJSONPath = path.join(path.dirname(__dirname), 'package.json');
function exec(cmdLine) {
console.log(cmdLine);
cp.execSync(cmdLine, { stdio: "inherit" });
}
function checkout() {
const packageJSON = JSON.parse(fs.readFileSync(packageJSONPath).toString());
exec('git fetch distro');
exec(`git checkout ${packageJSON['distro']} -- src/vs/server resources/server`);
exec('git reset HEAD src/vs/server resources/server');
}
checkout();
if (watch) {
console.log('Enter watch mode (observing package.json)');
const watcher = fs.watch(packageJSONPath);
watcher.addListener('change', () => {
try {
checkout();
} catch (e) {
console.log(e);
}
});
}
return Promise.resolve();
}
gulp.task(task.define('mixin-server', () => mixinServer(false)));
gulp.task(task.define('mixin-server-watch', () => mixinServer(true)));

View file

@ -315,6 +315,8 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op
// TODO the API should be copied to `out` during compile, not here
const api = gulp.src('src/vs/vscode.d.ts').pipe(rename('out/vs/vscode.d.ts'));
const telemetry = gulp.src('.build/telemetry/**', { base: '.build/telemetry', dot: true });
const depsSrc = [
..._.flatten(productionDependencies.map(d => path.relative(root, d.path)).map(d => [`${d}/**`, `!${d}/**/{test,tests}/**`])),
// @ts-ignore JSON checking: dependencies is optional
@ -327,10 +329,11 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op
.pipe(createAsar(path.join(process.cwd(), 'node_modules'), ['**/*.node', '**/vscode-ripgrep/bin/*', '**/node-pty/build/Release/*'], 'app/node_modules.asar'));
let all = es.merge(
packageJsonStream,
packageJsonStream,
productJsonStream,
license,
api,
telemetry,
sources,
deps
);
@ -380,7 +383,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op
.pipe(util.skipDirectories())
.pipe(util.fixWin32DirectoryPermissions())
.pipe(electron(_.extend({}, config, { platform, arch, ffmpegChromium: true })))
.pipe(filter(['**', '!LICENSE', '!LICENSES.chromium.html', '!version']));
.pipe(filter(['**', '!LICENSE', '!LICENSES.chromium.html', '!version'], { dot: true }));
// result = es.merge(result, gulp.src('resources/completions/**', { base: '.' }));

View file

@ -25,7 +25,7 @@ const zipDir = arch => path.join(repoPath, '.build', `win32-${arch}`, 'archive')
const zipPath = arch => path.join(zipDir(arch), `VSCode-win32-${arch}.zip`);
const setupDir = (arch, target) => path.join(repoPath, '.build', `win32-${arch}`, `${target}-setup`);
const issPath = path.join(__dirname, 'win32', 'code.iss');
const innoSetupPath = path.join(path.dirname(path.dirname(require.resolve('innosetup-compiler'))), 'bin', 'ISCC.exe');
const innoSetupPath = path.join(path.dirname(path.dirname(require.resolve('innosetup'))), 'bin', 'ISCC.exe');
const signPS1 = path.join(repoPath, 'build', 'azure-pipelines', 'win32', 'sign.ps1');
function packageInnoSetup(iss, options, cb) {

View file

@ -20,13 +20,10 @@ function yarnInstall(location, opts) {
const raw = process.env['npm_config_argv'] || '{}';
const argv = JSON.parse(raw);
const original = argv.original || [];
const args = ['install'];
const args = original.filter(arg => arg === '--ignore-optional' || arg === '--frozen-lockfile');
if (original.indexOf('--ignore-optional') > -1) {
args.push('--ignore-optional');
}
console.log('Installing dependencies in \'%s\'.', location);
console.log(`Installing dependencies in ${location}...`);
console.log(`$ yarn ${args.join(' ')}`);
const result = cp.spawnSync(yarn, args, opts);
if (result.error || result.status !== 0) {

View file

@ -1034,7 +1034,7 @@ begin
AltArch := '32';
end;
if not Result then begin
if not Result and not WizardSilent() then begin
MsgBox('Please uninstall the ' + AltArch + '-bit version of {#NameShort} before installing this ' + ThisArch + '-bit version.', mbInformation, MB_OK);
end;
end;

File diff suppressed because it is too large Load diff

View file

@ -60,12 +60,12 @@
"git": {
"name": "electron",
"repositoryUrl": "https://github.com/electron/electron",
"commitHash": "c1b5a1cfc8a14a337540193daecfa5d0f50dd7bb"
"commitHash": "5d67ec3da5376a5058990e8a9557bc9124ad59a8"
}
},
"isOnlyProductionDependency": true,
"license": "MIT",
"version": "4.2.4"
"version": "4.2.5"
},
{
"component": {

View file

@ -339,7 +339,7 @@ export class Git {
}
async clone(url: string, parentPath: string, cancellationToken?: CancellationToken): Promise<string> {
let baseFolderName = decodeURI(url).replace(/^.*\//, '').replace(/\.git$/, '') || 'repository';
let baseFolderName = decodeURI(url).replace(/[\/]+$/, '').replace(/^.*\//, '').replace(/\.git$/, '') || 'repository';
let folderName = baseFolderName;
let folderPath = path.join(parentPath, folderName);
let count = 1;

View file

@ -233,7 +233,7 @@ export class Model {
}
const dotGit = await this.git.getRepositoryDotGit(repositoryRoot);
const repository = new Repository(this.git.open(repositoryRoot, dotGit), this.globalState);
const repository = new Repository(this.git.open(repositoryRoot, dotGit), this.globalState, this.outputChannel);
this.open(repository);
} catch (err) {

View file

@ -3,9 +3,9 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { commands, Uri, Command, EventEmitter, Event, scm, SourceControl, SourceControlInputBox, SourceControlResourceGroup, SourceControlResourceState, SourceControlResourceDecorations, SourceControlInputBoxValidation, Disposable, ProgressLocation, window, workspace, WorkspaceEdit, ThemeColor, DecorationData, Memento, SourceControlInputBoxValidationType } from 'vscode';
import { commands, Uri, Command, EventEmitter, Event, scm, SourceControl, SourceControlInputBox, SourceControlResourceGroup, SourceControlResourceState, SourceControlResourceDecorations, SourceControlInputBoxValidation, Disposable, ProgressLocation, window, workspace, WorkspaceEdit, ThemeColor, DecorationData, Memento, SourceControlInputBoxValidationType, OutputChannel, LogLevel, env } from 'vscode';
import { Repository as BaseRepository, Commit, Stash, GitError, Submodule, CommitOptions, ForcePushMode } from './git';
import { anyEvent, filterEvent, eventToPromise, dispose, find, isDescendant, IDisposable, onceEvent, EmptyDisposable, debounceEvent, toDisposable } from './util';
import { anyEvent, filterEvent, eventToPromise, dispose, find, isDescendant, IDisposable, onceEvent, EmptyDisposable, debounceEvent, combinedDisposable, watch, IFileWatcher } from './util';
import { memoize, throttle, debounce } from './decorators';
import { toGitUri } from './uri';
import { AutoFetcher } from './autofetch';
@ -447,6 +447,91 @@ class ProgressManager {
}
}
class FileEventLogger {
private eventDisposable: IDisposable = EmptyDisposable;
private logLevelDisposable: IDisposable = EmptyDisposable;
constructor(
private onWorkspaceWorkingTreeFileChange: Event<Uri>,
private onDotGitFileChange: Event<Uri>,
private outputChannel: OutputChannel
) {
this.logLevelDisposable = env.onDidChangeLogLevel(this.onDidChangeLogLevel, this);
this.onDidChangeLogLevel(env.logLevel);
}
private onDidChangeLogLevel(level: LogLevel): void {
this.eventDisposable.dispose();
if (level > LogLevel.Debug) {
return;
}
this.eventDisposable = combinedDisposable([
this.onWorkspaceWorkingTreeFileChange(uri => this.outputChannel.appendLine(`[debug] [wt] Change: ${uri.fsPath}`)),
this.onDotGitFileChange(uri => this.outputChannel.appendLine(`[debug] [.git] Change: ${uri.fsPath}`))
]);
}
dispose(): void {
this.eventDisposable.dispose();
this.logLevelDisposable.dispose();
}
}
class DotGitWatcher implements IFileWatcher {
readonly event: Event<Uri>;
private emitter = new EventEmitter<Uri>();
private transientDisposables: IDisposable[] = [];
private disposables: IDisposable[] = [];
constructor(
private repository: Repository,
private outputChannel: OutputChannel
) {
const rootWatcher = watch(repository.dotGit);
this.disposables.push(rootWatcher);
const filteredRootWatcher = filterEvent(rootWatcher.event, uri => !/\/\.git(\/index\.lock)?$/.test(uri.path));
this.event = anyEvent(filteredRootWatcher, this.emitter.event);
repository.onDidRunGitStatus(this.updateTransientWatchers, this, this.disposables);
this.updateTransientWatchers();
}
private updateTransientWatchers() {
this.transientDisposables = dispose(this.transientDisposables);
if (!this.repository.HEAD || !this.repository.HEAD.upstream) {
return;
}
this.transientDisposables = dispose(this.transientDisposables);
const { name, remote } = this.repository.HEAD.upstream;
const upstreamPath = path.join(this.repository.dotGit, 'refs', 'remotes', remote, name);
try {
const upstreamWatcher = watch(upstreamPath);
this.transientDisposables.push(upstreamWatcher);
upstreamWatcher.event(this.emitter.fire, this.emitter, this.transientDisposables);
} catch (err) {
if (env.logLevel <= LogLevel.Info) {
this.outputChannel.appendLine(`Failed to watch ref '${upstreamPath}'. Ref is most likely packed.`);
}
}
}
dispose() {
this.emitter.dispose();
this.transientDisposables = dispose(this.transientDisposables);
this.disposables = dispose(this.disposables);
}
}
export class Repository implements Disposable {
private _onDidChangeRepository = new EventEmitter<Uri>();
@ -544,37 +629,41 @@ export class Repository implements Disposable {
return this.repository.root;
}
get dotGit(): string {
return this.repository.dotGit;
}
private isRepositoryHuge = false;
private didWarnAboutLimit = false;
private isFreshRepository: boolean | undefined = undefined;
private disposables: Disposable[] = [];
constructor(
private readonly repository: BaseRepository,
globalState: Memento
globalState: Memento,
outputChannel: OutputChannel
) {
const workspaceWatcher = workspace.createFileSystemWatcher('**');
this.disposables.push(workspaceWatcher);
const onWorkspaceFileChanges = anyEvent(workspaceWatcher.onDidChange, workspaceWatcher.onDidCreate, workspaceWatcher.onDidDelete);
const onWorkspaceRepositoryFileChanges = filterEvent(onWorkspaceFileChanges, uri => isDescendant(repository.root, uri.fsPath));
const onWorkspaceWorkingTreeFileChanges = filterEvent(onWorkspaceRepositoryFileChanges, uri => !/\/\.git($|\/)/.test(uri.path));
const onWorkspaceFileChange = anyEvent(workspaceWatcher.onDidChange, workspaceWatcher.onDidCreate, workspaceWatcher.onDidDelete);
const onWorkspaceRepositoryFileChange = filterEvent(onWorkspaceFileChange, uri => isDescendant(repository.root, uri.fsPath));
const onWorkspaceWorkingTreeFileChange = filterEvent(onWorkspaceRepositoryFileChange, uri => !/\/\.git($|\/)/.test(uri.path));
const dotGitWatcher = fs.watch(repository.dotGit);
const onRepositoryFileEmitter = new EventEmitter<Uri>();
dotGitWatcher.on('change', (_, e) => onRepositoryFileEmitter.fire(Uri.file(path.join(repository.dotGit, e as string))));
dotGitWatcher.on('error', err => console.error(err));
this.disposables.push(toDisposable(() => dotGitWatcher.close()));
const onRelevantRepositoryChanges = filterEvent(onRepositoryFileEmitter.event, uri => !/\/\.git(\/index\.lock)?$/.test(uri.path));
const dotGitFileWatcher = new DotGitWatcher(this, outputChannel);
this.disposables.push(dotGitFileWatcher);
// FS changes should trigger `git status`:
// - any change inside the repository working tree
// - any change whithin the first level of the `.git` folder, except the folder itself and `index.lock`
const onFSChange = anyEvent(onWorkspaceWorkingTreeFileChanges, onRelevantRepositoryChanges);
onFSChange(this.onFSChange, this, this.disposables);
const onFileChange = anyEvent(onWorkspaceWorkingTreeFileChange, dotGitFileWatcher.event);
onFileChange(this.onFileChange, this, this.disposables);
// Relevate repository changes should trigger virtual document change events
onRelevantRepositoryChanges(this._onDidChangeRepository.fire, this._onDidChangeRepository, this.disposables);
dotGitFileWatcher.event(this._onDidChangeRepository.fire, this._onDidChangeRepository, this.disposables);
this.disposables.push(new FileEventLogger(onWorkspaceWorkingTreeFileChange, dotGitFileWatcher.event, outputChannel));
const root = Uri.file(repository.root);
this._sourceControl = scm.createSourceControl('git', 'Git', root);
@ -584,9 +673,9 @@ export class Repository implements Disposable {
this._sourceControl.inputBox.validateInput = this.validateInput.bind(this);
this.disposables.push(this._sourceControl);
this._mergeGroup = this._sourceControl.createResourceGroup('merge', localize('merge changes', "Merge Changes"));
this._indexGroup = this._sourceControl.createResourceGroup('index', localize('staged changes', "Staged Changes"));
this._workingTreeGroup = this._sourceControl.createResourceGroup('workingTree', localize('changes', "Changes"));
this._mergeGroup = this._sourceControl.createResourceGroup('merge', localize('merge changes', "MERGE CHANGES"));
this._indexGroup = this._sourceControl.createResourceGroup('index', localize('staged changes', "STAGED CHANGES"));
this._workingTreeGroup = this._sourceControl.createResourceGroup('workingTree', localize('changes', "CHANGES"));
const updateIndexGroupVisibility = () => {
const config = workspace.getConfiguration('git', root);
@ -1454,7 +1543,7 @@ export class Repository implements Disposable {
return result;
}
private onFSChange(_uri: Uri): void {
private onFileChange(_uri: Uri): void {
const config = workspace.getConfiguration('git');
const autorefresh = config.get<boolean>('autorefresh');

View file

@ -3,8 +3,8 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Event } from 'vscode';
import { dirname, sep } from 'path';
import { Event, EventEmitter, Uri } from 'vscode';
import { dirname, sep, join } from 'path';
import { Readable } from 'stream';
import * as fs from 'fs';
import * as byline from 'byline';
@ -343,4 +343,20 @@ export function pathEquals(a: string, b: string): boolean {
}
return a === b;
}
}
export interface IFileWatcher extends IDisposable {
readonly event: Event<Uri>;
}
export function watch(location: string): IFileWatcher {
const dotGitWatcher = fs.watch(location);
const onDotGitFileChangeEmitter = new EventEmitter<Uri>();
dotGitWatcher.on('change', (_, e) => onDotGitFileChangeEmitter.fire(Uri.file(join(location, e as string))));
dotGitWatcher.on('error', err => console.error(err));
return new class implements IFileWatcher {
event = onDotGitFileChangeEmitter.event;
dispose() { dotGitWatcher.close(); }
};
}

View file

@ -24,7 +24,6 @@
".bowerrc",
".jshintrc",
".jscsrc",
".eslintrc",
".swcrc",
".webmanifest",
".js.map",
@ -49,7 +48,9 @@
"extensions": [
".hintrc",
".babelrc",
".jsonc"
".jsonc",
".eslintrc",
".eslintrc.json"
],
"configuration": "./language-configuration.json"
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -4,7 +4,8 @@
"description": "%description%",
"version": "1.0.0",
"icon": "icon.png",
"publisher": "vscode",
"publisher": "vscode",
"enableProposedApi": true,
"license": "MIT",
"aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217",
"engines": {

View file

@ -19,7 +19,7 @@ const settings = getSettings();
const vscode = acquireVsCodeApi();
// Set VS Code state
let state = getData('data-state');
let state = getData<{ line: number }>('data-state');
vscode.setState(state);
const messaging = createPosterForVsCode(vscode);
@ -131,8 +131,8 @@ document.addEventListener('click', event => {
if (node.getAttribute('href').startsWith('#')) {
break;
}
if (node.href.startsWith('file://') || node.href.startsWith('vscode-resource:')) {
const [path, fragment] = node.href.replace(/^(file:\/\/|vscode-resource:)/i, '').split('#');
if (node.href.startsWith('file://') || node.href.startsWith('vscode-resource:') || node.href.startsWith(settings.webviewResourceRoot)) {
const [path, fragment] = node.href.replace(/^(file:\/\/|vscode-resource:)/i, '').replace(new RegExp(`^${escapeRegExp(settings.webviewResourceRoot)}`)).split('#');
messaging.postMessage('clickLink', { path, fragment });
event.preventDefault();
event.stopPropagation();
@ -157,4 +157,8 @@ if (settings.scrollEditorWithPreview) {
}
}
}, 50));
}
function escapeRegExp(text: string) {
return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
}

View file

@ -4,18 +4,19 @@
*--------------------------------------------------------------------------------------------*/
export interface PreviewSettings {
source: string;
line: number;
lineCount: number;
scrollPreviewWithEditor?: boolean;
scrollEditorWithPreview: boolean;
disableSecurityWarnings: boolean;
doubleClickToSwitchToEditor: boolean;
readonly source: string;
readonly line: number;
readonly lineCount: number;
readonly scrollPreviewWithEditor?: boolean;
readonly scrollEditorWithPreview: boolean;
readonly disableSecurityWarnings: boolean;
readonly doubleClickToSwitchToEditor: boolean;
readonly webviewResourceRoot: string;
}
let cachedSettings: PreviewSettings | undefined = undefined;
export function getData(key: string): PreviewSettings {
export function getData<T = {}>(key: string): T {
const element = document.getElementById('vscode-markdown-preview-data');
if (element) {
const data = element.getAttribute(key);

View file

@ -39,7 +39,7 @@ function parseLink(
return {
uri: OpenDocumentLinkCommand.createCommandUri(resourcePath, tempUri.fragment),
tooltip: localize('documentLink.tooltip', 'follow link')
tooltip: localize('documentLink.tooltip', 'Follow link')
};
}

View file

@ -412,7 +412,7 @@ export class MarkdownPreview extends Disposable {
this.currentVersion = pendingVersion;
if (this._resource === resource) {
const content = await this._contentProvider.provideTextDocumentContent(document, this._previewConfigurations, this.line, this.state);
const content = await this._contentProvider.provideTextDocumentContent(document, await this.editor.webview.resourceRoot, this._previewConfigurations, this.line, this.state);
// Another call to `doUpdate` may have happened.
// Make sure we are still updating for the correct document
if (this.currentVersion && this.currentVersion.equals(pendingVersion)) {

View file

@ -14,6 +14,7 @@ import { Logger } from '../logger';
import { ContentSecurityPolicyArbiter, MarkdownPreviewSecurityLevel } from '../security';
import { MarkdownPreviewConfigurationManager, MarkdownPreviewConfiguration } from './previewConfig';
import { MarkdownContributionProvider } from '../markdownExtensions';
import { toResoruceUri } from '../util/resources';
/**
* Strings used inside the markdown preview.
@ -50,6 +51,7 @@ export class MarkdownContentProvider {
public async provideTextDocumentContent(
markdownDocument: vscode.TextDocument,
webviewResourceRoot: string,
previewConfigurations: MarkdownPreviewConfigurationManager,
initialLine: number | undefined = undefined,
state?: any
@ -63,14 +65,15 @@ export class MarkdownContentProvider {
scrollPreviewWithEditor: config.scrollPreviewWithEditor,
scrollEditorWithPreview: config.scrollEditorWithPreview,
doubleClickToSwitchToEditor: config.doubleClickToSwitchToEditor,
disableSecurityWarnings: this.cspArbiter.shouldDisableSecurityWarnings()
disableSecurityWarnings: this.cspArbiter.shouldDisableSecurityWarnings(),
webviewResourceRoot: webviewResourceRoot,
};
this.logger.log('provideTextDocumentContent', initialData);
// Content Security Policy
const nonce = new Date().getTime() + '' + new Date().getMilliseconds();
const csp = this.getCspForResource(sourceUri, nonce);
const csp = this.getCspForResource(webviewResourceRoot, sourceUri, nonce);
const body = await this.engine.render(markdownDocument);
return `<!DOCTYPE html>
@ -82,14 +85,14 @@ export class MarkdownContentProvider {
data-settings="${escapeAttribute(JSON.stringify(initialData))}"
data-strings="${escapeAttribute(JSON.stringify(previewStrings))}"
data-state="${escapeAttribute(JSON.stringify(state || {}))}">
<script src="${this.extensionResourcePath('pre.js')}" nonce="${nonce}"></script>
${this.getStyles(sourceUri, nonce, config, state)}
<base href="${markdownDocument.uri.with({ scheme: 'vscode-resource' }).toString(true)}">
<script src="${this.extensionResourcePath(webviewResourceRoot, 'pre.js')}" nonce="${nonce}"></script>
${this.getStyles(webviewResourceRoot, sourceUri, nonce, config, state)}
<base href="${toResoruceUri(webviewResourceRoot, markdownDocument.uri)}">
</head>
<body class="vscode-body ${config.scrollBeyondLastLine ? 'scrollBeyondLastLine' : ''} ${config.wordWrap ? 'wordWrap' : ''} ${config.markEditorSelection ? 'showEditorSelection' : ''}">
${body}
<div class="code-line" data-line="${markdownDocument.lineCount}"></div>
${this.getScripts(nonce)}
${this.getScripts(webviewResourceRoot, nonce)}
</body>
</html>`;
}
@ -107,13 +110,12 @@ export class MarkdownContentProvider {
</html>`;
}
private extensionResourcePath(mediaFile: string): string {
return vscode.Uri.file(this.context.asAbsolutePath(path.join('media', mediaFile)))
.with({ scheme: 'vscode-resource' })
private extensionResourcePath(webviewResourceRoot: string, mediaFile: string): string {
return toResoruceUri(webviewResourceRoot, vscode.Uri.file(this.context.asAbsolutePath(path.join('media', mediaFile))))
.toString();
}
private fixHref(resource: vscode.Uri, href: string): string {
private fixHref(webviewResourceRoot: string, resource: vscode.Uri, href: string): string {
if (!href) {
return href;
}
@ -124,29 +126,23 @@ export class MarkdownContentProvider {
// Assume it must be a local file
if (path.isAbsolute(href)) {
return vscode.Uri.file(href)
.with({ scheme: 'vscode-resource' })
.toString();
return toResoruceUri(webviewResourceRoot, vscode.Uri.file(href)).toString();
}
// Use a workspace relative path if there is a workspace
const root = vscode.workspace.getWorkspaceFolder(resource);
if (root) {
return vscode.Uri.file(path.join(root.uri.fsPath, href))
.with({ scheme: 'vscode-resource' })
.toString();
return toResoruceUri(webviewResourceRoot, vscode.Uri.file(path.join(root.uri.fsPath, href))).toString();
}
// Otherwise look relative to the markdown file
return vscode.Uri.file(path.join(path.dirname(resource.fsPath), href))
.with({ scheme: 'vscode-resource' })
.toString();
return toResoruceUri(webviewResourceRoot, vscode.Uri.file(path.join(path.dirname(resource.fsPath), href))).toString();
}
private computeCustomStyleSheetIncludes(resource: vscode.Uri, config: MarkdownPreviewConfiguration): string {
private computeCustomStyleSheetIncludes(webviewResourceRoot: string, resource: vscode.Uri, config: MarkdownPreviewConfiguration): string {
if (Array.isArray(config.styles)) {
return config.styles.map(style => {
return `<link rel="stylesheet" class="code-user-style" data-source="${escapeAttribute(style)}" href="${escapeAttribute(this.fixHref(resource, style))}" type="text/css" media="screen">`;
return `<link rel="stylesheet" class="code-user-style" data-source="${escapeAttribute(style)}" href="${escapeAttribute(this.fixHref(webviewResourceRoot, resource, style))}" type="text/css" media="screen">`;
}).join('\n');
}
return '';
@ -177,37 +173,41 @@ export class MarkdownContentProvider {
return ret;
}
private getStyles(resource: vscode.Uri, nonce: string, config: MarkdownPreviewConfiguration, state?: any): string {
private getStyles(webviewResourceRoot: string, resource: vscode.Uri, nonce: string, config: MarkdownPreviewConfiguration, state?: any): string {
const baseStyles = this.contributionProvider.contributions.previewStyles
.map(resource => `<link rel="stylesheet" type="text/css" href="${escapeAttribute(resource.toString())}">`)
.map(resource => `<link rel="stylesheet" type="text/css" href="${escapeAttribute(toResoruceUri(webviewResourceRoot, resource).toString())}">`)
.join('\n');
return `${baseStyles}
${this.getSettingsOverrideStyles(nonce, config)}
${this.computeCustomStyleSheetIncludes(resource, config)}
${this.computeCustomStyleSheetIncludes(webviewResourceRoot, resource, config)}
${this.getImageStabilizerStyles(state)}`;
}
private getScripts(nonce: string): string {
private getScripts(resourceRoot: string, nonce: string): string {
return this.contributionProvider.contributions.previewScripts
.map(resource => `<script async src="${escapeAttribute(resource.toString())}" nonce="${nonce}" charset="UTF-8"></script>`)
.map(resource => `<script async src="${escapeAttribute(toResoruceUri(resourceRoot, resource).toString())}" nonce="${nonce}" charset="UTF-8"></script>`)
.join('\n');
}
private getCspForResource(resource: vscode.Uri, nonce: string): string {
private getCspForResource(
webviewResourceRoot: string,
resource: vscode.Uri,
nonce: string
): string {
switch (this.cspArbiter.getSecurityLevelForResource(resource)) {
case MarkdownPreviewSecurityLevel.AllowInsecureContent:
return `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src vscode-resource: http: https: data:; media-src vscode-resource: http: https: data:; script-src 'nonce-${nonce}'; style-src 'self' vscode-resource: 'unsafe-inline' http: https: data:; font-src vscode-resource: http: https: data:;">`;
return `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' ${webviewResourceRoot} http: https: data:; media-src 'self' ${webviewResourceRoot} http: https: data:; script-src 'nonce-${nonce}'; style-src 'self' ${webviewResourceRoot} 'unsafe-inline' http: https: data:; font-src 'self' ${webviewResourceRoot} http: https: data:;">`;
case MarkdownPreviewSecurityLevel.AllowInsecureLocalContent:
return `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src vscode-resource: https: data: http://localhost:* http://127.0.0.1:*; media-src vscode-resource: https: data: http://localhost:* http://127.0.0.1:*; script-src 'nonce-${nonce}'; style-src 'self' vscode-resource: 'unsafe-inline' https: data: http://localhost:* http://127.0.0.1:*; font-src vscode-resource: https: data: http://localhost:* http://127.0.0.1:*;">`;
return `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' ${webviewResourceRoot} https: data: http://localhost:* http://127.0.0.1:*; media-src 'self' ${webviewResourceRoot} https: data: http://localhost:* http://127.0.0.1:*; script-src 'nonce-${nonce}'; style-src 'self' ${webviewResourceRoot} 'unsafe-inline' https: data: http://localhost:* http://127.0.0.1:*; font-src 'self' ${webviewResourceRoot} https: data: http://localhost:* http://127.0.0.1:*;">`;
case MarkdownPreviewSecurityLevel.AllowScriptsAndAllContent:
return '';
case MarkdownPreviewSecurityLevel.Strict:
default:
return `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src vscode-resource: https: data:; media-src vscode-resource: https: data:; script-src 'nonce-${nonce}'; style-src 'self' vscode-resource: 'unsafe-inline' https: data:; font-src vscode-resource: https: data:;">`;
return `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' ${webviewResourceRoot} https: data:; media-src 'self' ${webviewResourceRoot} https: data:; script-src 'nonce-${nonce}'; style-src 'self' ${webviewResourceRoot} 'unsafe-inline' https: data:; font-src 'self' ${webviewResourceRoot} https: data:;">`;
}
}
}

View file

@ -9,8 +9,7 @@ import { Disposable } from './util/dispose';
import * as arrays from './util/arrays';
const resolveExtensionResource = (extension: vscode.Extension<any>, resourcePath: string): vscode.Uri => {
return vscode.Uri.file(path.join(extension.extensionPath, resourcePath))
.with({ scheme: 'vscode-resource' });
return vscode.Uri.file(path.join(extension.extensionPath, resourcePath));
};
const resolveExtensionResources = (extension: vscode.Extension<any>, resourcePaths: unknown): vscode.Uri[] => {

View file

@ -3,13 +3,14 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
const cp = require('child_process');
import * as vscode from 'vscode';
function exec(cmdLine) {
console.log(cmdLine);
cp.execSync(cmdLine, {stdio: "inherit"});
export function toResoruceUri(webviewResourceRoot: string, uri: vscode.Uri): vscode.Uri {
const rootUri = vscode.Uri.parse(webviewResourceRoot);
return rootUri.with({
path: rootUri.path + uri.path,
query: uri.query,
fragment: uri.fragment,
});
}
exec('git fetch distro');
exec(`git checkout ${process.env['npm_package_distro']} -- src/vs/server resources/server`);
exec('git reset HEAD src/vs/server resources/server');

View file

@ -22,5 +22,11 @@
["\"", "\""],
["'", "'"],
["`", "`"]
]
}
],
"folding": {
"markers": {
"start": "^\\s*#\\s*#?region\\b.*",
"end": "^\\s*#\\s*#?endregion\\b.*"
}
}
}

View file

@ -14,7 +14,7 @@
"id": "shellscript",
"aliases": ["Shell Script", "shellscript", "bash", "sh", "zsh", "ksh"],
"extensions": [".sh", ".bash", ".bashrc", ".bash_aliases", ".bash_profile", ".bash_login", ".ebuild", ".install", ".profile", ".bash_logout", ".zsh", ".zshrc", ".zprofile", ".zlogin", ".zlogout", ".zshenv", ".zsh-theme", ".ksh"],
"filenames": ["PKGBUILD"],
"filenames": ["APKBUILD", "PKGBUILD"],
"firstLine": "^#!.*\\b(bash|zsh|sh|tcsh|ksh|ash|qsh).*|^#\\s*-\\*-[^*]*mode:\\s*shell-script[^*]*-\\*-",
"configuration": "./language-configuration.json",
"mimetypes": ["text/x-shellscript"]

View file

@ -591,6 +591,12 @@
"default": true,
"description": "%configuration.surveys.enabled%",
"scope": "window"
},
"typescript.experimental.useSeparateSyntaxServer": {
"type": "boolean",
"default": false,
"description": "%configuration.experimental.useSeparateSyntaxServer%",
"scope": "window"
}
}
},

View file

@ -49,6 +49,7 @@
"typescript.problemMatchers.tsc.label": "TypeScript problems",
"typescript.problemMatchers.tscWatch.label": "TypeScript problems (watch mode)",
"configuration.suggest.paths": "Enable/disable suggestions for paths in import statements and require calls.",
"configuration.experimental.useSeparateSyntaxServer": "Enable/disable spawning a separate TypeScript server that can more quickly respond to syntax related operations, such as calculating folding or computing document symbols. Requires using TypeScript 3.4.0 or newer in the workspace.",
"typescript.locale": "Sets the locale used to report JavaScript and TypeScript errors. Requires using TypeScript 2.6.0 or newer in the workspace. Default of `null` uses VS Code's locale.",
"javascript.implicitProjectConfig.experimentalDecorators": "Enable/disable `experimentalDecorators` for JavaScript files that are not part of a project. Existing jsconfig.json or tsconfig.json files override this setting. Requires using TypeScript 2.3.1 or newer in the workspace.",
"configuration.suggest.autoImports": "Enable/disable auto import suggestions. Requires using TypeScript 2.6.1 or newer in the workspace.",

View file

@ -470,20 +470,20 @@ export default class BufferSyncSupport extends Disposable {
private sendPendingDiagnostics(): void {
const orderedFileSet = this.pendingDiagnostics.getOrderedFileSet();
if (this.pendingGetErr) {
this.pendingGetErr.cancel();
for (const file of this.pendingGetErr.files.entries) {
orderedFileSet.set(file.resource, undefined);
}
}
// Add all open TS buffers to the geterr request. They might be visible
for (const buffer of this.syncedBuffers.values) {
orderedFileSet.set(buffer.resource, undefined);
}
if (orderedFileSet.size) {
if (this.pendingGetErr) {
this.pendingGetErr.cancel();
for (const file of this.pendingGetErr.files.entries) {
orderedFileSet.set(file.resource, undefined);
}
}
const getErr = this.pendingGetErr = GetErrRequest.executeGetErrRequest(this.client, orderedFileSet, () => {
if (this.pendingGetErr === getErr) {
this.pendingGetErr = undefined;

View file

@ -37,10 +37,18 @@ export default class TypeScriptDefinitionProvider extends DefinitionProviderBase
return response.body.definitions
.map((location): vscode.DefinitionLink => {
const target = typeConverters.Location.fromTextSpan(this.client.toResource(location.file), location);
if ((location as any).contextStart) {
return {
originSelectionRange: span,
targetRange: typeConverters.Range.fromLocations((location as any).contextStart, (location as any).contextEnd),
targetUri: target.uri,
targetSelectionRange: target.range,
};
}
return {
originSelectionRange: span,
targetRange: target.range,
targetUri: target.uri,
targetUri: target.uri
};
});
}

View file

@ -26,7 +26,7 @@ export default class TypeScriptImplementationsCodeLensProvider extends TypeScrip
const codeLens = inputCodeLens as ReferencesCodeLens;
const args = typeConverters.Position.toFileLocationRequestArgs(codeLens.file, codeLens.range.start);
const response = await this.client.execute('implementation', args, token, /* lowPriority */ true);
const response = await this.client.execute('implementation', args, token, { lowPriority: true });
if (response.type !== 'response' || !response.body) {
codeLens.command = response.type === 'cancelled'
? TypeScriptBaseCodeLensProvider.cancelledCommand

View file

@ -22,7 +22,7 @@ class TypeScriptReferencesCodeLensProvider extends TypeScriptBaseCodeLensProvide
public async resolveCodeLens(inputCodeLens: vscode.CodeLens, token: vscode.CancellationToken): Promise<vscode.CodeLens> {
const codeLens = inputCodeLens as ReferencesCodeLens;
const args = typeConverters.Position.toFileLocationRequestArgs(codeLens.file, codeLens.range.start);
const response = await this.client.execute('references', args, token, /* lowPriority */ true);
const response = await this.client.execute('references', args, token, { lowPriority: true });
if (response.type !== 'response' || !response.body) {
codeLens.command = response.type === 'cancelled'
? TypeScriptBaseCodeLensProvider.cancelledCommand

View file

@ -234,8 +234,10 @@ class TscTaskProvider implements vscode.TaskProvider {
private getLabelForTasks(project: TSConfig): string {
if (project.workspaceFolder) {
return path.posix.relative(project.workspaceFolder.uri.path, project.posixPath);
const workspaceNormalizedUri = vscode.Uri.file(path.normalize(project.workspaceFolder.uri.fsPath)); // Make sure the drive letter is lowercase
return path.posix.relative(workspaceNormalizedUri.path, project.posixPath);
}
return project.posixPath;
}

View file

@ -6,7 +6,7 @@
import * as assert from 'assert';
import 'mocha';
import * as stream from 'stream';
import { PipeRequestCanceller, ServerProcess, TypeScriptServer } from '../tsServer/server';
import { PipeRequestCanceller, TsServerProcess, ProcessBasedTsServer } from '../tsServer/server';
import { nulToken } from '../utils/cancellation';
import Logger from '../utils/logger';
import TelemetryReporter from '../utils/telemetry';
@ -19,7 +19,7 @@ const NoopTelemetryReporter = new class implements TelemetryReporter {
dispose(): void { /* noop */ }
};
class FakeServerProcess implements ServerProcess {
class FakeServerProcess implements TsServerProcess {
private readonly _out: stream.PassThrough;
private readonly writeListeners = new Set<(data: Buffer) => void>();
@ -62,7 +62,7 @@ suite('Server', () => {
test('should send requests with increasing sequence numbers', async () => {
const process = new FakeServerProcess();
const server = new TypeScriptServer(process, undefined, new PipeRequestCanceller(undefined, tracer), undefined!, NoopTelemetryReporter, tracer);
const server = new ProcessBasedTsServer('semantic', process, undefined, new PipeRequestCanceller('semantic', undefined, tracer), undefined!, NoopTelemetryReporter, tracer);
const onWrite1 = process.onWrite();
server.executeImpl('geterr', {}, { isAsync: false, token: nulToken, expectsResult: true });

View file

@ -3,248 +3,19 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as child_process from 'child_process';
import * as fs from 'fs';
import * as path from 'path';
import * as stream from 'stream';
import * as vscode from 'vscode';
import * as Proto from '../protocol';
import { ServerResponse } from '../typescriptService';
import API from '../utils/api';
import { TsServerLogLevel, TypeScriptServiceConfiguration } from '../utils/configuration';
import { ServerResponse, TypeScriptRequests } from '../typescriptService';
import { Disposable } from '../utils/dispose';
import * as electron from '../utils/electron';
import LogDirectoryProvider from '../utils/logDirectoryProvider';
import Logger from '../utils/logger';
import { TypeScriptPluginPathsProvider } from '../utils/pluginPathsProvider';
import { PluginManager } from '../utils/plugins';
import { escapeRegExp } from '../utils/regexp';
import TelemetryReporter from '../utils/telemetry';
import Tracer from '../utils/tracer';
import { TypeScriptVersion, TypeScriptVersionProvider } from '../utils/versionProvider';
import { TypeScriptVersion } from '../utils/versionProvider';
import { Reader } from '../utils/wireProtocol';
import { CallbackMap } from './callbackMap';
import { RequestItem, RequestQueue, RequestQueueingType } from './requestQueue';
class TypeScriptServerError extends Error {
public static create(
version: TypeScriptVersion,
response: Proto.Response,
): TypeScriptServerError {
const parsedResult = TypeScriptServerError.parseErrorText(version, response);
return new TypeScriptServerError(version, response,
parsedResult ? parsedResult.message : undefined,
parsedResult ? parsedResult.stack : undefined);
}
constructor(
version: TypeScriptVersion,
private readonly response: Proto.Response,
public readonly serverMessage: string | undefined,
public readonly serverStack: string | undefined,
) {
super(`TypeScript Server Error (${version.versionString})\n${serverMessage}\n${serverStack}`);
}
public get serverErrorText() {
return this.response.message;
}
public get serverCommand() {
return this.response.command;
}
/**
* Given a `errorText` from a tsserver request indicating failure in handling a request,
* prepares a payload for telemetry-logging.
*/
private static parseErrorText(
version: TypeScriptVersion,
response: Proto.Response,
) {
const errorText = response.message;
if (errorText) {
const errorPrefix = 'Error processing request. ';
if (errorText.startsWith(errorPrefix)) {
const prefixFreeErrorText = errorText.substr(errorPrefix.length);
const newlineIndex = prefixFreeErrorText.indexOf('\n');
if (newlineIndex >= 0) {
// Newline expected between message and stack.
return {
message: prefixFreeErrorText.substring(0, newlineIndex),
stack: TypeScriptServerError.normalizeMessageStack(version, prefixFreeErrorText.substring(newlineIndex + 1))
};
}
}
}
return undefined;
}
/**
* Try to replace full TS Server paths with 'tsserver.js' so that we don't have to post process the data as much
*/
private static normalizeMessageStack(
version: TypeScriptVersion,
message: string | undefined,
) {
if (!message) {
return '';
}
return message.replace(new RegExp(`${escapeRegExp(version.path)}[/\\\\]tsserver.js:`, 'gi'), 'tsserver.js:');
}
}
export class TypeScriptServerSpawner {
public constructor(
private readonly _versionProvider: TypeScriptVersionProvider,
private readonly _logDirectoryProvider: LogDirectoryProvider,
private readonly _pluginPathsProvider: TypeScriptPluginPathsProvider,
private readonly _logger: Logger,
private readonly _telemetryReporter: TelemetryReporter,
private readonly _tracer: Tracer,
) { }
public spawn(
version: TypeScriptVersion,
configuration: TypeScriptServiceConfiguration,
pluginManager: PluginManager
): TypeScriptServer {
const apiVersion = version.version || API.defaultVersion;
const { args, cancellationPipeName, tsServerLogFile } = this.getTsServerArgs(configuration, version, apiVersion, pluginManager);
if (TypeScriptServerSpawner.isLoggingEnabled(apiVersion, configuration)) {
if (tsServerLogFile) {
this._logger.info(`TSServer log file: ${tsServerLogFile}`);
} else {
this._logger.error('Could not create TSServer log directory');
}
}
this._logger.info('Forking TSServer');
const childProcess = electron.fork(version.tsServerPath, args, this.getForkOptions());
this._logger.info('Started TSServer');
return new TypeScriptServer(
new ChildServerProcess(childProcess),
tsServerLogFile,
new PipeRequestCanceller(cancellationPipeName, this._tracer),
version,
this._telemetryReporter,
this._tracer);
}
private getForkOptions() {
const debugPort = TypeScriptServerSpawner.getDebugPort();
const tsServerForkOptions: electron.ForkOptions = {
execArgv: debugPort ? [`--inspect=${debugPort}`] : [],
};
return tsServerForkOptions;
}
private getTsServerArgs(
configuration: TypeScriptServiceConfiguration,
currentVersion: TypeScriptVersion,
apiVersion: API,
pluginManager: PluginManager,
): { args: string[], cancellationPipeName: string | undefined, tsServerLogFile: string | undefined } {
const args: string[] = [];
let cancellationPipeName: string | undefined;
let tsServerLogFile: string | undefined;
if (apiVersion.gte(API.v206)) {
if (apiVersion.gte(API.v250)) {
args.push('--useInferredProjectPerProjectRoot');
} else {
args.push('--useSingleInferredProject');
}
if (configuration.disableAutomaticTypeAcquisition) {
args.push('--disableAutomaticTypingAcquisition');
}
}
if (apiVersion.gte(API.v208)) {
args.push('--enableTelemetry');
}
if (apiVersion.gte(API.v222)) {
cancellationPipeName = electron.getTempFile('tscancellation');
args.push('--cancellationPipeName', cancellationPipeName + '*');
}
if (TypeScriptServerSpawner.isLoggingEnabled(apiVersion, configuration)) {
const logDir = this._logDirectoryProvider.getNewLogDirectory();
if (logDir) {
tsServerLogFile = path.join(logDir, `tsserver.log`);
args.push('--logVerbosity', TsServerLogLevel.toString(configuration.tsServerLogLevel));
args.push('--logFile', tsServerLogFile);
}
}
if (apiVersion.gte(API.v230)) {
const pluginPaths = this._pluginPathsProvider.getPluginPaths();
if (pluginManager.plugins.length) {
args.push('--globalPlugins', pluginManager.plugins.map(x => x.name).join(','));
const isUsingBundledTypeScriptVersion = currentVersion.path === this._versionProvider.defaultVersion.path;
for (const plugin of pluginManager.plugins) {
if (isUsingBundledTypeScriptVersion || plugin.enableForWorkspaceTypeScriptVersions) {
pluginPaths.push(plugin.path);
}
}
}
if (pluginPaths.length !== 0) {
args.push('--pluginProbeLocations', pluginPaths.join(','));
}
}
if (apiVersion.gte(API.v234)) {
if (configuration.npmLocation) {
args.push('--npmLocation', `"${configuration.npmLocation}"`);
}
}
if (apiVersion.gte(API.v260)) {
args.push('--locale', TypeScriptServerSpawner.getTsLocale(configuration));
}
if (apiVersion.gte(API.v291)) {
args.push('--noGetErrOnBackgroundUpdate');
}
if (apiVersion.gte(API.v345)) {
args.push('--validateDefaultNpmLocation');
}
return { args, cancellationPipeName, tsServerLogFile };
}
private static getDebugPort(): number | undefined {
const value = process.env['TSS_DEBUG'];
if (value) {
const port = parseInt(value);
if (!isNaN(port)) {
return port;
}
}
return undefined;
}
private static isLoggingEnabled(apiVersion: API, configuration: TypeScriptServiceConfiguration) {
return apiVersion.gte(API.v222) &&
configuration.tsServerLogLevel !== TsServerLogLevel.Off;
}
private static getTsLocale(configuration: TypeScriptServiceConfiguration): string {
return configuration.locale
? configuration.locale
: vscode.env.language;
}
}
import { TypeScriptServerError } from './serverError';
export interface OngoingRequestCanceller {
tryCancelOngoingRequest(seq: number): boolean;
@ -252,6 +23,7 @@ export interface OngoingRequestCanceller {
export class PipeRequestCanceller implements OngoingRequestCanceller {
public constructor(
private readonly _serverId: string,
private readonly _cancellationPipeName: string | undefined,
private readonly _tracer: Tracer,
) { }
@ -260,7 +32,7 @@ export class PipeRequestCanceller implements OngoingRequestCanceller {
if (!this._cancellationPipeName) {
return false;
}
this._tracer.logTrace(`TypeScript Server: trying to cancel ongoing request with sequence number ${seq}`);
this._tracer.logTrace(this._serverId, `TypeScript Server: trying to cancel ongoing request with sequence number ${seq}`);
try {
fs.writeFileSync(this._cancellationPipeName + seq, '');
} catch {
@ -270,7 +42,24 @@ export class PipeRequestCanceller implements OngoingRequestCanceller {
}
}
export interface ServerProcess {
export interface ITypeScriptServer {
readonly onEvent: vscode.Event<Proto.Event>;
readonly onExit: vscode.Event<any>;
readonly onError: vscode.Event<any>;
readonly onReaderError: vscode.Event<Error>;
readonly tsServerLogFile: string | undefined;
kill(): void;
executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: false, lowPriority?: boolean }): undefined;
executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>>;
executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>> | undefined;
dispose(): void;
}
export interface TsServerProcess {
readonly stdout: stream.Readable;
write(serverRequest: Proto.Request): void;
@ -280,37 +69,15 @@ export interface ServerProcess {
kill(): void;
}
class ChildServerProcess implements ServerProcess {
public constructor(
private readonly _process: child_process.ChildProcess,
) { }
get stdout(): stream.Readable { return this._process.stdout!; }
write(serverRequest: Proto.Request): void {
this._process.stdin!.write(JSON.stringify(serverRequest) + '\r\n', 'utf8');
}
on(name: 'exit', handler: (code: number | null) => void): void;
on(name: 'error', handler: (error: Error) => void): void;
on(name: any, handler: any) {
this._process.on(name, handler);
}
kill(): void {
this._process.kill();
}
}
export class TypeScriptServer extends Disposable {
export class ProcessBasedTsServer extends Disposable implements ITypeScriptServer {
private readonly _reader: Reader<Proto.Response>;
private readonly _requestQueue = new RequestQueue();
private readonly _callbacks = new CallbackMap<Proto.Response>();
private readonly _pendingResponses = new Set<number>();
constructor(
private readonly _process: ServerProcess,
private readonly _serverId: string,
private readonly _process: TsServerProcess,
private readonly _tsServerLogFile: string | undefined,
private readonly _requestCanceller: OngoingRequestCanceller,
private readonly _version: TypeScriptVersion,
@ -371,11 +138,11 @@ export class TypeScriptServer extends Disposable {
const seq = (event as Proto.RequestCompletedEvent).body.request_seq;
const p = this._callbacks.fetch(seq);
if (p) {
this._tracer.traceRequestCompleted('requestCompleted', seq, p.startTime);
this._tracer.traceRequestCompleted(this._serverId, 'requestCompleted', seq, p.startTime);
p.onSuccess(undefined);
}
} else {
this._tracer.traceEvent(event);
this._tracer.traceEvent(this._serverId, event);
this._onEvent.fire(event);
}
break;
@ -391,7 +158,7 @@ export class TypeScriptServer extends Disposable {
private tryCancelRequest(seq: number, command: string): boolean {
try {
if (this._requestQueue.tryDeletePendingRequest(seq)) {
this._tracer.logTrace(`TypeScript Server: canceled request with sequence number ${seq}`);
this.logTrace(`Canceled request with sequence number ${seq}`);
return true;
}
@ -399,7 +166,7 @@ export class TypeScriptServer extends Disposable {
return true;
}
this._tracer.logTrace(`TypeScript Server: tried to cancel request with sequence number ${seq}. But request got already delivered.`);
this.logTrace(`Tried to cancel request with sequence number ${seq}. But request got already delivered.`);
return false;
} finally {
const callback = this.fetchCallback(seq);
@ -415,26 +182,26 @@ export class TypeScriptServer extends Disposable {
return;
}
this._tracer.traceResponse(response, callback.startTime);
this._tracer.traceResponse(this._serverId, response, callback.startTime);
if (response.success) {
callback.onSuccess(response);
} else if (response.message === 'No content available.') {
// Special case where response itself is successful but there is not any data to return.
callback.onSuccess(ServerResponse.NoContent);
} else {
callback.onError(TypeScriptServerError.create(this._version, response));
callback.onError(TypeScriptServerError.create(this._serverId, this._version, response));
}
}
public executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: false, lowPriority?: boolean }): undefined;
public executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>>;
public executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>> | undefined {
public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: false, lowPriority?: boolean }): undefined;
public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>>;
public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>> | undefined {
const request = this._requestQueue.createRequest(command, args);
const requestInfo: RequestItem = {
request,
expectsResponse: executeInfo.expectsResult,
isAsync: executeInfo.isAsync,
queueingType: getQueueingType(command, executeInfo.lowPriority)
queueingType: ProcessBasedTsServer.getQueueingType(command, executeInfo.lowPriority)
};
let result: Promise<ServerResponse.Response<Proto.Response>> | undefined;
if (executeInfo.expectsResult) {
@ -490,7 +257,7 @@ export class TypeScriptServer extends Disposable {
private sendRequest(requestItem: RequestItem): void {
const serverRequest = requestItem.request;
this._tracer.traceRequest(serverRequest, requestItem.expectsResponse, this._requestQueue.length);
this._tracer.traceRequest(this._serverId, serverRequest, requestItem.expectsResponse, this._requestQueue.length);
if (requestItem.expectsResponse && !requestItem.isAsync) {
this._pendingResponses.add(requestItem.request.seq);
@ -515,17 +282,112 @@ export class TypeScriptServer extends Disposable {
this._pendingResponses.delete(seq);
return callback;
}
}
const fenceCommands = new Set(['change', 'close', 'open', 'updateOpen']);
function getQueueingType(
command: string,
lowPriority?: boolean
): RequestQueueingType {
if (fenceCommands.has(command)) {
return RequestQueueingType.Fence;
private logTrace(message: string) {
this._tracer.logTrace(this._serverId, message);
}
private static readonly fenceCommands = new Set(['change', 'close', 'open', 'updateOpen']);
private static getQueueingType(
command: string,
lowPriority?: boolean
): RequestQueueingType {
if (ProcessBasedTsServer.fenceCommands.has(command)) {
return RequestQueueingType.Fence;
}
return lowPriority ? RequestQueueingType.LowPriority : RequestQueueingType.Normal;
}
return lowPriority ? RequestQueueingType.LowPriority : RequestQueueingType.Normal;
}
export class SyntaxRoutingTsServer extends Disposable implements ITypeScriptServer {
public constructor(
private readonly syntaxServer: ITypeScriptServer,
private readonly semanticServer: ITypeScriptServer,
) {
super();
this._register(syntaxServer.onEvent(e => this._onEvent.fire(e)));
this._register(semanticServer.onEvent(e => this._onEvent.fire(e)));
this._register(semanticServer.onExit(e => this._onExit.fire(e)));
this._register(semanticServer.onError(e => this._onError.fire(e)));
}
private readonly _onEvent = this._register(new vscode.EventEmitter<Proto.Event>());
public readonly onEvent = this._onEvent.event;
private readonly _onExit = this._register(new vscode.EventEmitter<any>());
public readonly onExit = this._onExit.event;
private readonly _onError = this._register(new vscode.EventEmitter<any>());
public readonly onError = this._onError.event;
public get onReaderError() { return this.semanticServer.onReaderError; }
public get tsServerLogFile() { return this.semanticServer.tsServerLogFile; }
public kill(): void {
this.syntaxServer.kill();
this.semanticServer.kill();
}
private static readonly syntaxCommands = new Set<keyof TypeScriptRequests>([
'navtree',
'getOutliningSpans',
'jsxClosingTag',
'selectionRange',
'format',
'formatonkey',
'docCommentTemplate',
]);
private static readonly sharedCommands = new Set<keyof TypeScriptRequests>([
'change',
'close',
'open',
'updateOpen',
'configure',
'configurePlugin',
]);
public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: false, lowPriority?: boolean }): undefined;
public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>>;
public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>> | undefined {
if (SyntaxRoutingTsServer.syntaxCommands.has(command)) {
return this.syntaxServer.executeImpl(command, args, executeInfo);
} else if (SyntaxRoutingTsServer.sharedCommands.has(command)) {
// Dispatch to both server but only return from syntax one
// Also make sure we never cancel requests to just one server
let hasCompletedSyntax = false;
let hasCompletedSemantic = false;
let token: vscode.CancellationToken | undefined = undefined;
if (executeInfo.token) {
const source = new vscode.CancellationTokenSource();
executeInfo.token.onCancellationRequested(() => {
if (hasCompletedSyntax && !hasCompletedSemantic || hasCompletedSemantic && !hasCompletedSyntax) {
// Don't cancel.
// One of the servers completed this request so we don't want to leave the other
// in a different state
return;
}
source.cancel();
});
token = source.token;
}
const semanticRequest = this.semanticServer.executeImpl(command, args, { ...executeInfo, token });
if (semanticRequest) {
semanticRequest.finally(() => { hasCompletedSemantic = true; });
}
const syntaxRequest = this.syntaxServer.executeImpl(command, args, { ...executeInfo, token });
if (syntaxRequest) {
syntaxRequest.finally(() => { hasCompletedSyntax = true; });
}
return syntaxRequest;
} else {
return this.semanticServer.executeImpl(command, args, executeInfo);
}
}
}

View file

@ -0,0 +1,66 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as Proto from '../protocol';
import { escapeRegExp } from '../utils/regexp';
import { TypeScriptVersion } from '../utils/versionProvider';
export class TypeScriptServerError extends Error {
public static create(
serverId: string,
version: TypeScriptVersion,
response: Proto.Response
): TypeScriptServerError {
const parsedResult = TypeScriptServerError.parseErrorText(version, response);
return new TypeScriptServerError(serverId, version, response, parsedResult ? parsedResult.message : undefined, parsedResult ? parsedResult.stack : undefined);
}
private constructor(
serverId: string,
version: TypeScriptVersion,
private readonly response: Proto.Response,
public readonly serverMessage: string | undefined,
public readonly serverStack: string | undefined
) {
super(`<${serverId}> TypeScript Server Error (${version.versionString})\n${serverMessage}\n${serverStack}`);
}
public get serverErrorText() { return this.response.message; }
public get serverCommand() { return this.response.command; }
/**
* Given a `errorText` from a tsserver request indicating failure in handling a request,
* prepares a payload for telemetry-logging.
*/
private static parseErrorText(version: TypeScriptVersion, response: Proto.Response) {
const errorText = response.message;
if (errorText) {
const errorPrefix = 'Error processing request. ';
if (errorText.startsWith(errorPrefix)) {
const prefixFreeErrorText = errorText.substr(errorPrefix.length);
const newlineIndex = prefixFreeErrorText.indexOf('\n');
if (newlineIndex >= 0) {
// Newline expected between message and stack.
return {
message: prefixFreeErrorText.substring(0, newlineIndex),
stack: TypeScriptServerError.normalizeMessageStack(version, prefixFreeErrorText.substring(newlineIndex + 1))
};
}
}
}
return undefined;
}
/**
* Try to replace full TS Server paths with 'tsserver.js' so that we don't have to post process the data as much
*/
private static normalizeMessageStack(version: TypeScriptVersion, message: string | undefined) {
if (!message) {
return '';
}
return message.replace(new RegExp(`${escapeRegExp(version.path)}[/\\\\]tsserver.js:`, 'gi'), 'tsserver.js:');
}
}

View file

@ -0,0 +1,229 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as child_process from 'child_process';
import * as path from 'path';
import * as stream from 'stream';
import * as vscode from 'vscode';
import * as Proto from '../protocol';
import API from '../utils/api';
import { TsServerLogLevel, TypeScriptServiceConfiguration } from '../utils/configuration';
import * as electron from '../utils/electron';
import LogDirectoryProvider from '../utils/logDirectoryProvider';
import Logger from '../utils/logger';
import { TypeScriptPluginPathsProvider } from '../utils/pluginPathsProvider';
import { PluginManager } from '../utils/plugins';
import TelemetryReporter from '../utils/telemetry';
import Tracer from '../utils/tracer';
import { TypeScriptVersion, TypeScriptVersionProvider } from '../utils/versionProvider';
import { ITypeScriptServer, PipeRequestCanceller, ProcessBasedTsServer, SyntaxRoutingTsServer, TsServerProcess } from './server';
type ServerKind = 'main' | 'syntax' | 'semantic';
export class TypeScriptServerSpawner {
public constructor(
private readonly _versionProvider: TypeScriptVersionProvider,
private readonly _logDirectoryProvider: LogDirectoryProvider,
private readonly _pluginPathsProvider: TypeScriptPluginPathsProvider,
private readonly _logger: Logger,
private readonly _telemetryReporter: TelemetryReporter,
private readonly _tracer: Tracer,
) { }
public spawn(
version: TypeScriptVersion,
configuration: TypeScriptServiceConfiguration,
pluginManager: PluginManager
): ITypeScriptServer {
if (this.shouldUseSeparateSyntaxServer(version, configuration)) {
const syntaxServer = this.spawnTsServer('syntax', version, configuration, pluginManager);
const semanticServer = this.spawnTsServer('semantic', version, configuration, pluginManager);
return new SyntaxRoutingTsServer(syntaxServer, semanticServer);
}
return this.spawnTsServer('main', version, configuration, pluginManager);
}
private shouldUseSeparateSyntaxServer(
version: TypeScriptVersion,
configuration: TypeScriptServiceConfiguration,
): boolean {
return configuration.useSeparateSyntaxServer && !!version.apiVersion && version.apiVersion.gte(API.v340);
}
private spawnTsServer(
kind: ServerKind,
version: TypeScriptVersion,
configuration: TypeScriptServiceConfiguration,
pluginManager: PluginManager,
): ITypeScriptServer {
const apiVersion = version.apiVersion || API.defaultVersion;
const { args, cancellationPipeName, tsServerLogFile } = this.getTsServerArgs(kind, configuration, version, apiVersion, pluginManager);
if (TypeScriptServerSpawner.isLoggingEnabled(apiVersion, configuration)) {
if (tsServerLogFile) {
this._logger.info(`<${kind}> Log file: ${tsServerLogFile}`);
} else {
this._logger.error(`<${kind}> Could not create log directory`);
}
}
this._logger.info(`<${kind}> Forking...`);
const childProcess = electron.fork(version.tsServerPath, args, this.getForkOptions(kind));
this._logger.info(`<${kind}> Starting...`);
return new ProcessBasedTsServer(
kind,
new ChildServerProcess(childProcess),
tsServerLogFile,
new PipeRequestCanceller(kind, cancellationPipeName, this._tracer),
version,
this._telemetryReporter,
this._tracer);
}
private getForkOptions(kind: ServerKind) {
const debugPort = TypeScriptServerSpawner.getDebugPort(kind);
const tsServerForkOptions: electron.ForkOptions = {
execArgv: debugPort ? [`--inspect=${debugPort}`] : [],
};
return tsServerForkOptions;
}
private getTsServerArgs(
kind: ServerKind,
configuration: TypeScriptServiceConfiguration,
currentVersion: TypeScriptVersion,
apiVersion: API,
pluginManager: PluginManager,
): { args: string[], cancellationPipeName: string | undefined, tsServerLogFile: string | undefined } {
const args: string[] = [];
let cancellationPipeName: string | undefined;
let tsServerLogFile: string | undefined;
if (kind === 'syntax') {
args.push('--syntaxOnly');
}
if (apiVersion.gte(API.v206)) {
if (apiVersion.gte(API.v250)) {
args.push('--useInferredProjectPerProjectRoot');
} else {
args.push('--useSingleInferredProject');
}
if (configuration.disableAutomaticTypeAcquisition || kind === 'syntax') {
args.push('--disableAutomaticTypingAcquisition');
}
}
if (apiVersion.gte(API.v208) && kind !== 'syntax') {
args.push('--enableTelemetry');
}
if (apiVersion.gte(API.v222)) {
cancellationPipeName = electron.getTempFile('tscancellation');
args.push('--cancellationPipeName', cancellationPipeName + '*');
}
if (TypeScriptServerSpawner.isLoggingEnabled(apiVersion, configuration)) {
const logDir = this._logDirectoryProvider.getNewLogDirectory();
if (logDir) {
tsServerLogFile = path.join(logDir, `tsserver.log`);
args.push('--logVerbosity', TsServerLogLevel.toString(configuration.tsServerLogLevel));
args.push('--logFile', tsServerLogFile);
}
}
if (apiVersion.gte(API.v230)) {
const pluginPaths = this._pluginPathsProvider.getPluginPaths();
if (pluginManager.plugins.length) {
args.push('--globalPlugins', pluginManager.plugins.map(x => x.name).join(','));
const isUsingBundledTypeScriptVersion = currentVersion.path === this._versionProvider.defaultVersion.path;
for (const plugin of pluginManager.plugins) {
if (isUsingBundledTypeScriptVersion || plugin.enableForWorkspaceTypeScriptVersions) {
pluginPaths.push(plugin.path);
}
}
}
if (pluginPaths.length !== 0) {
args.push('--pluginProbeLocations', pluginPaths.join(','));
}
}
if (apiVersion.gte(API.v234)) {
if (configuration.npmLocation) {
args.push('--npmLocation', `"${configuration.npmLocation}"`);
}
}
if (apiVersion.gte(API.v260)) {
args.push('--locale', TypeScriptServerSpawner.getTsLocale(configuration));
}
if (apiVersion.gte(API.v291)) {
args.push('--noGetErrOnBackgroundUpdate');
}
if (apiVersion.gte(API.v345)) {
args.push('--validateDefaultNpmLocation');
}
return { args, cancellationPipeName, tsServerLogFile };
}
private static getDebugPort(kind: ServerKind): number | undefined {
if (kind === 'syntax') {
// We typically only want to debug the main semantic server
return undefined;
}
const value = process.env['TSS_DEBUG'];
if (value) {
const port = parseInt(value);
if (!isNaN(port)) {
return port;
}
}
return undefined;
}
private static isLoggingEnabled(apiVersion: API, configuration: TypeScriptServiceConfiguration) {
return apiVersion.gte(API.v222) &&
configuration.tsServerLogLevel !== TsServerLogLevel.Off;
}
private static getTsLocale(configuration: TypeScriptServiceConfiguration): string {
return configuration.locale
? configuration.locale
: vscode.env.language;
}
}
class ChildServerProcess implements TsServerProcess {
public constructor(
private readonly _process: child_process.ChildProcess,
) { }
get stdout(): stream.Readable { return this._process.stdout!; }
write(serverRequest: Proto.Request): void {
this._process.stdin!.write(JSON.stringify(serverRequest) + '\r\n', 'utf8');
}
on(name: 'exit', handler: (code: number | null) => void): void;
on(name: 'error', handler: (error: Error) => void): void;
on(name: any, handler: any) {
this._process.on(name, handler);
}
kill(): void {
this._process.kill();
}
}

View file

@ -26,7 +26,7 @@ export namespace ServerResponse {
export type Response<T extends Proto.Response> = T | Cancelled | typeof NoContent;
}
export interface TypeScriptRequestTypes {
interface StandardTsServerRequests {
'applyCodeActionCommand': [Proto.ApplyCodeActionCommandRequestArgs, Proto.ApplyCodeActionCommandResponse];
'completionEntryDetails': [Proto.CompletionDetailsRequestArgs, Proto.CompletionDetailsResponse];
'completionInfo': [Proto.CompletionsRequestArgs, Proto.CompletionInfoResponse];
@ -59,6 +59,26 @@ export interface TypeScriptRequestTypes {
'typeDefinition': [Proto.FileLocationRequestArgs, Proto.TypeDefinitionResponse];
}
interface NoResponseTsServerRequests {
'open': [Proto.OpenRequestArgs, null];
'close': [Proto.FileRequestArgs];
'change': [Proto.ChangeRequestArgs, null];
'updateOpen': [Proto.UpdateOpenRequestArgs, null];
'compilerOptionsForInferredProjects': [Proto.SetCompilerOptionsForInferredProjectsArgs, null];
'reloadProjects': [null, null];
'configurePlugin': [Proto.ConfigurePluginRequest, Proto.ConfigurePluginResponse];
}
interface AsyncTsServerRequests {
'geterr': [Proto.GeterrRequestArgs, Proto.Response];
}
export type TypeScriptRequests = StandardTsServerRequests & NoResponseTsServerRequests & AsyncTsServerRequests;
export type ExecConfig = {
lowPriority?: boolean;
};
export interface ITypeScriptServiceClient {
/**
* Convert a resource (VS Code) to a normalized path (TypeScript).
@ -100,19 +120,17 @@ export interface ITypeScriptServiceClient {
readonly logger: Logger;
readonly bufferSyncSupport: BufferSyncSupport;
execute<K extends keyof TypeScriptRequestTypes>(
execute<K extends keyof StandardTsServerRequests>(
command: K,
args: TypeScriptRequestTypes[K][0],
args: StandardTsServerRequests[K][0],
token: vscode.CancellationToken,
lowPriority?: boolean
): Promise<ServerResponse.Response<TypeScriptRequestTypes[K][1]>>;
config?: ExecConfig
): Promise<ServerResponse.Response<StandardTsServerRequests[K][1]>>;
executeWithoutWaitingForResponse(command: 'open', args: Proto.OpenRequestArgs): void;
executeWithoutWaitingForResponse(command: 'close', args: Proto.FileRequestArgs): void;
executeWithoutWaitingForResponse(command: 'change', args: Proto.ChangeRequestArgs): void;
executeWithoutWaitingForResponse(command: 'updateOpen', args: Proto.UpdateOpenRequestArgs): void;
executeWithoutWaitingForResponse(command: 'compilerOptionsForInferredProjects', args: Proto.SetCompilerOptionsForInferredProjectsArgs): void;
executeWithoutWaitingForResponse(command: 'reloadProjects', args: null): void;
executeWithoutWaitingForResponse<K extends keyof NoResponseTsServerRequests>(
command: K,
args: NoResponseTsServerRequests[K][0]
): void;
executeAsync(command: 'geterr', args: Proto.GeterrRequestArgs, token: vscode.CancellationToken): Promise<ServerResponse.Response<Proto.Response>>;

View file

@ -10,8 +10,8 @@ import * as nls from 'vscode-nls';
import BufferSyncSupport from './features/bufferSyncSupport';
import { DiagnosticKind, DiagnosticsManager } from './features/diagnostics';
import * as Proto from './protocol';
import { TypeScriptServer, TypeScriptServerSpawner } from './tsServer/server';
import { ITypeScriptServiceClient, ServerResponse } from './typescriptService';
import { ITypeScriptServer } from './tsServer/server';
import { ITypeScriptServiceClient, ServerResponse, TypeScriptRequests, ExecConfig } from './typescriptService';
import API from './utils/api';
import { TsServerLogLevel, TypeScriptServiceConfiguration } from './utils/configuration';
import { Disposable } from './utils/dispose';
@ -25,6 +25,7 @@ import Tracer from './utils/tracer';
import { inferredProjectConfig } from './utils/tsconfig';
import { TypeScriptVersionPicker } from './utils/versionPicker';
import { TypeScriptVersion, TypeScriptVersionProvider } from './utils/versionProvider';
import { TypeScriptServerSpawner } from './tsServer/spawner';
const localize = nls.loadMessageBundle();
@ -46,7 +47,7 @@ namespace ServerState {
export class Running {
readonly type = Type.Running;
constructor(
public readonly server: TypeScriptServer,
public readonly server: ITypeScriptServer,
/**
* API version obtained from the version picker after checking the corresponding path exists.
@ -284,7 +285,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType
currentVersion = this.versionPicker.currentVersion;
}
const apiVersion = this.versionPicker.currentVersion.version || API.defaultVersion;
const apiVersion = this.versionPicker.currentVersion.apiVersion || API.defaultVersion;
this.onDidChangeTypeScriptVersion(currentVersion);
let mytoken = ++this.token;
@ -352,7 +353,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType
handle.onEvent(event => this.dispatchEvent(event));
this._onReady!.resolve();
this._onTsServerStarted.fire(currentVersion.version);
this._onTsServerStarted.fire(currentVersion.apiVersion);
if (apiVersion.gte(API.v300)) {
this.loadingIndicator.startedLoadingProject(undefined /* projectName */);
@ -606,16 +607,16 @@ export default class TypeScriptServiceClient extends Disposable implements IType
return undefined;
}
public execute(command: string, args: any, token: vscode.CancellationToken, lowPriority?: boolean): Promise<ServerResponse.Response<Proto.Response>> {
public execute(command: keyof TypeScriptRequests, args: any, token: vscode.CancellationToken, config?: ExecConfig): Promise<ServerResponse.Response<Proto.Response>> {
return this.executeImpl(command, args, {
isAsync: false,
token,
expectsResult: true,
lowPriority
lowPriority: config ? config.lowPriority : undefined
});
}
public executeWithoutWaitingForResponse(command: string, args: any): void {
public executeWithoutWaitingForResponse(command: keyof TypeScriptRequests, args: any): void {
this.executeImpl(command, args, {
isAsync: false,
token: undefined,
@ -623,7 +624,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType
});
}
public executeAsync(command: string, args: Proto.GeterrRequestArgs, token: vscode.CancellationToken): Promise<ServerResponse.Response<Proto.Response>> {
public executeAsync(command: keyof TypeScriptRequests, args: Proto.GeterrRequestArgs, token: vscode.CancellationToken): Promise<ServerResponse.Response<Proto.Response>> {
return this.executeImpl(command, args, {
isAsync: true,
token,
@ -631,9 +632,9 @@ export default class TypeScriptServiceClient extends Disposable implements IType
});
}
private executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: false, lowPriority?: boolean }): undefined;
private executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>>;
private executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>> | undefined {
private executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: false, lowPriority?: boolean }): undefined;
private executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>>;
private executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>> | undefined {
this.bufferSyncSupport.beforeCommand(command);
const runningServerState = this.service();
return runningServerState.server.executeImpl(command, args, executeInfo);
@ -768,7 +769,6 @@ export default class TypeScriptServiceClient extends Disposable implements IType
this.logTelemetry(telemetryData.telemetryEventName, properties);
}
private configurePlugin(pluginName: string, configuration: {}): any {
if (this.apiVersion.gte(API.v314)) {
this.executeWithoutWaitingForResponse('configurePlugin', { pluginName, configuration });

View file

@ -54,6 +54,7 @@ export class TypeScriptServiceConfiguration {
public readonly checkJs: boolean;
public readonly experimentalDecorators: boolean;
public readonly disableAutomaticTypeAcquisition: boolean;
public readonly useSeparateSyntaxServer: boolean;
public static loadFromWorkspace(): TypeScriptServiceConfiguration {
return new TypeScriptServiceConfiguration();
@ -71,6 +72,7 @@ export class TypeScriptServiceConfiguration {
this.checkJs = TypeScriptServiceConfiguration.readCheckJs(configuration);
this.experimentalDecorators = TypeScriptServiceConfiguration.readExperimentalDecorators(configuration);
this.disableAutomaticTypeAcquisition = TypeScriptServiceConfiguration.readDisableAutomaticTypeAcquisition(configuration);
this.useSeparateSyntaxServer = TypeScriptServiceConfiguration.readUseSeparateSyntaxServer(configuration);
}
public isEqualTo(other: TypeScriptServiceConfiguration): boolean {
@ -82,7 +84,8 @@ export class TypeScriptServiceConfiguration {
&& this.checkJs === other.checkJs
&& this.experimentalDecorators === other.experimentalDecorators
&& this.disableAutomaticTypeAcquisition === other.disableAutomaticTypeAcquisition
&& arrays.equals(this.tsServerPluginPaths, other.tsServerPluginPaths);
&& arrays.equals(this.tsServerPluginPaths, other.tsServerPluginPaths)
&& this.useSeparateSyntaxServer === other.useSeparateSyntaxServer;
}
private static fixPathPrefixes(inspectValue: string): string {
@ -139,4 +142,8 @@ export class TypeScriptServiceConfiguration {
private static extractLocale(configuration: vscode.WorkspaceConfiguration): string | null {
return configuration.get<string | null>('typescript.locale', null);
}
private static readUseSeparateSyntaxServer(configuration: vscode.WorkspaceConfiguration): boolean {
return configuration.get<boolean>('typescript.experimental.useSeparateSyntaxServer', false);
}
}

View file

@ -7,13 +7,14 @@ import * as temp from './temp';
import path = require('path');
import fs = require('fs');
import cp = require('child_process');
import process = require('process');
const getRootTempDir = (() => {
let dir: string | undefined;
return () => {
if (!dir) {
dir = temp.getTempFile(`vscode-typescript`);
dir = temp.getTempFile(`vscode-typescript${process.platform !== 'win32' && process.getuid ? process.getuid() : ''}`);
}
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);

View file

@ -9,7 +9,6 @@ import { RelativeWorkspacePathResolver } from './relativePathResolver';
export class TypeScriptPluginPathsProvider {
public readonly relativePathResolver: RelativeWorkspacePathResolver = new RelativeWorkspacePathResolver();
public constructor(
private configuration: TypeScriptServiceConfiguration
@ -32,7 +31,7 @@ export class TypeScriptPluginPathsProvider {
return [pluginPath];
}
const workspacePath = this.relativePathResolver.asAbsoluteWorkspacePath(pluginPath);
const workspacePath = RelativeWorkspacePathResolver.asAbsoluteWorkspacePath(pluginPath);
if (workspacePath !== undefined) {
return [workspacePath];
}

View file

@ -6,7 +6,7 @@ import * as path from 'path';
import * as vscode from 'vscode';
export class RelativeWorkspacePathResolver {
public asAbsoluteWorkspacePath(relativePath: string): string | undefined {
public static asAbsoluteWorkspacePath(relativePath: string): string | undefined {
for (const root of vscode.workspace.workspaceFolders || []) {
const rootPrefixes = [`./${root.name}/`, `${root.name}/`, `.\\${root.name}\\`, `${root.name}\\`];
for (const rootPrefix of rootPrefixes) {

View file

@ -50,7 +50,7 @@ export default class Tracer {
return result;
}
public traceRequest(request: Proto.Request, responseExpected: boolean, queueLength: number): void {
public traceRequest(serverId: string, request: Proto.Request, responseExpected: boolean, queueLength: number): void {
if (this.trace === Trace.Off) {
return;
}
@ -58,10 +58,10 @@ export default class Tracer {
if (this.trace === Trace.Verbose && request.arguments) {
data = `Arguments: ${JSON.stringify(request.arguments, null, 4)}`;
}
this.logTrace(`Sending request: ${request.command} (${request.seq}). Response expected: ${responseExpected ? 'yes' : 'no'}. Current queue length: ${queueLength}`, data);
this.logTrace(serverId, `Sending request: ${request.command} (${request.seq}). Response expected: ${responseExpected ? 'yes' : 'no'}. Current queue length: ${queueLength}`, data);
}
public traceResponse(response: Proto.Response, startTime: number): void {
public traceResponse(serverId: string, response: Proto.Response, startTime: number): void {
if (this.trace === Trace.Off) {
return;
}
@ -69,17 +69,17 @@ export default class Tracer {
if (this.trace === Trace.Verbose && response.body) {
data = `Result: ${JSON.stringify(response.body, null, 4)}`;
}
this.logTrace(`Response received: ${response.command} (${response.request_seq}). Request took ${Date.now() - startTime} ms. Success: ${response.success} ${!response.success ? '. Message: ' + response.message : ''}`, data);
this.logTrace(serverId, `Response received: ${response.command} (${response.request_seq}). Request took ${Date.now() - startTime} ms. Success: ${response.success} ${!response.success ? '. Message: ' + response.message : ''}`, data);
}
public traceRequestCompleted(command: string, request_seq: number, startTime: number): any {
public traceRequestCompleted(serverId: string, command: string, request_seq: number, startTime: number): any {
if (this.trace === Trace.Off) {
return;
}
this.logTrace(`Async response received: ${command} (${request_seq}). Request took ${Date.now() - startTime} ms.`);
this.logTrace(serverId, `Async response received: ${command} (${request_seq}). Request took ${Date.now() - startTime} ms.`);
}
public traceEvent(event: Proto.Event): void {
public traceEvent(serverId: string, event: Proto.Event): void {
if (this.trace === Trace.Off) {
return;
}
@ -87,12 +87,12 @@ export default class Tracer {
if (this.trace === Trace.Verbose && event.body) {
data = `Data: ${JSON.stringify(event.body, null, 4)}`;
}
this.logTrace(`Event received: ${event.event} (${event.seq}).`, data);
this.logTrace(serverId, `Event received: ${event.event} (${event.seq}).`, data);
}
public logTrace(message: string, data?: any): void {
public logTrace(serverId: string, message: string, data?: any): void {
if (this.trace !== Trace.Off) {
this.logger.logLevel('Trace', message, data);
this.logger.logLevel('Trace', `<${serverId}> ${message}`, data);
}
}
}

View file

@ -13,9 +13,12 @@ import { ITypeScriptServiceClient } from '../typescriptService';
export namespace Range {
export const fromTextSpan = (span: Proto.TextSpan): vscode.Range =>
fromLocations(span.start, span.end);
export const fromLocations = (start: Proto.Location, end: Proto.Location): vscode.Range =>
new vscode.Range(
Math.max(0, span.start.line - 1), Math.max(span.start.offset - 1, 0),
Math.max(0, span.end.line - 1), Math.max(0, span.end.offset - 1));
Math.max(0, start.line - 1), Math.max(start.offset - 1, 0),
Math.max(0, end.line - 1), Math.max(0, end.offset - 1));
export const toFileRangeRequestArgs = (file: string, range: vscode.Range): Proto.FileRangeRequestArgs => ({
file,

View file

@ -113,7 +113,7 @@ export class TypeScriptVersionPicker {
return { oldVersion: previousVersion, newVersion: shippedVersion };
case MessageAction.learnMore:
vscode.commands.executeCommand('vscode.open', vscode.Uri.parse('https://go.microsoft.com/fwlink/?linkid=839919'));
vscode.env.openExternal(vscode.Uri.parse('https://go.microsoft.com/fwlink/?linkid=839919'));
return { oldVersion: this.currentVersion };
default:

View file

@ -27,10 +27,10 @@ export class TypeScriptVersion {
}
public get isValid(): boolean {
return this.version !== undefined;
return this.apiVersion !== undefined;
}
public get version(): API | undefined {
public get apiVersion(): API | undefined {
const version = this.getTypeScriptVersion(this.tsServerPath);
if (version) {
return version;
@ -46,7 +46,7 @@ export class TypeScriptVersion {
}
public get versionString(): string {
const version = this.version;
const version = this.apiVersion;
return version ? version.versionString : localize(
'couldNotLoadTsVersion', 'Could not load the TypeScript version at this path');
}
@ -88,7 +88,6 @@ export class TypeScriptVersion {
}
export class TypeScriptVersionProvider {
private readonly relativePathResolver: RelativeWorkspacePathResolver = new RelativeWorkspacePathResolver();
public constructor(
private configuration: TypeScriptServiceConfiguration
@ -179,7 +178,7 @@ export class TypeScriptVersionProvider {
return [new TypeScriptVersion(tsdkPathSetting)];
}
const workspacePath = this.relativePathResolver.asAbsoluteWorkspacePath(tsdkPathSetting);
const workspacePath = RelativeWorkspacePathResolver.asAbsoluteWorkspacePath(tsdkPathSetting);
if (workspacePath !== undefined) {
return [new TypeScriptVersion(workspacePath, tsdkPathSetting)];
}

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { env } from 'vscode';
import { env, extensions, ExtensionKind } from 'vscode';
suite('env-namespace', () => {
@ -24,4 +24,22 @@ suite('env-namespace', () => {
assert.throws(() => (env as any).sessionId = '234');
});
test('env.remoteName', function () {
const remoteName = env.remoteName;
const apiTestExtension = extensions.getExtension('vscode.vscode-api-tests');
const testResolverExtension = extensions.getExtension('vscode.vscode-test-resolver');
if (typeof remoteName === 'undefined') {
assert.ok(apiTestExtension);
assert.ok(testResolverExtension);
assert.equal(ExtensionKind.UI, apiTestExtension!.extensionKind);
assert.equal(ExtensionKind.UI, testResolverExtension!.extensionKind);
} else if (typeof remoteName === 'string') {
assert.ok(apiTestExtension);
assert.ok(!testResolverExtension); // we currently can only access extensions that run on same host
assert.equal(ExtensionKind.Workspace, apiTestExtension!.extensionKind);
} else {
assert.fail();
}
});
});

View file

@ -251,7 +251,7 @@ suite('Webview tests', () => {
});
</script>`);
const workspaceRootUri = vscode.Uri.file(vscode.workspace.rootPath!).with({ scheme: 'vscode-resource' });
const workspaceRootUri = webview.webview.resourceRoot + vscode.Uri.file(vscode.workspace.rootPath!).path;
{
const imagePath = workspaceRootUri.toString() + '/image.png';

View file

@ -569,7 +569,7 @@ suite('window namespace tests', () => {
});
});
suite('Terminal', () => {
(process.platform === 'win32' ? suite.skip /* https://github.com/microsoft/vscode/issues/75689 */ : suite)('Terminal', () => {
test('sendText immediately after createTerminal should not throw', () => {
const terminal = window.createTerminal();
assert.doesNotThrow(terminal.sendText.bind(terminal, 'echo "foo"'));
@ -738,8 +738,8 @@ suite('window namespace tests', () => {
terminal1.show();
});
test('runInBackground terminal: onDidWriteData should work', done => {
const terminal = window.createTerminal({ name: 'bg', runInBackground: true });
test('hideFromUser terminal: onDidWriteData should work', done => {
const terminal = window.createTerminal({ name: 'bg', hideFromUser: true });
let data = '';
terminal.onDidWriteData(e => {
data += e;
@ -751,8 +751,8 @@ suite('window namespace tests', () => {
terminal.sendText('foo');
});
test('runInBackground terminal: should be available to terminals API', done => {
const terminal = window.createTerminal({ name: 'bg', runInBackground: true });
test('hideFromUser terminal: should be available to terminals API', done => {
const terminal = window.createTerminal({ name: 'bg', hideFromUser: true });
window.onDidOpenTerminal(t => {
assert.equal(t, terminal);
assert.equal(t.name, 'bg');

View file

@ -0,0 +1,119 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import * as vscode from 'vscode';
import { posix } from 'path';
suite('workspace-fs', () => {
let root: vscode.Uri;
suiteSetup(function () {
root = vscode.workspace.workspaceFolders![0]!.uri;
});
test('fs.stat', async function () {
const stat = await vscode.workspace.fs.stat(root);
assert.equal(stat.type, vscode.FileType.Directory);
assert.equal(typeof stat.size, 'number');
assert.equal(typeof stat.mtime, 'number');
assert.equal(typeof stat.ctime, 'number');
const entries = await vscode.workspace.fs.readDirectory(root);
assert.ok(entries.length > 0);
// find far.js
const tuple = entries.find(tuple => tuple[0] === 'far.js')!;
assert.ok(tuple);
assert.equal(tuple[0], 'far.js');
assert.equal(tuple[1], vscode.FileType.File);
});
test('fs.stat - bad scheme', async function () {
try {
await vscode.workspace.fs.stat(vscode.Uri.parse('foo:/bar/baz/test.txt'));
assert.ok(false);
} catch {
assert.ok(true);
}
});
test('fs.stat - missing file', async function () {
try {
await vscode.workspace.fs.stat(root.with({ path: root.path + '.bad' }));
assert.ok(false);
} catch (e) {
assert.ok(true);
}
});
test('fs.write/stat/delete', async function () {
const uri = root.with({ path: posix.join(root.path, 'new.file') });
await vscode.workspace.fs.writeFile(uri, Buffer.from('HELLO'));
const stat = await vscode.workspace.fs.stat(uri);
assert.equal(stat.type, vscode.FileType.File);
await vscode.workspace.fs.delete(uri);
try {
await vscode.workspace.fs.stat(uri);
assert.ok(false);
} catch {
assert.ok(true);
}
});
test('fs.delete folder', async function () {
const folder = root.with({ path: posix.join(root.path, 'folder') });
const file = root.with({ path: posix.join(root.path, 'folder/file') });
await vscode.workspace.fs.createDirectory(folder);
await vscode.workspace.fs.writeFile(file, Buffer.from('FOO'));
await vscode.workspace.fs.stat(folder);
await vscode.workspace.fs.stat(file);
// ensure non empty folder cannot be deleted
try {
await vscode.workspace.fs.delete(folder, { recursive: false });
assert.ok(false);
} catch {
await vscode.workspace.fs.stat(folder);
await vscode.workspace.fs.stat(file);
}
// ensure non empty folder cannot be deleted is DEFAULT
try {
await vscode.workspace.fs.delete(folder); // recursive: false as default
assert.ok(false);
} catch {
await vscode.workspace.fs.stat(folder);
await vscode.workspace.fs.stat(file);
}
// delete non empty folder with recursive-flag
await vscode.workspace.fs.delete(folder, { recursive: true });
// esnure folder/file are gone
try {
await vscode.workspace.fs.stat(folder);
assert.ok(false);
} catch {
assert.ok(true);
}
try {
await vscode.workspace.fs.stat(file);
assert.ok(false);
} catch {
assert.ok(true);
}
});
});

View file

@ -1,7 +1,7 @@
{
"name": "code-oss-dev",
"version": "1.36.0",
"distro": "0293a04fc4ec308a7770025ead5660e2fb9667a9",
"version": "1.37.0",
"distro": "b49e556c274bfe2fc7f4fafd3804cb4ebd948c4f",
"author": {
"name": "Microsoft Corporation"
},
@ -24,10 +24,7 @@
"smoketest": "cd test/smoke && node test/index.js",
"download-builtin-extensions": "node build/lib/builtInExtensions.js",
"monaco-compile-check": "tsc -p src/tsconfig.monaco.json --noEmit",
"strict-initialization-watch": "tsc --watch -p src/tsconfig.json --noEmit --strictPropertyInitialization",
"web": "node resources/server/bin-dev/code-web.js --port 9888",
"web-selfhost": "node resources/server/bin-dev/code-web.js --port 9777 --selfhost --folder .",
"install-server": "node build/npm/install-server.js"
"strict-initialization-watch": "tsc --watch -p src/tsconfig.json --noEmit --strictPropertyInitialization"
},
"dependencies": {
"applicationinsights": "1.0.8",
@ -40,7 +37,7 @@
"keytar": "4.2.1",
"minimist": "1.2.0",
"native-is-elevated": "^0.2.1",
"native-keymap": "1.2.5",
"native-keymap": "1.2.6",
"native-watchdog": "1.0.0",
"node-pty": "0.9.0-beta17",
"onigasm-umd": "^2.2.2",
@ -54,7 +51,7 @@
"vscode-ripgrep": "^1.2.5",
"vscode-sqlite3": "4.0.7",
"vscode-textmate": "^4.1.1",
"xterm": "3.15.0-beta42",
"xterm": "3.15.0-beta50",
"xterm-addon-search": "0.1.0-beta6",
"xterm-addon-web-links": "0.1.0-beta10",
"yauzl": "^2.9.2",
@ -79,7 +76,7 @@
"debounce": "^1.0.0",
"documentdb": "^1.5.1",
"electron-mksnapshot": "~2.0.0",
"eslint": "^3.4.0",
"eslint": "^4.18.2",
"event-stream": "3.3.4",
"express": "^4.13.1",
"fancy-log": "^1.3.3",
@ -108,7 +105,7 @@
"gulp-vinyl-zip": "^2.1.2",
"http-server": "^0.11.1",
"husky": "^0.13.1",
"innosetup-compiler": "^5.5.60",
"innosetup": "5.6.1",
"is": "^3.1.0",
"istanbul": "^0.3.17",
"jsdom-no-contextify": "^3.1.0",

View file

@ -12,7 +12,7 @@
"keytar": "4.2.1",
"minimist": "1.2.0",
"native-watchdog": "1.0.0",
"node-pty": "0.8.1",
"node-pty": "0.9.0-beta17",
"onigasm-umd": "^2.2.2",
"semver": "^5.5.0",
"spdlog": "^0.9.0",
@ -21,13 +21,14 @@
"vscode-proxy-agent": "0.4.0",
"vscode-ripgrep": "^1.2.5",
"vscode-textmate": "^4.1.1",
"yauzl": "^2.9.2",
"yazl": "^2.4.3",
"xterm": "3.15.0-beta34",
"xterm-addon-search": "0.1.0-beta6",
"xterm-addon-web-links": "0.1.0-beta10"
"xterm-addon-web-links": "0.1.0-beta10",
"yauzl": "^2.9.2",
"yazl": "^2.4.3"
},
"optionalDependencies": {
"vscode-windows-ca-certs": "0.1.0"
"vscode-windows-ca-certs": "0.1.0",
"vscode-windows-registry": "1.0.1"
}
}

View file

@ -599,11 +599,6 @@ ms@2.0.0:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
nan@2.12.1:
version "2.12.1"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.12.1.tgz#7b1aa193e9aa86057e3c7bbd0ac448e770925552"
integrity sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==
nan@2.8.0:
version "2.8.0"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.8.0.tgz#ed715f3fe9de02b57a5e6252d90a96675e1f085a"
@ -614,7 +609,7 @@ nan@^2.10.0:
resolved "https://registry.yarnpkg.com/nan/-/nan-2.11.0.tgz#574e360e4d954ab16966ec102c0c049fd961a099"
integrity sha512-F4miItu2rGnV2ySkXOQoA8FKz/SR2Q2sWP0sbTxNxz/tuokeC8WxOhPMcwi0qIyGtVn/rrSeLbvVkznqCdwYnw==
nan@^2.12.1, nan@^2.14.0:
nan@^2.12.1, nan@^2.13.2, nan@^2.14.0:
version "2.14.0"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c"
integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==
@ -636,12 +631,12 @@ node-addon-api@1.6.2:
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.6.2.tgz#d8aad9781a5cfc4132cc2fecdbdd982534265217"
integrity sha512-479Bjw9nTE5DdBSZZWprFryHGjUaQC31y1wHo19We/k0BZlrmhqQitWoUL0cD8+scljCbIUL+E58oRDEakdGGA==
node-pty@0.8.1:
version "0.8.1"
resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.8.1.tgz#94b457bec013e7a09b8d9141f63b0787fa25c23f"
integrity sha512-j+/g0Q5dR+vkELclpJpz32HcS3O/3EdPSGPvDXJZVJQLCvgG0toEbfmymxAEyQyZEpaoKHAcoL+PvKM+4N9nlw==
node-pty@0.9.0-beta17:
version "0.9.0-beta17"
resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.9.0-beta17.tgz#9b490df86a8124dea595e9fbedeaaf4b2eedbbcb"
integrity sha512-E94XwIs3JxLKAboquHY9Kytbbj/T/tJtRpQoAUdfPE7UXRta/NV+xdmRNhZkeU9jCji+plm656GbYFievgNPkQ==
dependencies:
nan "2.12.1"
nan "^2.13.2"
noop-logger@^0.1.1:
version "0.1.1"
@ -1117,6 +1112,13 @@ vscode-windows-ca-certs@0.1.0:
dependencies:
node-addon-api "1.6.2"
vscode-windows-registry@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/vscode-windows-registry/-/vscode-windows-registry-1.0.1.tgz#bc9f765563eb6dc1c9ad9a41f9eaacc84dfadc7c"
integrity sha512-q0aKXi9Py1OBdmXIJJFeJBzpPJMMUxMJNBU9FysWIXEwJyMQGEVevKzM2J3Qz/cHSc5LVqibmoUWzZ7g+97qRg==
dependencies:
nan "^2.12.1"
which-pm-runs@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb"

View file

@ -50,7 +50,7 @@ _code()
--uninstall-extension --enable-proposed-api --verbose --log -s
--status -p --performance --prof-startup --disable-extensions
--disable-extension --inspect-extensions
--inspect-brk-extensions --disable-gpu --upload-logs
--inspect-brk-extensions --disable-gpu
--max-memory=' -- "$cur") )
[[ $COMPREPLY == *= ]] && compopt -o nospace
return

View file

@ -14,6 +14,7 @@ arguments=(
'--user-data-dir[specify the directory that user data is kept in]:directory:_directories'
'(- *)'{-v,--version}'[print version]'
'(- *)'{-h,--help}'[print usage]'
'(- *)'{--telemetry}'[Shows all telemetry events which VS code collects.]'
'--extensions-dir[set the root path for extensions]:root path:_directories'
'--list-extensions[list the installed extensions]'
'--show-versions[show versions of installed extensions, when using --list-extension]'
@ -30,7 +31,6 @@ arguments=(
'--inspect-extensions[allow debugging and profiling of extensions]'
'--inspect-brk-extensions[allow debugging and profiling of extensions with the extension host being paused after start]'
'--disable-gpu[disable GPU hardware acceleration]'
'--upload-logs[upload logs from current session to a secure endpoint]:confirm:(iConfirmLogsUpload)'
'--max-memory=[max memory size for a window (in Mbytes)]:size (Mbytes)'
'*:file or directory:_files'
)

View file

@ -11,8 +11,18 @@ VSCODE_PATH="$(dirname "$(dirname "$(realpath "$0")")")"
ELECTRON="$VSCODE_PATH/$NAME.exe"
if grep -qi Microsoft /proc/version; then
# in a wsl shell
WSL_BUILD=$(uname -r | sed -E 's/^.+-([0-9]+)-[Mm]icrosoft/\1/')
if [ $WSL_BUILD -ge 17063 ] 2> /dev/null; then
if ! [ -z "$WSL_DISTRO_NAME" ]; then
# $WSL_DISTRO_NAME is available since WSL builds 18362, also for WSL2
WSL_BUILD=18362
else
WSL_BUILD=$(uname -r | sed -E 's/^.+-([0-9]+)-Microsoft/\1/')
if [ -z "$WSL_BUILD" ]; then
WSL_BUILD=0
fi
fi
if [ $WSL_BUILD -ge 17063 ]; then
# $WSL_DISTRO_NAME is available since WSL builds 18362, also for WSL2
# WSLPATH is available since WSL build 17046
# WSLENV is available since WSL build 17063
export WSLENV=ELECTRON_RUN_AS_NODE/w:$WSLENV

View file

@ -1,3 +0,0 @@
$env:npm_config_disturl="https://atom.io/download/electron"
$env:npm_config_target=(node -p "require('./build/lib/electron').getElectronVersion();")
$env:npm_config_runtime="electron"

View file

@ -1,6 +0,0 @@
#!/bin/bash
export npm_config_disturl=https://atom.io/download/electron
export npm_config_target=$(node -p "require('./build/lib/electron').getElectronVersion();")
export npm_config_runtime=electron
export npm_config_cache="$HOME/.npm-electron"
mkdir -p "$npm_config_cache"

View file

@ -10,16 +10,16 @@ call .\scripts\test.bat --runGlob **\*.integrationTest.js %*
if %errorlevel% neq 0 exit /b %errorlevel%
:: Tests in the extension host
call .\scripts\code.bat %~dp0\..\extensions\vscode-api-tests\testWorkspace --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\singlefolder-tests --disable-extensions --user-data-dir=%VSCODEUSERDATADIR%
call .\scripts\code.bat %~dp0\..\extensions\vscode-api-tests\testWorkspace --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\singlefolder-tests --disable-extensions --disable-inspect --user-data-dir=%VSCODEUSERDATADIR%
if %errorlevel% neq 0 exit /b %errorlevel%
call .\scripts\code.bat %~dp0\..\extensions\vscode-api-tests\testworkspace.code-workspace --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\workspace-tests --disable-extensions --user-data-dir=%VSCODEUSERDATADIR%
call .\scripts\code.bat %~dp0\..\extensions\vscode-api-tests\testworkspace.code-workspace --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\workspace-tests --disable-extensions --disable-inspect --user-data-dir=%VSCODEUSERDATADIR%
if %errorlevel% neq 0 exit /b %errorlevel%
call .\scripts\code.bat %~dp0\..\extensions\vscode-colorize-tests\test --extensionDevelopmentPath=%~dp0\..\extensions\vscode-colorize-tests --extensionTestsPath=%~dp0\..\extensions\vscode-colorize-tests\out --disable-extensions --user-data-dir=%VSCODEUSERDATADIR%
call .\scripts\code.bat %~dp0\..\extensions\vscode-colorize-tests\test --extensionDevelopmentPath=%~dp0\..\extensions\vscode-colorize-tests --extensionTestsPath=%~dp0\..\extensions\vscode-colorize-tests\out --disable-extensions --disable-inspect --user-data-dir=%VSCODEUSERDATADIR%
if %errorlevel% neq 0 exit /b %errorlevel%
call .\scripts\code.bat $%~dp0\..\extensions\emmet\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\emmet --extensionTestsPath=%~dp0\..\extensions\emmet\out\test --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% .
call .\scripts\code.bat $%~dp0\..\extensions\emmet\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\emmet --extensionTestsPath=%~dp0\..\extensions\emmet\out\test --disable-extensions --disable-inspect --user-data-dir=%VSCODEUSERDATADIR% .
if %errorlevel% neq 0 exit /b %errorlevel%
:: Tests in commonJS (HTML, CSS, JSON language server tests...)

View file

@ -16,13 +16,13 @@ cd $ROOT
./scripts/test.sh --runGlob **/*.integrationTest.js "$@"
# Tests in the extension host
./scripts/code.sh $ROOT/extensions/vscode-api-tests/testWorkspace --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/singlefolder-tests --disable-extensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started
./scripts/code.sh $ROOT/extensions/vscode-api-tests/testworkspace.code-workspace --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/workspace-tests --disable-extensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started
./scripts/code.sh $ROOT/extensions/vscode-colorize-tests/test --extensionDevelopmentPath=$ROOT/extensions/vscode-colorize-tests --extensionTestsPath=$ROOT/extensions/vscode-colorize-tests/out --disable-extensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started
./scripts/code.sh $ROOT/extensions/markdown-language-features/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/markdown-language-features --extensionTestsPath=$ROOT/extensions/markdown-language-features/out/test --disable-extensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started
./scripts/code.sh $ROOT/extensions/vscode-api-tests/testWorkspace --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/singlefolder-tests --disable-extensions --skip-getting-started --disable-inspect --user-data-dir=$VSCODEUSERDATADIR
./scripts/code.sh $ROOT/extensions/vscode-api-tests/testworkspace.code-workspace --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/workspace-tests --disable-extensions --skip-getting-started --disable-inspect --user-data-dir=$VSCODEUSERDATADIR
./scripts/code.sh $ROOT/extensions/vscode-colorize-tests/test --extensionDevelopmentPath=$ROOT/extensions/vscode-colorize-tests --extensionTestsPath=$ROOT/extensions/vscode-colorize-tests/out --disable-extensions --skip-getting-started --disable-inspect --user-data-dir=$VSCODEUSERDATADIR
./scripts/code.sh $ROOT/extensions/markdown-language-features/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/markdown-language-features --extensionTestsPath=$ROOT/extensions/markdown-language-features/out/test --disable-extensions --skip-getting-started --disable-inspect --user-data-dir=$VSCODEUSERDATADIR
mkdir -p $ROOT/extensions/emmet/test-fixtures
./scripts/code.sh $ROOT/extensions/emmet/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disable-extensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started
./scripts/code.sh $ROOT/extensions/emmet/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disable-extensions --skip-getting-started --disable-inspect --user-data-dir=$VSCODEUSERDATADIR
rm -rf $ROOT/extensions/emmet/test-fixtures
if [ -f ./resources/server/test/test-remote-integration.sh ]; then

View file

@ -1,4 +1,4 @@
// Type definitions for Electron 4.2.4
// Type definitions for Electron 4.2.5
// Project: http://electronjs.org/
// Definitions by: The Electron Team <https://github.com/electron/electron>
// Definitions: https://github.com/electron/electron-typescript-definitions

View file

@ -122,6 +122,7 @@ export const isSafari = (!isChrome && (userAgent.indexOf('Safari') >= 0));
export const isWebkitWebView = (!isChrome && !isSafari && isWebKit);
export const isIPad = (userAgent.indexOf('iPad') >= 0);
export const isEdgeWebView = isEdge && (userAgent.indexOf('WebView/') >= 0);
export const isStandalone = (window.matchMedia('(display-mode: standalone)').matches);
export function hasClipboardSupport() {
if (isIE) {

View file

@ -38,12 +38,12 @@ export function isInDOM(node: Node | null): boolean {
}
interface IDomClassList {
hasClass(node: HTMLElement, className: string): boolean;
addClass(node: HTMLElement, className: string): void;
addClasses(node: HTMLElement, ...classNames: string[]): void;
removeClass(node: HTMLElement, className: string): void;
removeClasses(node: HTMLElement, ...classNames: string[]): void;
toggleClass(node: HTMLElement, className: string, shouldHaveIt?: boolean): void;
hasClass(node: HTMLElement | SVGElement, className: string): boolean;
addClass(node: HTMLElement | SVGElement, className: string): void;
addClasses(node: HTMLElement | SVGElement, ...classNames: string[]): void;
removeClass(node: HTMLElement | SVGElement, className: string): void;
removeClasses(node: HTMLElement | SVGElement, ...classNames: string[]): void;
toggleClass(node: HTMLElement | SVGElement, className: string, shouldHaveIt?: boolean): void;
}
const _manualClassList = new class implements IDomClassList {
@ -191,12 +191,12 @@ const _nativeClassList = new class implements IDomClassList {
// In IE11 there is only partial support for `classList` which makes us keep our
// custom implementation. Otherwise use the native implementation, see: http://caniuse.com/#search=classlist
const _classList: IDomClassList = browser.isIE ? _manualClassList : _nativeClassList;
export const hasClass: (node: HTMLElement, className: string) => boolean = _classList.hasClass.bind(_classList);
export const addClass: (node: HTMLElement, className: string) => void = _classList.addClass.bind(_classList);
export const addClasses: (node: HTMLElement, ...classNames: string[]) => void = _classList.addClasses.bind(_classList);
export const removeClass: (node: HTMLElement, className: string) => void = _classList.removeClass.bind(_classList);
export const removeClasses: (node: HTMLElement, ...classNames: string[]) => void = _classList.removeClasses.bind(_classList);
export const toggleClass: (node: HTMLElement, className: string, shouldHaveIt?: boolean) => void = _classList.toggleClass.bind(_classList);
export const hasClass: (node: HTMLElement | SVGElement, className: string) => boolean = _classList.hasClass.bind(_classList);
export const addClass: (node: HTMLElement | SVGElement, className: string) => void = _classList.addClass.bind(_classList);
export const addClasses: (node: HTMLElement | SVGElement, ...classNames: string[]) => void = _classList.addClasses.bind(_classList);
export const removeClass: (node: HTMLElement | SVGElement, className: string) => void = _classList.removeClass.bind(_classList);
export const removeClasses: (node: HTMLElement | SVGElement, ...classNames: string[]) => void = _classList.removeClasses.bind(_classList);
export const toggleClass: (node: HTMLElement | SVGElement, className: string, shouldHaveIt?: boolean) => void = _classList.toggleClass.bind(_classList);
class DomListener implements IDisposable {
@ -804,8 +804,7 @@ export function createCSSRule(selector: string, cssText: string, style: HTMLStyl
if (!style || !cssText) {
return;
}
(<CSSStyleSheet>style.sheet).insertRule(selector + '{' + cssText + '}', 0);
style.textContent = `${selector}{${cssText}}\n${style.textContent}`;
}
export function removeCSSRulesContainingSelector(ruleName: string, style: HTMLStyleElement = getSharedStyleSheet()): void {
@ -858,6 +857,8 @@ export const EventType = {
ERROR: 'error',
RESIZE: 'resize',
SCROLL: 'scroll',
FULLSCREEN_CHANGE: 'fullscreenchange',
WK_FULLSCREEN_CHANGE: 'webkitfullscreenchange',
// Form
SELECT: 'select',
CHANGE: 'change',
@ -987,14 +988,28 @@ export function prepend<T extends Node>(parent: HTMLElement, child: T): T {
const SELECTOR_REGEX = /([\w\-]+)?(#([\w\-]+))?((.([\w\-]+))*)/;
export function $<T extends HTMLElement>(description: string, attrs?: { [key: string]: any; }, ...children: Array<Node | string>): T {
export enum Namespace {
HTML = 'http://www.w3.org/1999/xhtml',
SVG = 'http://www.w3.org/2000/svg'
}
function _$<T extends Element>(namespace: Namespace, description: string, attrs?: { [key: string]: any; }, ...children: Array<Node | string>): T {
let match = SELECTOR_REGEX.exec(description);
if (!match) {
throw new Error('Bad use of emmet');
}
let result = document.createElement(match[1] || 'div');
attrs = { ...(attrs || {}) };
let tagName = match[1] || 'div';
let result: T;
if (namespace !== Namespace.HTML) {
result = document.createElementNS(namespace as string, tagName) as T;
} else {
result = document.createElement(tagName) as unknown as T;
}
if (match[3]) {
result.id = match[3];
@ -1003,7 +1018,6 @@ export function $<T extends HTMLElement>(description: string, attrs?: { [key: st
result.className = match[4].replace(/\./g, ' ').trim();
}
attrs = attrs || {};
Object.keys(attrs).forEach(name => {
const value = attrs![name];
if (/^on\w+$/.test(name)) {
@ -1030,6 +1044,14 @@ export function $<T extends HTMLElement>(description: string, attrs?: { [key: st
return result as T;
}
export function $<T extends HTMLElement>(description: string, attrs?: { [key: string]: any; }, ...children: Array<Node | string>): T {
return _$(Namespace.HTML, description, attrs, ...children);
}
$.SVG = function <T extends SVGElement>(description: string, attrs?: { [key: string]: any; }, ...children: Array<Node | string>): T {
return _$(Namespace.SVG, description, attrs, ...children);
};
export function join(nodes: Node[], separator: Node | string): Node[] {
const result: Node[] = [];

View file

@ -259,9 +259,6 @@ export class ActionViewItem extends BaseActionViewItem {
this.label.setAttribute('role', 'menuitem');
} else {
this.label.setAttribute('role', 'button');
// TODO @misolori remove before shipping stable
this.label.setAttribute('data-title', this._action.id);
}
}

View file

@ -108,6 +108,10 @@ export class BaseDropdown extends ActionRunner {
this.visible = false;
}
isVisible(): boolean {
return this.visible;
}
protected onEvent(e: Event, activeElement: HTMLElement): void {
this.hide();
}

View file

@ -857,6 +857,7 @@ export interface IListStyles {
listFilterWidgetOutline?: Color;
listFilterWidgetNoMatchesOutline?: Color;
listMatchesShadow?: Color;
treeIndentGuidesStroke?: Color;
}
const defaultStyles: IListStyles = {
@ -867,7 +868,8 @@ const defaultStyles: IListStyles = {
listFocusAndSelectionForeground: Color.fromHex('#FFFFFF'),
listInactiveSelectionBackground: Color.fromHex('#3F3F46'),
listHoverBackground: Color.fromHex('#2A2D2E'),
listDropBackground: Color.fromHex('#383B3D')
listDropBackground: Color.fromHex('#383B3D'),
treeIndentGuidesStroke: Color.fromHex('#a9a9a9')
};
const DefaultOptions = {
@ -1112,6 +1114,7 @@ export class List<T> implements ISpliceable<T>, IDisposable {
return Event.map(this._onPin.event, indexes => this.toListEvent({ indexes }));
}
get domId(): string { return this.view.domId; }
get onDidScroll(): Event<ScrollEvent> { return this.view.onDidScroll; }
get onMouseClick(): Event<IListMouseEvent<T>> { return this.view.onMouseClick; }
get onMouseDblClick(): Event<IListMouseEvent<T>> { return this.view.onMouseDblClick; }

View file

@ -18,7 +18,7 @@ import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableEle
import { ScrollbarVisibility, ScrollEvent } from 'vs/base/common/scrollable';
import { Event, Emitter } from 'vs/base/common/event';
import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview';
import { isLinux } from 'vs/base/common/platform';
import { isLinux, isMacintosh } from 'vs/base/common/platform';
function createMenuMnemonicRegExp() {
try {
@ -91,7 +91,7 @@ export class Menu extends ActionBar {
context: options.context,
actionRunner: options.actionRunner,
ariaLabel: options.ariaLabel,
triggerKeys: { keys: [KeyCode.Enter], keyDown: true }
triggerKeys: { keys: [KeyCode.Enter, ...(isMacintosh ? [KeyCode.Space] : [])], keyDown: true }
});
this.menuElement = menuElement;
@ -109,7 +109,7 @@ export class Menu extends ActionBar {
// Stop tab navigation of menus
if (event.equals(KeyCode.Tab)) {
EventHelper.stop(e, true);
e.preventDefault();
}
});

View file

@ -14,10 +14,12 @@ import { cleanMnemonic, IMenuOptions, Menu, MENU_ESCAPED_MNEMONIC_REGEX, MENU_MN
import { ActionRunner, IAction, IActionRunner } from 'vs/base/common/actions';
import { RunOnceScheduler } from 'vs/base/common/async';
import { Event, Emitter } from 'vs/base/common/event';
import { KeyCode, ResolvedKeybinding } from 'vs/base/common/keyCodes';
import { KeyCode, ResolvedKeybinding, KeyMod } from 'vs/base/common/keyCodes';
import { Disposable, dispose, IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { withNullAsUndefined } from 'vs/base/common/types';
import { asArray } from 'vs/base/common/arrays';
import { ScanCodeUtils, ScanCode } from 'vs/base/common/scanCode';
import { isMacintosh } from 'vs/base/common/platform';
const $ = DOM.$;
@ -115,9 +117,9 @@ export class MenuBar extends Disposable {
let eventHandled = true;
const key = !!e.key ? e.key.toLocaleLowerCase() : '';
if (event.equals(KeyCode.LeftArrow)) {
if (event.equals(KeyCode.LeftArrow) || (isMacintosh && event.equals(KeyCode.Tab | KeyMod.Shift))) {
this.focusPrevious();
} else if (event.equals(KeyCode.RightArrow)) {
} else if (event.equals(KeyCode.RightArrow) || (isMacintosh && event.equals(KeyCode.Tab))) {
this.focusNext();
} else if (event.equals(KeyCode.Escape) && this.isFocused && !this.isOpen) {
this.setUnfocusedState();
@ -128,6 +130,11 @@ export class MenuBar extends Disposable {
eventHandled = false;
}
// Never allow default tab behavior
if (event.equals(KeyCode.Tab | KeyMod.Shift) || event.equals(KeyCode.Tab)) {
event.preventDefault();
}
if (eventHandled) {
event.preventDefault();
event.stopPropagation();
@ -777,6 +784,13 @@ export class MenuBar extends Disposable {
return;
}
// Prevent alt-key default if the menu is not hidden and we use alt to focus
if (modifierKeyStatus.event && !this.options.disableAltFocus) {
if (ScanCodeUtils.toEnum(modifierKeyStatus.event.code) === ScanCode.AltLeft) {
modifierKeyStatus.event.preventDefault();
}
}
// Alt key pressed while menu is focused. This should return focus away from the menubar
if (this.isFocused && modifierKeyStatus.lastKeyPressed === 'alt' && modifierKeyStatus.altKey) {
this.setUnfocusedState();
@ -892,6 +906,7 @@ interface IModifierKeyStatus {
ctrlKey: boolean;
lastKeyPressed?: ModifierKey;
lastKeyReleased?: ModifierKey;
event?: KeyboardEvent;
}
@ -930,6 +945,7 @@ class ModifierKeyEmitter extends Emitter<IModifierKeyStatus> {
this._keyStatus.shiftKey = e.shiftKey;
if (this._keyStatus.lastKeyPressed) {
this._keyStatus.event = e;
this.fire(this._keyStatus);
}
}));
@ -954,6 +970,7 @@ class ModifierKeyEmitter extends Emitter<IModifierKeyStatus> {
this._keyStatus.shiftKey = e.shiftKey;
if (this._keyStatus.lastKeyReleased) {
this._keyStatus.event = e;
this.fire(this._keyStatus);
}
}));

View file

@ -4,10 +4,10 @@
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/tree';
import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { IDisposable, dispose, Disposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { IListOptions, List, IListStyles, mightProducePrintableCharacter, MouseController } from 'vs/base/browser/ui/list/listWidget';
import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListEvent, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction, IKeyboardNavigationLabelProvider, IIdentityProvider } from 'vs/base/browser/ui/list/list';
import { append, $, toggleClass, getDomNodePagePosition, removeClass, addClass, hasClass, hasParentWithClass } from 'vs/base/browser/dom';
import { append, $, toggleClass, getDomNodePagePosition, removeClass, addClass, hasClass, hasParentWithClass, createStyleSheet, clearNode } from 'vs/base/browser/dom';
import { Event, Relay, Emitter, EventBufferer } from 'vs/base/common/event';
import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
@ -25,6 +25,7 @@ import { isMacintosh } from 'vs/base/common/platform';
import { values } from 'vs/base/common/map';
import { clamp } from 'vs/base/common/numbers';
import { ScrollEvent } from 'vs/base/common/scrollable';
import { SetMap } from 'vs/base/common/collections';
function asTreeDragAndDropData<T, TFilterData>(data: IDragAndDropData): IDragAndDropData {
if (data instanceof ElementsDragAndDropData) {
@ -188,12 +189,48 @@ export class ComposedTreeDelegate<T, N extends { element: T }> implements IListV
interface ITreeListTemplateData<T> {
readonly container: HTMLElement;
readonly indent: HTMLElement;
readonly twistie: HTMLElement;
indentGuidesDisposable: IDisposable;
readonly templateData: T;
}
export enum RenderIndentGuides {
None = 'none',
OnHover = 'onHover',
Always = 'always'
}
interface ITreeRendererOptions {
readonly indent?: number;
readonly renderIndentGuides?: RenderIndentGuides;
}
interface IRenderData<TTemplateData> {
templateData: ITreeListTemplateData<TTemplateData>;
height: number;
}
interface Collection<T> {
readonly elements: T[];
readonly onDidChange: Event<T[]>;
}
class EventCollection<T> implements Collection<T> {
private disposables = new DisposableStore();
get elements(): T[] {
return this._elements;
}
constructor(readonly onDidChange: Event<T[]>, private _elements: T[] = []) {
onDidChange(e => this._elements = e, null, this.disposables);
}
dispose() {
this.disposables.dispose();
}
}
class TreeRenderer<T, TFilterData, TTemplateData> implements IListRenderer<ITreeNode<T, TFilterData>, ITreeListTemplateData<TTemplateData>> {
@ -202,13 +239,20 @@ class TreeRenderer<T, TFilterData, TTemplateData> implements IListRenderer<ITree
readonly templateId: string;
private renderedElements = new Map<T, ITreeNode<T, TFilterData>>();
private renderedNodes = new Map<ITreeNode<T, TFilterData>, ITreeListTemplateData<TTemplateData>>();
private renderedNodes = new Map<ITreeNode<T, TFilterData>, IRenderData<TTemplateData>>();
private indent: number = TreeRenderer.DefaultIndent;
private _renderIndentGuides: RenderIndentGuides = RenderIndentGuides.None;
private renderedIndentGuides = new SetMap<ITreeNode<T, TFilterData>, HTMLDivElement>();
private activeParentNodes = new Set<ITreeNode<T, TFilterData>>();
private indentGuidesDisposable: IDisposable = Disposable.None;
private disposables: IDisposable[] = [];
constructor(
private renderer: ITreeRenderer<T, TFilterData, TTemplateData>,
onDidChangeCollapseState: Event<ICollapseStateChangeEvent<T, TFilterData>>,
private activeNodes: Collection<ITreeNode<T, TFilterData>>,
options: ITreeRendererOptions = {}
) {
this.templateId = renderer.templateId;
@ -226,34 +270,57 @@ class TreeRenderer<T, TFilterData, TTemplateData> implements IListRenderer<ITree
this.indent = clamp(options.indent, 0, 40);
}
this.renderedNodes.forEach((templateData, node) => {
templateData.twistie.style.marginLeft = `${node.depth * this.indent}px`;
});
if (typeof options.renderIndentGuides !== 'undefined') {
const renderIndentGuides = options.renderIndentGuides;
if (renderIndentGuides !== this._renderIndentGuides) {
this._renderIndentGuides = renderIndentGuides;
if (renderIndentGuides) {
const disposables = new DisposableStore();
this.activeNodes.onDidChange(this._onDidChangeActiveNodes, this, disposables);
this.indentGuidesDisposable = disposables;
this._onDidChangeActiveNodes(this.activeNodes.elements);
} else {
this.indentGuidesDisposable.dispose();
}
}
}
}
renderTemplate(container: HTMLElement): ITreeListTemplateData<TTemplateData> {
const el = append(container, $('.monaco-tl-row'));
const indent = append(el, $('.monaco-tl-indent'));
const twistie = append(el, $('.monaco-tl-twistie'));
const contents = append(el, $('.monaco-tl-contents'));
const templateData = this.renderer.renderTemplate(contents);
return { container, twistie, templateData };
return { container, indent, twistie, indentGuidesDisposable: Disposable.None, templateData };
}
renderElement(node: ITreeNode<T, TFilterData>, index: number, templateData: ITreeListTemplateData<TTemplateData>, height: number | undefined): void {
if (typeof height === 'number') {
this.renderedNodes.set(node, templateData);
this.renderedNodes.set(node, { templateData, height });
this.renderedElements.set(node.element, node);
}
const indent = TreeRenderer.DefaultIndent + (node.depth - 1) * this.indent;
templateData.twistie.style.marginLeft = `${indent}px`;
this.update(node, templateData);
templateData.indent.style.width = `${indent + this.indent - 16}px`;
this.renderTwistie(node, templateData);
if (typeof height === 'number') {
this.renderIndentGuides(node, templateData);
}
this.renderer.renderElement(node, index, templateData.templateData, height);
}
disposeElement(node: ITreeNode<T, TFilterData>, index: number, templateData: ITreeListTemplateData<TTemplateData>, height: number | undefined): void {
templateData.indentGuidesDisposable.dispose();
if (this.renderer.disposeElement) {
this.renderer.disposeElement(node, index, templateData.templateData, height);
}
@ -279,16 +346,17 @@ class TreeRenderer<T, TFilterData, TTemplateData> implements IListRenderer<ITree
}
private onDidChangeNodeTwistieState(node: ITreeNode<T, TFilterData>): void {
const templateData = this.renderedNodes.get(node);
const data = this.renderedNodes.get(node);
if (!templateData) {
if (!data) {
return;
}
this.update(node, templateData);
this.renderTwistie(node, data.templateData);
this.renderIndentGuides(node, data.templateData);
}
private update(node: ITreeNode<T, TFilterData>, templateData: ITreeListTemplateData<TTemplateData>) {
private renderTwistie(node: ITreeNode<T, TFilterData>, templateData: ITreeListTemplateData<TTemplateData>) {
if (this.renderer.renderTwistie) {
this.renderer.renderTwistie(node.element, templateData.twistie);
}
@ -303,9 +371,72 @@ class TreeRenderer<T, TFilterData, TTemplateData> implements IListRenderer<ITree
}
}
private renderIndentGuides(target: ITreeNode<T, TFilterData>, templateData: ITreeListTemplateData<TTemplateData>): void {
clearNode(templateData.indent);
templateData.indentGuidesDisposable.dispose();
if (this._renderIndentGuides === RenderIndentGuides.None) {
return;
}
const disposableStore = new DisposableStore();
let node = target;
while (node.parent && node.parent.parent) {
const parent = node.parent;
const guide = $<HTMLDivElement>('.indent-guide', { style: `width: ${this.indent}px` });
if (this.activeParentNodes.has(parent)) {
addClass(guide, 'active');
}
if (templateData.indent.childElementCount === 0) {
templateData.indent.appendChild(guide);
} else {
templateData.indent.insertBefore(guide, templateData.indent.firstElementChild);
}
this.renderedIndentGuides.add(parent, guide);
disposableStore.add(toDisposable(() => this.renderedIndentGuides.delete(parent, guide)));
node = parent;
}
templateData.indentGuidesDisposable = disposableStore;
}
private _onDidChangeActiveNodes(nodes: ITreeNode<T, TFilterData>[]): void {
if (this._renderIndentGuides === RenderIndentGuides.None) {
return;
}
const set = new Set<ITreeNode<T, TFilterData>>();
nodes.forEach(node => {
if (node.parent) {
set.add(node.parent);
}
});
this.activeParentNodes.forEach(node => {
if (!set.has(node)) {
this.renderedIndentGuides.forEach(node, line => removeClass(line, 'active'));
}
});
set.forEach(node => {
if (!this.activeParentNodes.has(node)) {
this.renderedIndentGuides.forEach(node, line => addClass(line, 'active'));
}
});
this.activeParentNodes = set;
}
dispose(): void {
this.renderedNodes.clear();
this.renderedElements.clear();
this.indentGuidesDisposable.dispose();
this.disposables = dispose(this.disposables);
}
}
@ -820,6 +951,10 @@ class Trait<T> {
return [...this.elements];
}
getNodes(): readonly ITreeNode<T, any>[] {
return this.nodes;
}
has(node: ITreeNode<T, any>): boolean {
return this.nodeSet.has(node);
}
@ -1008,6 +1143,7 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
private eventBufferer = new EventBufferer();
private typeFilterController?: TypeFilterController<T, TFilterData>;
private focusNavigationFilter: ((node: ITreeNode<T, TFilterData>) => boolean) | undefined;
private styleElement: HTMLStyleElement;
protected disposables: IDisposable[] = [];
get onDidScroll(): Event<ScrollEvent> { return this.view.onDidScroll; }
@ -1036,7 +1172,6 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
get filterOnType(): boolean { return !!this._options.filterOnType; }
get onDidChangeTypeFilterPattern(): Event<string> { return this.typeFilterController ? this.typeFilterController.onDidChangePattern : Event.None; }
// Options TODO@joao expose options only, not Optional<>
get openOnSingleClick(): boolean { return typeof this._options.openOnSingleClick === 'undefined' ? true : this._options.openOnSingleClick; }
get expandOnlyOnTwistieClick(): boolean | ((e: T) => boolean) { return typeof this._options.expandOnlyOnTwistieClick === 'undefined' ? false : this._options.expandOnlyOnTwistieClick; }
@ -1054,7 +1189,11 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
const treeDelegate = new ComposedTreeDelegate<T, ITreeNode<T, TFilterData>>(delegate);
const onDidChangeCollapseStateRelay = new Relay<ICollapseStateChangeEvent<T, TFilterData>>();
this.renderers = renderers.map(r => new TreeRenderer<T, TFilterData, any>(r, onDidChangeCollapseStateRelay.event, _options));
const onDidChangeActiveNodes = new Relay<ITreeNode<T, TFilterData>[]>();
const activeNodes = new EventCollection(onDidChangeActiveNodes.event);
this.disposables.push(activeNodes);
this.renderers = renderers.map(r => new TreeRenderer<T, TFilterData, any>(r, onDidChangeCollapseStateRelay.event, activeNodes, _options));
this.disposables.push(...this.renderers);
let filter: TypeFilter<T> | undefined;
@ -1077,6 +1216,8 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
this.selection.onDidModelSplice(e);
}, null, this.disposables);
onDidChangeActiveNodes.input = Event.map(Event.any<any>(this.focus.onDidChange, this.selection.onDidChange, this.model.onDidSplice), () => [...this.focus.getNodes(), ...this.selection.getNodes()]);
if (_options.keyboardSupport !== false) {
const onKeyDown = Event.chain(this.view.onKeyDown)
.filter(e => !isInputElement(e.target as HTMLElement))
@ -1092,6 +1233,9 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
this.focusNavigationFilter = node => this.typeFilterController!.shouldAllowFocus(node);
this.disposables.push(this.typeFilterController!);
}
this.styleElement = createStyleSheet(this.view.getHTMLElement());
toggleClass(this.getHTMLElement(), 'always', this._options.renderIndentGuides === RenderIndentGuides.Always);
}
updateOptions(optionsUpdate: IAbstractTreeOptionsUpdate = {}): void {
@ -1111,6 +1255,8 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
}
this._onDidUpdateOptions.fire(this._options);
toggleClass(this.getHTMLElement(), 'always', this._options.renderIndentGuides === RenderIndentGuides.Always);
}
get options(): IAbstractTreeOptions<T, TFilterData> {
@ -1192,6 +1338,19 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
}
style(styles: IListStyles): void {
const suffix = `.${this.view.domId}`;
const content: string[] = [];
if (styles.treeIndentGuidesStroke) {
content.push(`.monaco-list${suffix}:hover .monaco-tl-indent > .indent-guide, .monaco-list${suffix}.always .monaco-tl-indent > .indent-guide { border-color: ${styles.treeIndentGuidesStroke.transparent(0.4)}; }`);
content.push(`.monaco-list${suffix} .monaco-tl-indent > .indent-guide.active { border-color: ${styles.treeIndentGuidesStroke}; }`);
}
const newStyles = content.join('\n');
if (newStyles !== this.styleElement.innerHTML) {
this.styleElement.innerHTML = newStyles;
}
this.view.style(styles);
}

View file

@ -225,6 +225,7 @@ function asObjectTreeOptions<TInput, T, TFilterData>(options?: IAsyncDataTreeOpt
}
},
keyboardNavigationLabelProvider: options.keyboardNavigationLabelProvider && {
...options.keyboardNavigationLabelProvider,
getKeyboardNavigationLabel(e) {
return options.keyboardNavigationLabelProvider!.getKeyboardNavigationLabel(e.element as T);
}

View file

@ -18,6 +18,7 @@ export interface IDataTreeViewState {
readonly focus: string[];
readonly selection: string[];
readonly expanded: string[];
readonly scrollTop: number;
}
export class DataTree<TInput, T, TFilterData = void> extends AbstractTree<T | null, TFilterData, T | null> {
@ -80,6 +81,10 @@ export class DataTree<TInput, T, TFilterData = void> extends AbstractTree<T | nu
this._refresh(input, isCollapsed, onDidCreateNode);
this.setFocus(focus);
this.setSelection(selection);
if (viewState && typeof viewState.scrollTop === 'number') {
this.scrollTop = viewState.scrollTop;
}
}
updateChildren(element: TInput | T = this.input!): void {
@ -193,6 +198,6 @@ export class DataTree<TInput, T, TFilterData = void> extends AbstractTree<T | nu
queue.push(...node.children);
}
return { focus, selection, expanded };
return { focus, selection, expanded, scrollTop: this.scrollTop };
}
}

View file

@ -7,6 +7,30 @@
display: flex;
height: 100%;
align-items: center;
position: relative;
}
.monaco-tl-indent {
height: 100%;
position: absolute;
top: 0;
left: 18px;
pointer-events: none;
}
.hide-arrows .monaco-tl-indent {
left: 12px;
}
.monaco-tl-indent > .indent-guide {
display: inline-block;
box-sizing: border-box;
height: 100%;
border-left: 1px solid transparent;
}
.monaco-tl-indent > .indent-guide {
transition: border-color 0.1s linear;
}
.monaco-tl-twistie,
@ -66,4 +90,4 @@
.hc-black .monaco-tl-twistie.loading {
background-image: url("loading-hc.svg");
}
}

View file

@ -78,7 +78,10 @@ export class VSBuffer {
}
slice(start?: number, end?: number): VSBuffer {
return new VSBuffer(this.buffer.slice(start, end));
// IMPORTANT: use subarray instead of slice because TypedArray#slice
// creates shallow copy and NodeBuffer#slice doesn't. The use of subarray
// ensures the same, performant, behaviour.
return new VSBuffer(this.buffer.subarray(start!/*bad lib.d.ts*/, end));
}
set(array: VSBuffer, offset?: number): void {
@ -437,4 +440,4 @@ class VSBufferWriteableStreamImpl implements VSBufferWriteableStream {
this.listeners.end.length = 0;
}
}
}
}

View file

@ -96,4 +96,44 @@ export function fromMap<T>(original: Map<string, T>): IStringDictionary<T> {
});
}
return result;
}
export class SetMap<K, V> {
private map = new Map<K, Set<V>>();
add(key: K, value: V): void {
let values = this.map.get(key);
if (!values) {
values = new Set<V>();
this.map.set(key, values);
}
values.add(value);
}
delete(key: K, value: V): void {
const values = this.map.get(key);
if (!values) {
return;
}
values.delete(value);
if (values.size === 0) {
this.map.delete(key);
}
}
forEach(key: K, fn: (value: V) => void): void {
const values = this.map.get(key);
if (!values) {
return;
}
values.forEach(fn);
}
}

View file

@ -7,28 +7,26 @@ import * as strings from 'vs/base/common/strings';
import { sep } from 'vs/base/common/path';
import { IdleValue } from 'vs/base/common/async';
let intlFileNameCollator: IdleValue<{ collator: Intl.Collator, collatorIsNumeric: boolean }>;
export function setFileNameComparer(collator: IdleValue<{ collator: Intl.Collator, collatorIsNumeric: boolean }>): void {
intlFileNameCollator = collator;
}
const intlFileNameCollator: IdleValue<{ collator: Intl.Collator, collatorIsNumeric: boolean }> = new IdleValue(() => {
const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' });
return {
collator: collator,
collatorIsNumeric: collator.resolvedOptions().numeric
};
});
export function compareFileNames(one: string | null, other: string | null, caseSensitive = false): number {
if (intlFileNameCollator) {
const a = one || '';
const b = other || '';
const result = intlFileNameCollator.getValue().collator.compare(a, b);
const a = one || '';
const b = other || '';
const result = intlFileNameCollator.getValue().collator.compare(a, b);
// Using the numeric option in the collator will
// make compare(`foo1`, `foo01`) === 0. We must disambiguate.
if (intlFileNameCollator.getValue().collatorIsNumeric && result === 0 && a !== b) {
return a < b ? -1 : 1;
}
return result;
// Using the numeric option in the collator will
// make compare(`foo1`, `foo01`) === 0. We must disambiguate.
if (intlFileNameCollator.getValue().collatorIsNumeric && result === 0 && a !== b) {
return a < b ? -1 : 1;
}
return noIntlCompareFileNames(one, other, caseSensitive);
return result;
}
const FileNameMatch = /^(.*?)(\.([^.]*))?$/;
@ -54,46 +52,27 @@ export function noIntlCompareFileNames(one: string | null, other: string | null,
}
export function compareFileExtensions(one: string | null, other: string | null): number {
if (intlFileNameCollator) {
const [oneName, oneExtension] = extractNameAndExtension(one);
const [otherName, otherExtension] = extractNameAndExtension(other);
const [oneName, oneExtension] = extractNameAndExtension(one);
const [otherName, otherExtension] = extractNameAndExtension(other);
let result = intlFileNameCollator.getValue().collator.compare(oneExtension, otherExtension);
let result = intlFileNameCollator.getValue().collator.compare(oneExtension, otherExtension);
if (result === 0) {
// Using the numeric option in the collator will
// make compare(`foo1`, `foo01`) === 0. We must disambiguate.
if (intlFileNameCollator.getValue().collatorIsNumeric && oneExtension !== otherExtension) {
return oneExtension < otherExtension ? -1 : 1;
}
// Extensions are equal, compare filenames
result = intlFileNameCollator.getValue().collator.compare(oneName, otherName);
if (intlFileNameCollator.getValue().collatorIsNumeric && result === 0 && oneName !== otherName) {
return oneName < otherName ? -1 : 1;
}
if (result === 0) {
// Using the numeric option in the collator will
// make compare(`foo1`, `foo01`) === 0. We must disambiguate.
if (intlFileNameCollator.getValue().collatorIsNumeric && oneExtension !== otherExtension) {
return oneExtension < otherExtension ? -1 : 1;
}
return result;
// Extensions are equal, compare filenames
result = intlFileNameCollator.getValue().collator.compare(oneName, otherName);
if (intlFileNameCollator.getValue().collatorIsNumeric && result === 0 && oneName !== otherName) {
return oneName < otherName ? -1 : 1;
}
}
return noIntlCompareFileExtensions(one, other);
}
function noIntlCompareFileExtensions(one: string | null, other: string | null): number {
const [oneName, oneExtension] = extractNameAndExtension(one && one.toLowerCase());
const [otherName, otherExtension] = extractNameAndExtension(other && other.toLowerCase());
if (oneExtension !== otherExtension) {
return oneExtension < otherExtension ? -1 : 1;
}
if (oneName === otherName) {
return 0;
}
return oneName < otherName ? -1 : 1;
return result;
}
function extractNameAndExtension(str?: string | null): [string, string] {

View file

@ -3,23 +3,13 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { compareFileNames, compareFileExtensions, setFileNameComparer } from 'vs/base/common/comparers';
import { compareFileNames, compareFileExtensions } from 'vs/base/common/comparers';
import * as assert from 'assert';
import { IdleValue } from 'vs/base/common/async';
suite('Comparers', () => {
test('compareFileNames', () => {
// Setup Intl
setFileNameComparer(new IdleValue(() => {
const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' });
return {
collator: collator,
collatorIsNumeric: collator.resolvedOptions().numeric
};
}));
assert(compareFileNames(null, null) === 0, 'null should be equal');
assert(compareFileNames(null, 'abc') < 0, 'null should be come before real values');
assert(compareFileNames('', '') === 0, 'empty should be equal');
@ -35,15 +25,6 @@ suite('Comparers', () => {
test('compareFileExtensions', () => {
// Setup Intl
setFileNameComparer(new IdleValue(() => {
const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' });
return {
collator: collator,
collatorIsNumeric: collator.resolvedOptions().numeric
};
}));
assert(compareFileExtensions(null, null) === 0, 'null should be equal');
assert(compareFileExtensions(null, '.abc') < 0, 'null should come before real files');
assert(compareFileExtensions(null, 'abc') < 0, 'null should come before real files without extension');
@ -66,4 +47,4 @@ suite('Comparers', () => {
assert(compareFileExtensions('file2.ext2', 'file1.ext10') < 0, 'extensions with numbers should be in numerical order, not alphabetical order');
assert(compareFileExtensions('file.ext01', 'file.ext1') < 0, 'extensions with equal numbers should be in alphabetical order');
});
});
});

View file

@ -364,4 +364,42 @@ suite('Buffer', () => {
assert.equal(ended, false);
assert.equal(errors.length, 0);
});
test('Performance issue with VSBuffer#slice #76076', function () {
// Buffer#slice creates a view
{
const buff = Buffer.from([10, 20, 30, 40]);
const b2 = buff.slice(1, 3);
assert.equal(buff[1], 20);
assert.equal(b2[0], 20);
buff[1] = 17; // modify buff AND b2
assert.equal(buff[1], 17);
assert.equal(b2[0], 17);
}
// TypedArray#slice creates a copy
{
const unit = new Uint8Array([10, 20, 30, 40]);
const u2 = unit.slice(1, 3);
assert.equal(unit[1], 20);
assert.equal(u2[0], 20);
unit[1] = 17; // modify unit, NOT b2
assert.equal(unit[1], 17);
assert.equal(u2[0], 20);
}
// TypedArray#subarray creates a view
{
const unit = new Uint8Array([10, 20, 30, 40]);
const u2 = unit.subarray(1, 3);
assert.equal(unit[1], 20);
assert.equal(u2[0], 20);
unit[1] = 17; // modify unit AND b2
assert.equal(unit[1], 17);
assert.equal(u2[0], 17);
}
});
});

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