Merge branch 'main' into async-tokenization-2

This commit is contained in:
Henning Dieterichs 2023-02-17 17:08:17 +01:00
commit a42ec7cdd2
No known key found for this signature in database
GPG key ID: 771381EFFDB9EC06
230 changed files with 4361 additions and 2339 deletions

View file

@ -227,6 +227,7 @@
"@vscode/vscode-languagedetection",
"@vscode/ripgrep",
"@vscode/iconv-lite-umd",
"@vscode/policy-watcher",
"assert",
"child_process",
"console",
@ -254,7 +255,6 @@
"url",
"util",
"v8-inspect-profiler",
"vscode-policy-watcher",
"vscode-proxy-agent",
"vscode-regexpp",
"vscode-textmate",

View file

@ -7,7 +7,7 @@
{
"kind": 2,
"language": "github-issues",
"value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels\n\n$MILESTONE=milestone:\"January 2023\""
"value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels\n\n$MILESTONE=milestone:\"February 2023\""
},
{
"kind": 1,

View file

@ -7,7 +7,7 @@
{
"kind": 2,
"language": "github-issues",
"value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels\n\n$MILESTONE=milestone:\"January 2023\"\n\n$MINE=assignee:@me"
"value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels\n\n$MILESTONE=milestone:\"February 2023\"\n\n$MINE=assignee:@me"
},
{
"kind": 1,

View file

@ -12,7 +12,7 @@
{
"kind": 2,
"language": "github-issues",
"value": "$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels\n$milestone=milestone:\"November 2022\""
"value": "$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels\n$milestone=milestone:\"February 2023\""
},
{
"kind": 1,

View file

@ -113,13 +113,13 @@ vscode-encrypt/binding.gyp
vscode-encrypt/README.md
!vscode-encrypt/build/Release/vscode-encrypt-native.node
vscode-policy-watcher/build/**
vscode-policy-watcher/.husky/**
vscode-policy-watcher/src/**
vscode-policy-watcher/binding.gyp
vscode-policy-watcher/README.md
vscode-policy-watcher/index.d.ts
!vscode-policy-watcher/build/Release/vscode-policy-watcher.node
@vscode/policy-watcher/build/**
@vscode/policy-watcher/.husky/**
@vscode/policy-watcher/src/**
@vscode/policy-watcher/binding.gyp
@vscode/policy-watcher/README.md
@vscode/policy-watcher/index.d.ts
!@vscode/policy-watcher/build/Release/vscode-policy-watcher.node
vscode-windows-ca-certs/**/*
!vscode-windows-ca-certs/package.json

View file

@ -177,4 +177,5 @@ steps:
displayName: "Component Detection"
inputs:
sourceScanPath: $(Build.SourcesDirectory)
alertWarningLevel: 'Medium'
continueOnError: true

View file

@ -9,51 +9,109 @@ const Vinyl = require("vinyl");
const vfs = require("vinyl-fs");
const filter = require("gulp-filter");
const gzip = require("gulp-gzip");
const mime = require("mime");
const identity_1 = require("@azure/identity");
const azure = require('gulp-azure-storage');
const commit = process.env['VSCODE_DISTRO_COMMIT'] || process.env['BUILD_SOURCEVERSION'];
const credential = new identity_1.ClientSecretCredential(process.env['AZURE_TENANT_ID'], process.env['AZURE_CLIENT_ID'], process.env['AZURE_CLIENT_SECRET']);
mime.define({
'application/typescript': ['ts'],
'application/json': ['code-snippets'],
});
// From default AFD configuration
const MimeTypesToCompress = new Set([
'application/eot',
'application/font',
'application/font-sfnt',
'application/javascript',
'application/json',
'application/opentype',
'application/otf',
'application/pkcs7-mime',
'application/truetype',
'application/ttf',
'application/typescript',
'application/vnd.ms-fontobject',
'application/xhtml+xml',
'application/xml',
'application/xml+rss',
'application/x-font-opentype',
'application/x-font-truetype',
'application/x-font-ttf',
'application/x-httpd-cgi',
'application/x-javascript',
'application/x-mpegurl',
'application/x-opentype',
'application/x-otf',
'application/x-perl',
'application/x-ttf',
'font/eot',
'font/ttf',
'font/otf',
'font/opentype',
'image/svg+xml',
'text/css',
'text/csv',
'text/html',
'text/javascript',
'text/js',
'text/markdown',
'text/plain',
'text/richtext',
'text/tab-separated-values',
'text/xml',
'text/x-script',
'text/x-component',
'text/x-java-source'
]);
function wait(stream) {
return new Promise((c, e) => {
stream.on('end', () => c());
stream.on('error', (err) => e(err));
});
}
async function main() {
const files = [];
const options = {
const options = (compressed) => ({
account: process.env.AZURE_STORAGE_ACCOUNT,
credential,
container: process.env.VSCODE_QUALITY,
prefix: commit + '/',
contentSettings: {
contentEncoding: 'gzip',
contentEncoding: compressed ? 'gzip' : undefined,
cacheControl: 'max-age=31536000, public'
}
};
await new Promise((c, e) => {
vfs.src('**', { cwd: '../vscode-web', base: '../vscode-web', dot: true })
.pipe(filter(f => !f.isDirectory()))
.pipe(gzip({ append: false }))
.pipe(es.through(function (data) {
console.log('Uploading:', data.relative); // debug
files.push(data.relative);
this.emit('data', data);
}))
.pipe(azure.upload(options))
.on('end', () => c())
.on('error', (err) => e(err));
});
await new Promise((c, e) => {
const listing = new Vinyl({
path: 'files.txt',
contents: Buffer.from(files.join('\n')),
stat: { mode: 0o666 }
});
console.log(`Uploading: files.txt (${files.length} files)`); // debug
es.readArray([listing])
.pipe(gzip({ append: false }))
.pipe(azure.upload(options))
.on('end', () => c())
.on('error', (err) => e(err));
const all = vfs.src('**', { cwd: '../vscode-web', base: '../vscode-web', dot: true })
.pipe(filter(f => !f.isDirectory()));
const compressed = all
.pipe(filter(f => MimeTypesToCompress.has(mime.lookup(f.path))))
.pipe(gzip({ append: false }))
.pipe(azure.upload(options(true)));
const uncompressed = all
.pipe(filter(f => !MimeTypesToCompress.has(mime.lookup(f.path))))
.pipe(azure.upload(options(false)));
const out = es.merge(compressed, uncompressed)
.pipe(es.through(function (f) {
console.log('Uploaded:', f.relative);
files.push(f.relative);
this.emit('data', f);
}));
console.log(`Uploading files to CDN...`); // debug
await wait(out);
const listing = new Vinyl({
path: 'files.txt',
contents: Buffer.from(files.join('\n')),
stat: { mode: 0o666 }
});
const filesOut = es.readArray([listing])
.pipe(gzip({ append: false }))
.pipe(azure.upload(options(true)));
console.log(`Uploading: files.txt (${files.length} files)`); // debug
await wait(filesOut);
}
main().catch(err => {
console.error(err);
process.exit(1);
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXBsb2FkLWNkbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInVwbG9hZC1jZG4udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Z0dBR2dHOztBQUVoRyxtQ0FBbUM7QUFDbkMsK0JBQStCO0FBQy9CLGdDQUFnQztBQUNoQyxzQ0FBc0M7QUFDdEMsa0NBQWtDO0FBQ2xDLDhDQUF5RDtBQUN6RCxNQUFNLEtBQUssR0FBRyxPQUFPLENBQUMsb0JBQW9CLENBQUMsQ0FBQztBQUU1QyxNQUFNLE1BQU0sR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLHNCQUFzQixDQUFDLElBQUksT0FBTyxDQUFDLEdBQUcsQ0FBQyxxQkFBcUIsQ0FBQyxDQUFDO0FBQ3pGLE1BQU0sVUFBVSxHQUFHLElBQUksaUNBQXNCLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxpQkFBaUIsQ0FBRSxFQUFFLE9BQU8sQ0FBQyxHQUFHLENBQUMsaUJBQWlCLENBQUUsRUFBRSxPQUFPLENBQUMsR0FBRyxDQUFDLHFCQUFxQixDQUFFLENBQUMsQ0FBQztBQUVySixLQUFLLFVBQVUsSUFBSTtJQUNsQixNQUFNLEtBQUssR0FBYSxFQUFFLENBQUM7SUFDM0IsTUFBTSxPQUFPLEdBQUc7UUFDZixPQUFPLEVBQUUsT0FBTyxDQUFDLEdBQUcsQ0FBQyxxQkFBcUI7UUFDMUMsVUFBVTtRQUNWLFNBQVMsRUFBRSxPQUFPLENBQUMsR0FBRyxDQUFDLGNBQWM7UUFDckMsTUFBTSxFQUFFLE1BQU0sR0FBRyxHQUFHO1FBQ3BCLGVBQWUsRUFBRTtZQUNoQixlQUFlLEVBQUUsTUFBTTtZQUN2QixZQUFZLEVBQUUsMEJBQTBCO1NBQ3hDO0tBQ0QsQ0FBQztJQUVGLE1BQU0sSUFBSSxPQUFPLENBQU8sQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUU7UUFDaEMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxJQUFJLEVBQUUsRUFBRSxHQUFHLEVBQUUsZUFBZSxFQUFFLElBQUksRUFBRSxlQUFlLEVBQUUsR0FBRyxFQUFFLElBQUksRUFBRSxDQUFDO2FBQ3ZFLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxXQUFXLEVBQUUsQ0FBQyxDQUFDO2FBQ25DLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQzthQUM3QixJQUFJLENBQUMsRUFBRSxDQUFDLE9BQU8sQ0FBQyxVQUFVLElBQVc7WUFDckMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxZQUFZLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsUUFBUTtZQUNsRCxLQUFLLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQztZQUMxQixJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMsQ0FBQztRQUN6QixDQUFDLENBQUMsQ0FBQzthQUNGLElBQUksQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDO2FBQzNCLEVBQUUsQ0FBQyxLQUFLLEVBQUUsR0FBRyxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUM7YUFDcEIsRUFBRSxDQUFDLE9BQU8sRUFBRSxDQUFDLEdBQVEsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFDckMsQ0FBQyxDQUFDLENBQUM7SUFFSCxNQUFNLElBQUksT0FBTyxDQUFPLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFO1FBQ2hDLE1BQU0sT0FBTyxHQUFHLElBQUksS0FBSyxDQUFDO1lBQ3pCLElBQUksRUFBRSxXQUFXO1lBQ2pCLFFBQVEsRUFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDdkMsSUFBSSxFQUFFLEVBQUUsSUFBSSxFQUFFLEtBQUssRUFBUztTQUM1QixDQUFDLENBQUM7UUFFSCxPQUFPLENBQUMsR0FBRyxDQUFDLHlCQUF5QixLQUFLLENBQUMsTUFBTSxTQUFTLENBQUMsQ0FBQyxDQUFDLFFBQVE7UUFDckUsRUFBRSxDQUFDLFNBQVMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDO2FBQ3JCLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQzthQUM3QixJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsQ0FBQzthQUMzQixFQUFFLENBQUMsS0FBSyxFQUFFLEdBQUcsRUFBRSxDQUFDLENBQUMsRUFBRSxDQUFDO2FBQ3BCLEVBQUUsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxHQUFRLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO0lBQ3JDLENBQUMsQ0FBQyxDQUFDO0FBQ0osQ0FBQztBQUVELElBQUksRUFBRSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsRUFBRTtJQUNsQixPQUFPLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQ25CLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDakIsQ0FBQyxDQUFDLENBQUMifQ==
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXBsb2FkLWNkbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInVwbG9hZC1jZG4udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Z0dBR2dHOztBQUVoRyxtQ0FBbUM7QUFDbkMsK0JBQStCO0FBQy9CLGdDQUFnQztBQUNoQyxzQ0FBc0M7QUFDdEMsa0NBQWtDO0FBQ2xDLDZCQUE2QjtBQUM3Qiw4Q0FBeUQ7QUFDekQsTUFBTSxLQUFLLEdBQUcsT0FBTyxDQUFDLG9CQUFvQixDQUFDLENBQUM7QUFFNUMsTUFBTSxNQUFNLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxzQkFBc0IsQ0FBQyxJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMscUJBQXFCLENBQUMsQ0FBQztBQUN6RixNQUFNLFVBQVUsR0FBRyxJQUFJLGlDQUFzQixDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsaUJBQWlCLENBQUUsRUFBRSxPQUFPLENBQUMsR0FBRyxDQUFDLGlCQUFpQixDQUFFLEVBQUUsT0FBTyxDQUFDLEdBQUcsQ0FBQyxxQkFBcUIsQ0FBRSxDQUFDLENBQUM7QUFFckosSUFBSSxDQUFDLE1BQU0sQ0FBQztJQUNYLHdCQUF3QixFQUFFLENBQUMsSUFBSSxDQUFDO0lBQ2hDLGtCQUFrQixFQUFFLENBQUMsZUFBZSxDQUFDO0NBQ3JDLENBQUMsQ0FBQztBQUVILGlDQUFpQztBQUNqQyxNQUFNLG1CQUFtQixHQUFHLElBQUksR0FBRyxDQUFDO0lBQ25DLGlCQUFpQjtJQUNqQixrQkFBa0I7SUFDbEIsdUJBQXVCO0lBQ3ZCLHdCQUF3QjtJQUN4QixrQkFBa0I7SUFDbEIsc0JBQXNCO0lBQ3RCLGlCQUFpQjtJQUNqQix3QkFBd0I7SUFDeEIsc0JBQXNCO0lBQ3RCLGlCQUFpQjtJQUNqQix3QkFBd0I7SUFDeEIsK0JBQStCO0lBQy9CLHVCQUF1QjtJQUN2QixpQkFBaUI7SUFDakIscUJBQXFCO0lBQ3JCLDZCQUE2QjtJQUM3Qiw2QkFBNkI7SUFDN0Isd0JBQXdCO0lBQ3hCLHlCQUF5QjtJQUN6QiwwQkFBMEI7SUFDMUIsdUJBQXVCO0lBQ3ZCLHdCQUF3QjtJQUN4QixtQkFBbUI7SUFDbkIsb0JBQW9CO0lBQ3BCLG1CQUFtQjtJQUNuQixVQUFVO0lBQ1YsVUFBVTtJQUNWLFVBQVU7SUFDVixlQUFlO0lBQ2YsZUFBZTtJQUNmLFVBQVU7SUFDVixVQUFVO0lBQ1YsV0FBVztJQUNYLGlCQUFpQjtJQUNqQixTQUFTO0lBQ1QsZUFBZTtJQUNmLFlBQVk7SUFDWixlQUFlO0lBQ2YsMkJBQTJCO0lBQzNCLFVBQVU7SUFDVixlQUFlO0lBQ2Ysa0JBQWtCO0lBQ2xCLG9CQUFvQjtDQUNwQixDQUFDLENBQUM7QUFFSCxTQUFTLElBQUksQ0FBQyxNQUF3QjtJQUNyQyxPQUFPLElBQUksT0FBTyxDQUFPLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFO1FBQ2pDLE1BQU0sQ0FBQyxFQUFFLENBQUMsS0FBSyxFQUFFLEdBQUcsRUFBRSxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDNUIsTUFBTSxDQUFDLEVBQUUsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxHQUFRLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO0lBQzFDLENBQUMsQ0FBQyxDQUFDO0FBQ0osQ0FBQztBQUVELEtBQUssVUFBVSxJQUFJO0lBQ2xCLE1BQU0sS0FBSyxHQUFhLEVBQUUsQ0FBQztJQUMzQixNQUFNLE9BQU8sR0FBRyxDQUFDLFVBQW1CLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFDekMsT0FBTyxFQUFFLE9BQU8sQ0FBQyxHQUFHLENBQUMscUJBQXFCO1FBQzFDLFVBQVU7UUFDVixTQUFTLEVBQUUsT0FBTyxDQUFDLEdBQUcsQ0FBQyxjQUFjO1FBQ3JDLE1BQU0sRUFBRSxNQUFNLEdBQUcsR0FBRztRQUNwQixlQUFlLEVBQUU7WUFDaEIsZUFBZSxFQUFFLFVBQVUsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxTQUFTO1lBQ2hELFlBQVksRUFBRSwwQkFBMEI7U0FDeEM7S0FDRCxDQUFDLENBQUM7SUFFSCxNQUFNLEdBQUcsR0FBRyxHQUFHLENBQUMsR0FBRyxDQUFDLElBQUksRUFBRSxFQUFFLEdBQUcsRUFBRSxlQUFlLEVBQUUsSUFBSSxFQUFFLGVBQWUsRUFBRSxHQUFHLEVBQUUsSUFBSSxFQUFFLENBQUM7U0FDbkYsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLFdBQVcsRUFBRSxDQUFDLENBQUMsQ0FBQztJQUV0QyxNQUFNLFVBQVUsR0FBRyxHQUFHO1NBQ3BCLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxtQkFBbUIsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO1NBQy9ELElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQztTQUM3QixJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBRXBDLE1BQU0sWUFBWSxHQUFHLEdBQUc7U0FDdEIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsbUJBQW1CLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztTQUNoRSxJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBRXJDLE1BQU0sR0FBRyxHQUFHLEVBQUUsQ0FBQyxLQUFLLENBQUMsVUFBVSxFQUFFLFlBQVksQ0FBQztTQUM1QyxJQUFJLENBQUMsRUFBRSxDQUFDLE9BQU8sQ0FBQyxVQUFVLENBQUM7UUFDM0IsT0FBTyxDQUFDLEdBQUcsQ0FBQyxXQUFXLEVBQUUsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQ3JDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQ3RCLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFFTCxPQUFPLENBQUMsR0FBRyxDQUFDLDJCQUEyQixDQUFDLENBQUMsQ0FBQyxRQUFRO0lBQ2xELE1BQU0sSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBRWhCLE1BQU0sT0FBTyxHQUFHLElBQUksS0FBSyxDQUFDO1FBQ3pCLElBQUksRUFBRSxXQUFXO1FBQ2pCLFFBQVEsRUFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDdkMsSUFBSSxFQUFFLEVBQUUsSUFBSSxFQUFFLEtBQUssRUFBUztLQUM1QixDQUFDLENBQUM7SUFFSCxNQUFNLFFBQVEsR0FBRyxFQUFFLENBQUMsU0FBUyxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUM7U0FDdEMsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQUUsQ0FBQyxDQUFDO1NBQzdCLElBQUksQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFFcEMsT0FBTyxDQUFDLEdBQUcsQ0FBQyx5QkFBeUIsS0FBSyxDQUFDLE1BQU0sU0FBUyxDQUFDLENBQUMsQ0FBQyxRQUFRO0lBQ3JFLE1BQU0sSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO0FBQ3RCLENBQUM7QUFFRCxJQUFJLEVBQUUsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLEVBQUU7SUFDbEIsT0FBTyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUNuQixPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQ2pCLENBQUMsQ0FBQyxDQUFDIn0=

View file

@ -8,53 +8,119 @@ import * as Vinyl from 'vinyl';
import * as vfs from 'vinyl-fs';
import * as filter from 'gulp-filter';
import * as gzip from 'gulp-gzip';
import * as mime from 'mime';
import { ClientSecretCredential } from '@azure/identity';
const azure = require('gulp-azure-storage');
const commit = process.env['VSCODE_DISTRO_COMMIT'] || process.env['BUILD_SOURCEVERSION'];
const credential = new ClientSecretCredential(process.env['AZURE_TENANT_ID']!, process.env['AZURE_CLIENT_ID']!, process.env['AZURE_CLIENT_SECRET']!);
mime.define({
'application/typescript': ['ts'],
'application/json': ['code-snippets'],
});
// From default AFD configuration
const MimeTypesToCompress = new Set([
'application/eot',
'application/font',
'application/font-sfnt',
'application/javascript',
'application/json',
'application/opentype',
'application/otf',
'application/pkcs7-mime',
'application/truetype',
'application/ttf',
'application/typescript',
'application/vnd.ms-fontobject',
'application/xhtml+xml',
'application/xml',
'application/xml+rss',
'application/x-font-opentype',
'application/x-font-truetype',
'application/x-font-ttf',
'application/x-httpd-cgi',
'application/x-javascript',
'application/x-mpegurl',
'application/x-opentype',
'application/x-otf',
'application/x-perl',
'application/x-ttf',
'font/eot',
'font/ttf',
'font/otf',
'font/opentype',
'image/svg+xml',
'text/css',
'text/csv',
'text/html',
'text/javascript',
'text/js',
'text/markdown',
'text/plain',
'text/richtext',
'text/tab-separated-values',
'text/xml',
'text/x-script',
'text/x-component',
'text/x-java-source'
]);
function wait(stream: es.ThroughStream): Promise<void> {
return new Promise<void>((c, e) => {
stream.on('end', () => c());
stream.on('error', (err: any) => e(err));
});
}
async function main(): Promise<void> {
const files: string[] = [];
const options = {
const options = (compressed: boolean) => ({
account: process.env.AZURE_STORAGE_ACCOUNT,
credential,
container: process.env.VSCODE_QUALITY,
prefix: commit + '/',
contentSettings: {
contentEncoding: 'gzip',
contentEncoding: compressed ? 'gzip' : undefined,
cacheControl: 'max-age=31536000, public'
}
};
await new Promise<void>((c, e) => {
vfs.src('**', { cwd: '../vscode-web', base: '../vscode-web', dot: true })
.pipe(filter(f => !f.isDirectory()))
.pipe(gzip({ append: false }))
.pipe(es.through(function (data: Vinyl) {
console.log('Uploading:', data.relative); // debug
files.push(data.relative);
this.emit('data', data);
}))
.pipe(azure.upload(options))
.on('end', () => c())
.on('error', (err: any) => e(err));
});
await new Promise<void>((c, e) => {
const listing = new Vinyl({
path: 'files.txt',
contents: Buffer.from(files.join('\n')),
stat: { mode: 0o666 } as any
});
const all = vfs.src('**', { cwd: '../vscode-web', base: '../vscode-web', dot: true })
.pipe(filter(f => !f.isDirectory()));
console.log(`Uploading: files.txt (${files.length} files)`); // debug
es.readArray([listing])
.pipe(gzip({ append: false }))
.pipe(azure.upload(options))
.on('end', () => c())
.on('error', (err: any) => e(err));
const compressed = all
.pipe(filter(f => MimeTypesToCompress.has(mime.lookup(f.path))))
.pipe(gzip({ append: false }))
.pipe(azure.upload(options(true)));
const uncompressed = all
.pipe(filter(f => !MimeTypesToCompress.has(mime.lookup(f.path))))
.pipe(azure.upload(options(false)));
const out = es.merge(compressed, uncompressed)
.pipe(es.through(function (f) {
console.log('Uploaded:', f.relative);
files.push(f.relative);
this.emit('data', f);
}));
console.log(`Uploading files to CDN...`); // debug
await wait(out);
const listing = new Vinyl({
path: 'files.txt',
contents: Buffer.from(files.join('\n')),
stat: { mode: 0o666 } as any
});
const filesOut = es.readArray([listing])
.pipe(gzip({ append: false }))
.pipe(azure.upload(options(true)));
console.log(`Uploading: files.txt (${files.length} files)`); // debug
await wait(filesOut);
}
main().catch(err => {

View file

@ -126,7 +126,7 @@ const optimizeVSCodeTask = task.define('optimize-vscode', task.series(
{ src: [...windowBootstrapFiles, 'out-build/vs/code/electron-sandbox/workbench/workbench.js'], out: 'vs/code/electron-sandbox/workbench/workbench.js' },
{ src: [...windowBootstrapFiles, 'out-build/vs/code/electron-sandbox/issue/issueReporter.js'], out: 'vs/code/electron-sandbox/issue/issueReporter.js' },
{ src: [...windowBootstrapFiles, 'out-build/vs/code/electron-sandbox/processExplorer/processExplorer.js'], out: 'vs/code/electron-sandbox/processExplorer/processExplorer.js' },
{ src: [...windowBootstrapFiles, 'out-build/vs/code/electron-browser/sharedProcess/sharedProcess.js'], out: 'vs/code/electron-browser/sharedProcess/sharedProcess.js' }
{ src: [...windowBootstrapFiles, 'out-build/vs/code/node/sharedProcess/sharedProcess.js'], out: 'vs/code/node/sharedProcess/sharedProcess.js' }
]
}
)

View file

@ -493,6 +493,10 @@
{
"name": "vs/workbench/services/userDataProfile",
"project": "vscode-profiles"
},
{
"name": "vs/workbench/services/localization",
"project": "vscode-workbench"
}
]
}

View file

@ -6,6 +6,5 @@
"devDependencies": {
"node-gyp": "^8.4.1"
},
"scripts": {
}
"scripts": {}
}

View file

@ -3,14 +3,14 @@
"@gar/promisify@^1.0.1":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.2.tgz#30aa825f11d438671d585bd44e7fd564535fc210"
integrity sha512-82cpyJyKRoQoRi+14ibCeGPu0CwypgtBAdBhq1WfvagpCZNKqwXbKwXllYSMG91DhmG4jt9gN8eP6lGOtozuaw==
version "1.1.3"
resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6"
integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==
"@npmcli/fs@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-1.0.0.tgz#589612cfad3a6ea0feafcb901d29c63fd52db09f"
integrity sha512-8ltnOpRR/oJbOp8vaGUnipOi3bqkcW+sLHFlyXIr08OGHmVJLB1Hn7QtGXbYcpVtH1gAYZTlmDXtE4YV0+AMMQ==
version "1.1.1"
resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-1.1.1.tgz#72f719fe935e687c56a4faecf3c03d06ba593257"
integrity sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==
dependencies:
"@gar/promisify" "^1.0.1"
semver "^7.3.5"
@ -41,9 +41,9 @@ agent-base@6, agent-base@^6.0.2:
debug "4"
agentkeepalive@^4.1.3:
version "4.1.4"
resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.1.4.tgz#d928028a4862cb11718e55227872e842a44c945b"
integrity sha512-+V/rGa3EuU74H6wR04plBb7Ks10FbtUQgRj/FQOG7uUIEuaINI+AiqJR1k6t3SVNs7o7ZjIdus6706qqzVq8jQ==
version "4.2.1"
resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.2.1.tgz#a7975cbb9f83b367f06c90cc51ff28fe7d499717"
integrity sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA==
dependencies:
debug "^4.1.0"
depd "^1.1.2"
@ -67,10 +67,10 @@ ansi-regex@^5.0.1:
resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc"
integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==
are-we-there-yet@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c"
integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==
are-we-there-yet@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz#679df222b278c64f2cdba1175cdc00b0d96164bd"
integrity sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==
dependencies:
delegates "^1.0.0"
readable-stream "^3.6.0"
@ -122,7 +122,7 @@ clean-stack@^2.0.0:
resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b"
integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==
color-support@^1.1.2:
color-support@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2"
integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==
@ -130,29 +130,29 @@ color-support@^1.1.2:
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
console-control-strings@^1.0.0, console-control-strings@^1.1.0:
console-control-strings@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=
integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==
debug@4, debug@^4.1.0, debug@^4.3.1:
version "4.3.3"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664"
integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==
debug@4, debug@^4.1.0, debug@^4.3.3:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
dependencies:
ms "2.1.2"
delegates@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=
integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==
depd@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==
emoji-regex@^8.0.0:
version "8.0.0"
@ -186,49 +186,48 @@ fs-minipass@^2.0.0:
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
gauge@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.0.tgz#afba07aa0374a93c6219603b1fb83eaa2264d8f8"
integrity sha512-F8sU45yQpjQjxKkm1UOAhf0U/O0aFt//Fl7hsrNVto+patMHjs7dPI9mFOGUKbhrgKm0S3EjW3scMFuQmWSROw==
gauge@^4.0.3:
version "4.0.4"
resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.4.tgz#52ff0652f2bbf607a989793d53b751bef2328dce"
integrity sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==
dependencies:
ansi-regex "^5.0.1"
aproba "^1.0.3 || ^2.0.0"
color-support "^1.1.2"
console-control-strings "^1.0.0"
color-support "^1.1.3"
console-control-strings "^1.1.0"
has-unicode "^2.0.1"
signal-exit "^3.0.0"
signal-exit "^3.0.7"
string-width "^4.2.3"
strip-ansi "^6.0.1"
wide-align "^1.1.2"
wide-align "^1.1.5"
glob@^7.1.3, glob@^7.1.4:
version "7.2.0"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
version "7.2.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.0.4"
minimatch "^3.1.1"
once "^1.3.0"
path-is-absolute "^1.0.0"
graceful-fs@^4.2.6:
version "4.2.8"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a"
integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==
version "4.2.10"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
has-unicode@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=
integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==
http-cache-semantics@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390"
integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==
version "4.1.1"
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a"
integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==
http-proxy-agent@^4.0.1:
version "4.0.1"
@ -240,9 +239,9 @@ http-proxy-agent@^4.0.1:
debug "4"
https-proxy-agent@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2"
integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==
version "5.0.1"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==
dependencies:
agent-base "6"
debug "4"
@ -250,7 +249,7 @@ https-proxy-agent@^5.0.0:
humanize-ms@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed"
integrity sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=
integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==
dependencies:
ms "^2.0.0"
@ -264,7 +263,7 @@ iconv-lite@^0.6.2:
imurmurhash@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==
indent-string@^4.0.0:
version "4.0.0"
@ -279,7 +278,7 @@ infer-owner@^1.0.4:
inflight@^1.0.4:
version "1.0.6"
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
dependencies:
once "^1.3.0"
wrappy "1"
@ -289,10 +288,10 @@ inherits@2, inherits@^2.0.3:
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
ip@^1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a"
integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=
ip@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da"
integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==
is-fullwidth-code-point@^3.0.0:
version "3.0.0"
@ -302,12 +301,12 @@ is-fullwidth-code-point@^3.0.0:
is-lambda@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5"
integrity sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU=
integrity sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==
isexe@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
lru-cache@^6.0.0:
version "6.0.0"
@ -338,10 +337,10 @@ make-fetch-happen@^9.1.0:
socks-proxy-agent "^6.0.0"
ssri "^8.0.0"
minimatch@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
minimatch@^3.1.1:
version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
dependencies:
brace-expansion "^1.1.7"
@ -385,12 +384,17 @@ minipass-sized@^1.0.3:
minipass "^3.0.0"
minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3:
version "3.1.5"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.5.tgz#71f6251b0a33a49c01b3cf97ff77eda030dff732"
integrity sha512-+8NzxD82XQoNKNrl1d/FSi+X8wAEWR+sbYAfIvub4Nz0d22plFG72CEVVaufV8PNf4qSslFTD8VMOxNVhHCjTw==
version "3.3.6"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a"
integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==
dependencies:
yallist "^4.0.0"
minipass@^4.0.0:
version "4.0.3"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.0.3.tgz#00bfbaf1e16e35e804f4aa31a7c1f6b8d9f0ee72"
integrity sha512-OW2r4sQ0sI+z5ckEt5c1Tri4xTgZwYDxpE54eqWlQloQRoWtXjqt9udJ5Z4dSv7wK+nfFI7FRXyCpBSft+gpFw==
minizlib@^2.0.0, minizlib@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931"
@ -415,9 +419,9 @@ ms@^2.0.0:
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
negotiator@^0.6.2:
version "0.6.2"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==
version "0.6.3"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
node-gyp@^8.4.1:
version "8.4.1"
@ -443,19 +447,19 @@ nopt@^5.0.0:
abbrev "1"
npmlog@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.0.tgz#ba9ef39413c3d936ea91553db7be49c34ad0520c"
integrity sha512-03ppFRGlsyUaQFbGC2C8QWJN/C/K7PsfyD9aQdhVKAQIH4sQBc8WASqFBP7O+Ut4d2oo5LoeoboB3cGdBZSp6Q==
version "6.0.2"
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.2.tgz#c8166017a42f2dea92d6453168dd865186a70830"
integrity sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==
dependencies:
are-we-there-yet "^2.0.0"
are-we-there-yet "^3.0.0"
console-control-strings "^1.1.0"
gauge "^4.0.0"
gauge "^4.0.3"
set-blocking "^2.0.0"
once@^1.3.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
dependencies:
wrappy "1"
@ -469,12 +473,12 @@ p-map@^4.0.0:
path-is-absolute@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
promise-inflight@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3"
integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM=
integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==
promise-retry@^2.0.1:
version "2.0.1"
@ -496,7 +500,7 @@ readable-stream@^3.6.0:
retry@^0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b"
integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=
integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==
rimraf@^3.0.2:
version "3.0.2"
@ -516,43 +520,43 @@ safe-buffer@~5.2.0:
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
semver@^7.3.5:
version "7.3.5"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7"
integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==
version "7.3.8"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798"
integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==
dependencies:
lru-cache "^6.0.0"
set-blocking@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==
signal-exit@^3.0.0:
version "3.0.6"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.6.tgz#24e630c4b0f03fea446a2bd299e62b4a6ca8d0af"
integrity sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==
signal-exit@^3.0.7:
version "3.0.7"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
smart-buffer@^4.1.0:
smart-buffer@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae"
integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==
socks-proxy-agent@^6.0.0:
version "6.1.1"
resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-6.1.1.tgz#e664e8f1aaf4e1fb3df945f09e3d94f911137f87"
integrity sha512-t8J0kG3csjA4g6FTbsMOWws+7R7vuRC8aQ/wy3/1OWmsgwA68zs/+cExQ0koSitUDXqhufF/YJr9wtNMZHw5Ew==
version "6.2.1"
resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz#2687a31f9d7185e38d530bef1944fe1f1496d6ce"
integrity sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==
dependencies:
agent-base "^6.0.2"
debug "^4.3.1"
socks "^2.6.1"
debug "^4.3.3"
socks "^2.6.2"
socks@^2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/socks/-/socks-2.6.1.tgz#989e6534a07cf337deb1b1c94aaa44296520d30e"
integrity sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA==
socks@^2.6.2:
version "2.7.1"
resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55"
integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==
dependencies:
ip "^1.1.5"
smart-buffer "^4.1.0"
ip "^2.0.0"
smart-buffer "^4.2.0"
ssri@^8.0.0, ssri@^8.0.1:
version "8.0.1"
@ -585,13 +589,13 @@ strip-ansi@^6.0.1:
ansi-regex "^5.0.1"
tar@^6.0.2, tar@^6.1.2:
version "6.1.11"
resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621"
integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==
version "6.1.13"
resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.13.tgz#46e22529000f612180601a6fe0680e7da508847b"
integrity sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==
dependencies:
chownr "^2.0.0"
fs-minipass "^2.0.0"
minipass "^3.0.0"
minipass "^4.0.0"
minizlib "^2.1.1"
mkdirp "^1.0.3"
yallist "^4.0.0"
@ -613,7 +617,7 @@ unique-slug@^2.0.0:
util-deprecate@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
which@^2.0.2:
version "2.0.2"
@ -622,7 +626,7 @@ which@^2.0.2:
dependencies:
isexe "^2.0.0"
wide-align@^1.1.2:
wide-align@^1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3"
integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==
@ -632,7 +636,7 @@ wide-align@^1.1.2:
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
yallist@^4.0.0:
version "4.0.0"

View file

@ -15,6 +15,7 @@
"contribEditSessions",
"contribViewsWelcome",
"editSessionIdentityProvider",
"quickDiffProvider",
"scmActionButton",
"scmSelectedProvider",
"scmValidation",

View file

@ -960,6 +960,13 @@ export class Repository implements Disposable {
}
}
/**
* Quick diff label
*/
get label(): string {
return l10n.t('Git local working changes');
}
provideOriginalResource(uri: Uri): Uri | undefined {
if (uri.scheme !== 'file') {
return;

View file

@ -62,6 +62,7 @@ export interface IMicrosoftTokens {
}
interface IScopeData {
originalScopes?: string[];
scopes: string[];
scopeStr: string;
scopesToSend: string;
@ -210,27 +211,28 @@ export class AzureActiveDirectoryService {
}
}
const clientId = this.getClientId(scopes);
const scopeData: IScopeData = {
clientId,
originalScopes: scopes,
scopes: modifiedScopes,
scopeStr: modifiedScopesStr,
// filter our special scopes
scopesToSend: modifiedScopes.filter(s => !s.startsWith('VSCODE_')).join(' '),
tenant: this.getTenantId(scopes),
};
// If we still don't have a matching token try to get a new token from an existing token by using
// the refreshToken. This is documented here:
// https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow#refresh-the-access-token
// "Refresh tokens are valid for all permissions that your client has already received consent for."
if (!matchingTokens.length) {
const clientId = this.getClientId(modifiedScopes);
// Get a token with the correct client id.
const token = clientId === DEFAULT_CLIENT_ID
? this._tokens.find(t => t.refreshToken && !t.scope.includes('VSCODE_CLIENT_ID'))
: this._tokens.find(t => t.refreshToken && t.scope.includes(`VSCODE_CLIENT_ID:${clientId}`));
if (token) {
const scopeData: IScopeData = {
clientId,
scopes: modifiedScopes,
scopeStr: modifiedScopesStr,
// filter our special scopes
scopesToSend: modifiedScopes.filter(s => !s.startsWith('VSCODE_')).join(' '),
tenant: this.getTenantId(modifiedScopes),
};
try {
const itoken = await this.refreshToken(token.refreshToken, scopeData);
matchingTokens.push(itoken);
@ -241,36 +243,35 @@ export class AzureActiveDirectoryService {
}
Logger.info(`Got ${matchingTokens.length} sessions for scopes: ${modifiedScopesStr}`);
return Promise.all(matchingTokens.map(token => this.convertToSession(token)));
return Promise.all(matchingTokens.map(token => this.convertToSession(token, scopeData)));
}
public createSession(scopes: string[]): Promise<vscode.AuthenticationSession> {
if (!scopes.includes('openid')) {
scopes.push('openid');
let modifiedScopes = [...scopes];
if (!modifiedScopes.includes('openid')) {
modifiedScopes.push('openid');
}
if (!scopes.includes('email')) {
scopes.push('email');
if (!modifiedScopes.includes('email')) {
modifiedScopes.push('email');
}
if (!scopes.includes('profile')) {
scopes.push('profile');
if (!modifiedScopes.includes('profile')) {
modifiedScopes.push('profile');
}
if (!scopes.includes('offline_access')) {
scopes.push('offline_access');
if (!modifiedScopes.includes('offline_access')) {
modifiedScopes.push('offline_access');
}
scopes = scopes.sort();
modifiedScopes = modifiedScopes.sort();
const scopeData: IScopeData = {
scopes,
scopeStr: scopes.join(' '),
originalScopes: scopes,
scopes: modifiedScopes,
scopeStr: modifiedScopes.join(' '),
// filter our special scopes
scopesToSend: scopes.filter(s => !s.startsWith('VSCODE_')).join(' '),
scopesToSend: modifiedScopes.filter(s => !s.startsWith('VSCODE_')).join(' '),
clientId: this.getClientId(scopes),
tenant: this.getTenantId(scopes),
};
Logger.info(`Logging in for the following scopes: ${scopeData.scopeStr}`);
if (!scopeData.scopes.includes('offline_access')) {
Logger.info('Warning: The \'offline_access\' scope was not included, so the generated token will not be able to be refreshed.');
}
const runsRemote = vscode.env.remoteName !== undefined;
const runsServerless = vscode.env.remoteName === undefined && vscode.env.uiKind === vscode.UIKind.Web;
@ -511,7 +512,7 @@ export class AzureActiveDirectoryService {
};
}
private async convertToSession(token: IToken): Promise<vscode.AuthenticationSession> {
private async convertToSession(token: IToken, scopeData: IScopeData): Promise<vscode.AuthenticationSession> {
if (token.accessToken && (!token.expiresAt || token.expiresAt > Date.now())) {
token.expiresAt
? Logger.info(`Token available from cache (for scopes ${token.scope}), expires in ${token.expiresAt - Date.now()} milliseconds`)
@ -521,21 +522,12 @@ export class AzureActiveDirectoryService {
accessToken: token.accessToken,
idToken: token.idToken,
account: token.account,
scopes: token.scope.split(' ')
scopes: scopeData.originalScopes ?? scopeData.scopes
};
}
try {
Logger.info(`Token expired or unavailable (for scopes ${token.scope}), trying refresh`);
const scopes = token.scope.split(' ');
const scopeData: IScopeData = {
scopes,
scopeStr: token.scope,
// filter our special scopes
scopesToSend: scopes.filter(s => !s.startsWith('VSCODE_')).join(' '),
clientId: this.getClientId(scopes),
tenant: this.getTenantId(scopes),
};
const refreshedToken = await this.refreshToken(token.refreshToken, scopeData, token.sessionId);
if (refreshedToken.accessToken) {
return {
@ -543,7 +535,8 @@ export class AzureActiveDirectoryService {
accessToken: refreshedToken.accessToken,
idToken: refreshedToken.idToken,
account: token.account,
scopes: token.scope.split(' ')
// We always prefer the original scopes requested since that array is used as a key in the AuthService
scopes: scopeData.originalScopes ?? scopeData.scopes
};
} else {
throw new Error();
@ -729,7 +722,7 @@ export class AzureActiveDirectoryService {
}
this.setToken(token, scopeData);
Logger.info(`Login successful for scopes: ${scopeData.scopeStr}`);
return await this.convertToSession(token);
return await this.convertToSession(token, scopeData);
}
private async fetchTokenResponse(endpoint: string, postData: string, scopeData: IScopeData): Promise<ITokenResponse> {

View file

@ -31,6 +31,7 @@ interface JavaScriptRenderingHook {
interface RenderOptions {
readonly lineLimit: number;
readonly outputScrolling: boolean;
readonly outputWordWrap: boolean;
}
function clearContainer(container: HTMLElement) {
@ -134,6 +135,8 @@ async function renderJavascript(outputInfo: OutputItem, container: HTMLElement,
}
function renderError(outputInfo: OutputItem, container: HTMLElement, ctx: RendererContext<void> & { readonly settings: RenderOptions }): void {
clearContainer(container);
const element = document.createElement('div');
container.appendChild(element);
type ErrorLike = Partial<Error>;
@ -149,6 +152,9 @@ function renderError(outputInfo: OutputItem, container: HTMLElement, ctx: Render
if (err.stack) {
const stack = document.createElement('pre');
stack.classList.add('traceback');
if (ctx.settings.outputWordWrap) {
stack.classList.add('wordWrap');
}
stack.style.margin = '8px 0';
const element = document.createElement('span');
insertOutput(outputInfo.id, [err.stack ?? ''], ctx.settings.lineLimit, false, element, true);
@ -190,6 +196,11 @@ function renderStream(outputInfo: OutputItem, container: HTMLElement, error: boo
const text = outputInfo.text();
const element = existing ?? document.createElement('span');
element.classList.add('output-stream');
if (ctx.settings.outputWordWrap) {
element.classList.add('wordWrap');
} else {
element.classList.remove('wordWrap');
}
element.setAttribute('output-item-id', outputInfo.id);
insertOutput(outputInfo.id, [text], ctx.settings.lineLimit, ctx.settings.outputScrolling, element, false);
outputElement.appendChild(element);
@ -199,6 +210,9 @@ function renderStream(outputInfo: OutputItem, container: HTMLElement, error: boo
const element = document.createElement('span');
element.classList.add('output-stream');
if (ctx.settings.outputWordWrap) {
element.classList.add('wordWrap');
}
element.setAttribute('output-item-id', outputInfo.id);
const text = outputInfo.text();
@ -217,6 +231,9 @@ function renderText(outputInfo: OutputItem, container: HTMLElement, ctx: Rendere
clearContainer(container);
const contentNode = document.createElement('div');
contentNode.classList.add('output-plaintext');
if (ctx.settings.outputWordWrap) {
contentNode.classList.add('wordWrap');
}
const text = outputInfo.text();
insertOutput(outputInfo.id, [text], ctx.settings.lineLimit, ctx.settings.outputScrolling, contentNode, false);
container.appendChild(contentNode);
@ -243,16 +260,17 @@ export const activate: ActivationFunction<void> = (ctx) => {
-webkit-user-select: text;
-ms-user-select: text;
cursor: auto;
word-wrap: break-word;
/* text/stream output container should scroll but preserve newline character */
white-space: pre;
}
.output-plaintext,
.output-stream {
/* When wordwrap turned on, force it to pre-wrap */
.output-plaintext.wordWrap span,
.output-stream.wordWrap span,
.traceback.wordWrap span {
white-space: pre-wrap;
}
output-plaintext,
.traceback {
word-wrap: break-word;
}
.output > .scrollable {
.output .scrollable {
overflow-y: scroll;
max-height: var(--notebook-cell-output-max-height);
border: var(--vscode-editorWidget-border);

View file

@ -50,14 +50,14 @@ function truncatedArrayOfString(id: string, buffer: string[], linesLimit: number
}
function scrollableArrayOfString(id: string, buffer: string[], container: HTMLElement, trustHtml: boolean) {
container.classList.add('scrollable');
const scrollableDiv = document.createElement('div');
scrollableDiv.classList.add('scrollable');
if (buffer.length > 5000) {
container.appendChild(generateViewMoreElement(id, false));
}
const div = document.createElement('div');
container.appendChild(div);
div.appendChild(handleANSIOutput(buffer.slice(0, 5000).join('\n'), trustHtml));
container.appendChild(scrollableDiv);
scrollableDiv.appendChild(handleANSIOutput(buffer.slice(0, 5000).join('\n'), trustHtml));
}
export function insertOutput(id: string, outputs: string[], linesLimit: number, scrollable: boolean, container: HTMLElement, trustHtml: boolean) {

View file

@ -500,7 +500,7 @@
"inputOption.activeBorder": "#adafb7",
"dropdown.background": "#F5F5F5",
"editor.findMatchBackground": "#BF9CAC",
"editor.findMatchHighlightBackground": "#edc9d8",
"editor.findMatchHighlightBackground": "#edc9d899",
"peekViewEditor.matchHighlightBackground": "#C2DFE3",
"peekViewTitle.background": "#F2F8FC",
"peekViewEditor.background": "#F2F8FC",

View file

@ -1,7 +1,7 @@
{
"name": "code-oss-dev",
"version": "1.76.0",
"distro": "ea016b36988a95ad53a905e9449f360c77fe5026",
"distro": "cd6d83a02668b3ee1577d32ede0540c6e71b8119",
"author": {
"name": "Microsoft Corporation"
},
@ -65,6 +65,7 @@
"@microsoft/1ds-post-js": "^3.2.2",
"@parcel/watcher": "2.1.0",
"@vscode/iconv-lite-umd": "0.7.0",
"@vscode/policy-watcher": "^1.1.4",
"@vscode/ripgrep": "^1.14.2",
"@vscode/sqlite3": "5.1.2-vscode",
"@vscode/sudo-prompt": "9.3.1",
@ -83,17 +84,16 @@
"tas-client-umd": "0.1.6",
"v8-inspect-profiler": "^0.1.0",
"vscode-oniguruma": "1.7.0",
"vscode-policy-watcher": "^1.1.1",
"vscode-proxy-agent": "^0.12.0",
"vscode-regexpp": "^3.1.0",
"vscode-textmate": "9.0.0",
"xterm": "5.2.0-beta.28",
"xterm": "5.2.0-beta.29",
"xterm-addon-canvas": "0.4.0-beta.7",
"xterm-addon-search": "0.11.0",
"xterm-addon-serialize": "0.9.0",
"xterm-addon-unicode11": "0.5.0",
"xterm-addon-webgl": "0.15.0-beta.7",
"xterm-headless": "5.2.0-beta.28",
"xterm-headless": "5.2.0-beta.29",
"yauzl": "^2.9.2",
"yazl": "^2.4.3"
},

View file

@ -24,13 +24,13 @@
"vscode-proxy-agent": "^0.12.0",
"vscode-regexpp": "^3.1.0",
"vscode-textmate": "9.0.0",
"xterm": "5.2.0-beta.28",
"xterm": "5.2.0-beta.29",
"xterm-addon-canvas": "0.4.0-beta.7",
"xterm-addon-search": "0.11.0",
"xterm-addon-serialize": "0.9.0",
"xterm-addon-unicode11": "0.5.0",
"xterm-addon-webgl": "0.15.0-beta.7",
"xterm-headless": "5.2.0-beta.28",
"xterm-headless": "5.2.0-beta.29",
"yauzl": "^2.9.2",
"yazl": "^2.4.3"
},

View file

@ -11,7 +11,7 @@
"tas-client-umd": "0.1.6",
"vscode-oniguruma": "1.7.0",
"vscode-textmate": "9.0.0",
"xterm": "5.2.0-beta.28",
"xterm": "5.2.0-beta.29",
"xterm-addon-canvas": "0.4.0-beta.7",
"xterm-addon-search": "0.11.0",
"xterm-addon-unicode11": "0.5.0",

View file

@ -88,7 +88,7 @@ xterm-addon-webgl@0.15.0-beta.7:
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.15.0-beta.7.tgz#ab247b499f61e8eebff92e08ec5ca999d87e06af"
integrity sha512-7WCI/D6uFNp3y9TeTsbSo1h7gCy4h/yP2lWn8ZEjCaiGvO11DbKMq17fbiwaR3YmGWXoRKkcLaNIiqxFnjKO4w==
xterm@5.2.0-beta.28:
version "5.2.0-beta.28"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.2.0-beta.28.tgz#852347e4eaf5aae7d82c90592a42adc9daab2a79"
integrity sha512-aLDxCuqjWHjvnhfWfkxy/y6coNrC+QIhbDe2sdfLPkrxhK6KnYE6qiZD5jXUIQXeq0KmSDcYi/esuKujvobC2A==
xterm@5.2.0-beta.29:
version "5.2.0-beta.29"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.2.0-beta.29.tgz#99764aff5cd8cdb4335f5d59466b134cfcb45e3e"
integrity sha512-zx5RKcQqo78bza4R/m3WtxAJCBAF4U61fy6cxqb1PkqXF9/qdYlySUCVOauMxv+6n6cAxt3EQWwLlgvbvQBbsw==

View file

@ -866,15 +866,15 @@ xterm-addon-webgl@0.15.0-beta.7:
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.15.0-beta.7.tgz#ab247b499f61e8eebff92e08ec5ca999d87e06af"
integrity sha512-7WCI/D6uFNp3y9TeTsbSo1h7gCy4h/yP2lWn8ZEjCaiGvO11DbKMq17fbiwaR3YmGWXoRKkcLaNIiqxFnjKO4w==
xterm-headless@5.2.0-beta.28:
version "5.2.0-beta.28"
resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.2.0-beta.28.tgz#d2c149da51ef138f46268b755c4fdc4202eb771c"
integrity sha512-4XcjBhFwuyjpz2ubESwp75UceySOOKdJszKyyxOQ3/7L937uiVEBBLc8T231XU8lSwWUU7czyNjYyCfpszY4+Q==
xterm-headless@5.2.0-beta.29:
version "5.2.0-beta.29"
resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.2.0-beta.29.tgz#dd08312fdb4292c217e685d9e2e8b1957364e298"
integrity sha512-1P4urIeDTkl2C+zGb4WUnKJMACZMPGYHwVXMjkB0WhMISbkt6M34MH9ljxHhnL99dHwlx2Lvi6wvhnpyZucWCg==
xterm@5.2.0-beta.28:
version "5.2.0-beta.28"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.2.0-beta.28.tgz#852347e4eaf5aae7d82c90592a42adc9daab2a79"
integrity sha512-aLDxCuqjWHjvnhfWfkxy/y6coNrC+QIhbDe2sdfLPkrxhK6KnYE6qiZD5jXUIQXeq0KmSDcYi/esuKujvobC2A==
xterm@5.2.0-beta.29:
version "5.2.0-beta.29"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.2.0-beta.29.tgz#99764aff5cd8cdb4335f5d59466b134cfcb45e3e"
integrity sha512-zx5RKcQqo78bza4R/m3WtxAJCBAF4U61fy6cxqb1PkqXF9/qdYlySUCVOauMxv+6n6cAxt3EQWwLlgvbvQBbsw==
yallist@^4.0.0:
version "4.0.0"

View file

@ -73,10 +73,10 @@ exports.keyboardMaps = [
exports.code = [
createModuleDescription('vs/code/electron-main/main'),
createModuleDescription('vs/code/node/cli'),
createModuleDescription('vs/code/node/cliProcessMain', ['vs/code/node/cli']),
createModuleDescription('vs/code/node/cli/main'),
createModuleDescription('vs/code/node/cli/cliProcessMain', ['vs/code/node/cli/main']),
createModuleDescription('vs/code/electron-sandbox/issue/issueReporterMain'),
createModuleDescription('vs/code/electron-browser/sharedProcess/sharedProcessMain'),
createModuleDescription('vs/code/node/sharedProcess/sharedProcessMain'),
createModuleDescription('vs/code/electron-sandbox/processExplorer/processExplorerMain')
];

View file

@ -28,4 +28,4 @@ bootstrap.enableASARSupport();
process.env['VSCODE_CLI'] = '1';
// Load CLI through AMD loader
require('./bootstrap-amd').load('vs/code/node/cli');
require('./bootstrap-amd').load('vs/code/node/cli/main');

View file

@ -415,7 +415,7 @@ export class ActionViewItem extends BaseActionViewItem {
}
}
export class SelectActionViewItem extends BaseActionViewItem {
export class SelectActionViewItem<T = string> extends BaseActionViewItem {
protected selectBox: SelectBox;
constructor(ctx: unknown, action: IAction, options: ISelectOptionItem[], selected: number, contextViewProvider: IContextViewProvider, styles: ISelectBoxStyles, selectBoxOptions?: ISelectBoxOptions) {
@ -444,7 +444,7 @@ export class SelectActionViewItem extends BaseActionViewItem {
this.actionRunner.run(this._action, this.getActionContext(option, index));
}
protected getActionContext(option: string, index: number) {
protected getActionContext(option: string, index: number): T | string {
return option;
}

View file

@ -14,11 +14,40 @@ export interface IHoverDelegateTarget extends IDisposable {
}
export interface IHoverDelegateOptions extends IUpdatableHoverOptions {
/**
* The content to display in the primary section of the hover. The type of text determines the
* default `hideOnHover` behavior.
*/
content: IMarkdownString | string | HTMLElement;
/**
* The target for the hover. This determines the position of the hover and it will only be
* hidden when the mouse leaves both the hover and the target. A HTMLElement can be used for
* simple cases and a IHoverDelegateTarget for more complex cases where multiple elements and/or a
* dispose method is required.
*/
target: IHoverDelegateTarget | HTMLElement;
/**
* Position of the hover. The default is to show above the target. This option will be ignored
* if there is not enough room to layout the hover in the specified position, unless the
* forcePosition option is set.
*/
hoverPosition?: HoverPosition;
/**
* Whether to show the hover pointer
*/
showPointer?: boolean;
/**
* Whether to skip the fade in animation, this should be used when hovering from one hover to
* another in the same group so it looks like the hover is moving from one element to the other.
*/
skipFadeInAnimation?: boolean;
/**
* The container to pass to {@link IContextViewProvider.showContextView} which renders the hover
* in. This is particularly useful for more natural tab focusing behavior, where the hover is
* created as the next tab index after the element being hovered and/or to workaround the
* element's container hiding on `focusout`.
*/
container?: HTMLElement;
}
export interface IHoverDelegate {

View file

@ -164,6 +164,7 @@ export function setupCustomHover(hoverDelegate: IHoverDelegate, htmlElement: HTM
let hoverWidget: UpdatableHoverWidget | undefined;
const hideHover = (disposeWidget: boolean, disposePreparation: boolean) => {
const hadHover = hoverWidget !== undefined;
if (disposeWidget) {
hoverWidget?.dispose();
hoverWidget = undefined;
@ -172,7 +173,9 @@ export function setupCustomHover(hoverDelegate: IHoverDelegate, htmlElement: HTM
hoverPreparation?.dispose();
hoverPreparation = undefined;
}
hoverDelegate.onDidHideHover?.();
if (hadHover) {
hoverDelegate.onDidHideHover?.();
}
};
const triggerShowHover = (delay: number, focus?: boolean, target?: IHoverDelegateTarget) => {

View file

@ -48,15 +48,16 @@ export function listProcesses(rootPid: number): Promise<ProcessItem> {
function findName(cmd: string): string {
const SHARED_PROCESS_HINT = /--vscode-window-kind=shared-process/;
const ISSUE_REPORTER_HINT = /--vscode-window-kind=issue-reporter/;
const PROCESS_EXPLORER_HINT = /--vscode-window-kind=process-explorer/;
const UTILITY_NETWORK_HINT = /--utility-sub-type=network/;
const UTILITY_EXTENSION_HOST_HINT = /--vscode-utility-kind=extensionHost/;
const UTILITY_FILE_WATCHER_HOST_HINT = /--vscode-utility-kind=fileWatcher/;
const WINDOWS_CRASH_REPORTER = /--crashes-directory/;
const WINDOWS_PTY = /\\pipe\\winpty-control/;
const WINDOWS_CONSOLE_HOST = /conhost\.exe/;
const SHARED_PROCESS_HINT = /--vscode-window-kind=shared-process/i; // TODO@bpasero remove me
const ISSUE_REPORTER_HINT = /--vscode-window-kind=issue-reporter/i;
const PROCESS_EXPLORER_HINT = /--vscode-window-kind=process-explorer/i;
const UTILITY_NETWORK_HINT = /--utility-sub-type=network/i;
const UTILITY_EXTENSION_HOST_HINT = /--vscode-utility-kind=extensionHost/i;
const UTILITY_FILE_WATCHER_HOST_HINT = /--vscode-utility-kind=fileWatcher/i;
const UTILITY_SHARED_PROCESS_HINT = /--vscode-utility-kind=shared-process/i;
const WINDOWS_CRASH_REPORTER = /--crashes-directory/i;
const WINDOWS_PTY = /\\pipe\\winpty-control/i;
const WINDOWS_CONSOLE_HOST = /conhost\.exe/i;
const TYPE = /--type=([a-zA-Z-]+)/;
// find windows crash reporter
@ -103,6 +104,10 @@ export function listProcesses(rootPid: number): Promise<ProcessItem> {
if (UTILITY_FILE_WATCHER_HOST_HINT.exec(cmd)) {
return 'file-watcher';
}
if (UTILITY_SHARED_PROCESS_HINT.exec(cmd)) {
return 'shared-process';
}
} else if (matches[1] === 'extensionHost') {
return 'extension-host'; // normalize remote extension host type
}

View file

@ -8,6 +8,7 @@ import { VSBuffer } from 'vs/base/common/buffer';
import { ClientConnectionEvent, IMessagePassingProtocol, IPCServer } from 'vs/base/parts/ipc/common/ipc';
import { Emitter, Event } from 'vs/base/common/event';
import { assertType } from 'vs/base/common/types';
import { firstOrDefault } from 'vs/base/common/arrays';
/**
* The MessagePort `Protocol` leverages MessagePortMain style IPC communication
@ -44,8 +45,10 @@ export class Server extends IPCServer {
const onCreateMessageChannel = new Emitter<MessagePortMain>();
process.parentPort.on('message', (e: Electron.MessageEvent) => {
const ports = e.ports;
onCreateMessageChannel.fire(ports[0]);
const port = firstOrDefault(e.ports);
if (port) {
onCreateMessageChannel.fire(port);
}
});
return Event.map(onCreateMessageChannel.event, port => {
@ -67,3 +70,19 @@ export class Server extends IPCServer {
super(Server.getOnDidClientConnect());
}
}
interface INodeMessagePortFragment {
on(event: 'message', listener: (messageEvent: MessageEvent) => void): this;
removeListener(event: 'message', listener: (messageEvent: MessageEvent) => void): this;
}
export function once(port: INodeMessagePortFragment, message: unknown, callback: () => void): void {
const listener = (e: MessageEvent) => {
if (e.data === message) {
port.removeListener('message', listener);
callback();
}
};
port.on('message', listener);
}

View file

@ -213,9 +213,8 @@ export class CodeApplication extends Disposable {
};
const isAllowedWebviewRequest = (uri: URI, details: Electron.OnBeforeRequestListenerDetails): boolean => {
// Only restrict top level page of webviews: index.html
if (uri.path !== '/index.html') {
return true;
return true; // Only restrict top level page of webviews: index.html
}
const frame = details.frame;
@ -1264,6 +1263,7 @@ export class CodeApplication extends Disposable {
type: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'The type of shared process crash to understand the nature of the crash better.' };
reason: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The reason of the shared process crash to understand the nature of the crash better.' };
code: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'The exit code of the shared process crash to understand the nature of the crash better.' };
utilityprocess: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'If the shared process is using utility process or a hidden window.' };
visible: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'Whether the shared process window was visible or not.' };
shuttingdown: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'Whether the application is shutting down when the crash happens.' };
owner: 'bpasero';
@ -1275,6 +1275,7 @@ export class CodeApplication extends Disposable {
reason: string | undefined;
code: number | undefined;
visible: boolean;
utilityprocess: string;
shuttingdown: boolean;
};
telemetryService.publicLog2<SharedProcessErrorEvent, SharedProcessErrorClassification>('sharedprocesserror', {
@ -1282,6 +1283,7 @@ export class CodeApplication extends Disposable {
reason: details?.reason,
code: details?.exitCode,
visible: sharedProcess.isVisible(),
utilityprocess: sharedProcess.usingUtilityProcess() ? '1' : '0', // TODO@bpasero remove this once sandbox is enabled by default
shuttingdown: willShutdown
});
}));

View file

@ -9,13 +9,16 @@ import { Disposable } from 'vs/base/common/lifecycle';
import { join } from 'vs/base/common/path';
import { Promises } from 'vs/base/node/pfs';
import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services';
import { ILogService } from 'vs/platform/log/common/log';
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
import { StorageClient } from 'vs/platform/storage/common/storageIpc';
import { EXTENSION_DEVELOPMENT_EMPTY_WINDOW_WORKSPACE } from 'vs/platform/workspace/common/workspace';
import { NON_EMPTY_WORKSPACE_ID_LENGTH } from 'vs/platform/workspaces/node/workspaces';
/* eslint-disable local/code-layering, local/code-import-patterns */
// TODO@bpasero layer is not allowed in utility process
import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services';
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
export class UnusedWorkspaceStorageDataCleaner extends Disposable {
constructor(

View file

@ -10,7 +10,7 @@
const bootstrapWindow = bootstrapWindowLib();
// Load shared process into window
bootstrapWindow.load(['vs/code/electron-browser/sharedProcess/sharedProcessMain'], function (sharedProcess, configuration) {
bootstrapWindow.load(['vs/code/node/sharedProcess/sharedProcessMain'], function (sharedProcess, configuration) {
return sharedProcess.main(configuration);
},
{

View file

@ -3,7 +3,6 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ipcRenderer } from 'electron';
import { hostname, release } from 'os';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import { onUnexpectedError, setUnexpectedErrorHandler } from 'vs/base/common/errors';
@ -11,13 +10,13 @@ import { combinedDisposable, Disposable, toDisposable } from 'vs/base/common/lif
import { Schemas } from 'vs/base/common/network';
import { joinPath } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { ProxyChannel, StaticRouter } from 'vs/base/parts/ipc/common/ipc';
import { Server as MessagePortServer } from 'vs/base/parts/ipc/electron-browser/ipc.mp';
import { CodeCacheCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/codeCacheCleaner';
import { LanguagePackCachedDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner';
import { LocalizationsUpdater } from 'vs/code/electron-browser/sharedProcess/contrib/localizationsUpdater';
import { LogsDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner';
import { UnusedWorkspaceStorageDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner';
import { IPCServer, ProxyChannel, StaticRouter } from 'vs/base/parts/ipc/common/ipc';
import { Server as UtilityProcessMessagePortServer, once } from 'vs/base/parts/ipc/node/ipc.mp';
import { CodeCacheCleaner } from 'vs/code/node/sharedProcess/contrib/codeCacheCleaner';
import { LanguagePackCachedDataCleaner } from 'vs/code/node/sharedProcess/contrib/languagePackCachedDataCleaner';
import { LocalizationsUpdater } from 'vs/code/node/sharedProcess/contrib/localizationsUpdater';
import { LogsDataCleaner } from 'vs/code/node/sharedProcess/contrib/logsDataCleaner';
import { UnusedWorkspaceStorageDataCleaner } from 'vs/code/node/sharedProcess/contrib/storageDataCleaner';
import { IChecksumService } from 'vs/platform/checksum/common/checksumService';
import { ChecksumService } from 'vs/platform/checksum/node/checksumService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
@ -33,10 +32,8 @@ import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/
import { IExtensionGalleryService, IExtensionManagementService, IExtensionTipsService, IGlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionSignatureVerificationService, IExtensionSignatureVerificationService } from 'vs/platform/extensionManagement/node/extensionSignatureVerificationService';
import { ExtensionManagementChannel, ExtensionTipsChannel } from 'vs/platform/extensionManagement/common/extensionManagementIpc';
import { ExtensionTipsService } from 'vs/platform/extensionManagement/electron-sandbox/extensionTipsService';
import { ExtensionManagementService, INativeServerExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService';
import { IExtensionRecommendationNotificationService } from 'vs/platform/extensionRecommendations/common/extensionRecommendations';
import { ExtensionRecommendationNotificationServiceChannelClient } from 'vs/platform/extensionRecommendations/electron-sandbox/extensionRecommendationsIpc';
import { IFileService } from 'vs/platform/files/common/files';
import { FileService } from 'vs/platform/files/common/fileService';
import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
@ -44,19 +41,15 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { MessagePortMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService';
import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services';
import { ILanguagePackService } from 'vs/platform/languagePacks/common/languagePacks';
import { NativeLanguagePackService } from 'vs/platform/languagePacks/node/languagePacks';
import { ConsoleLogger, ILoggerService, ILogService } from 'vs/platform/log/common/log';
import { LoggerChannelClient } from 'vs/platform/log/common/logIpc';
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
import product from 'vs/platform/product/common/product';
import { IProductService } from 'vs/platform/product/common/productService';
import { IRequestService } from 'vs/platform/request/common/request';
import { ISharedProcessConfiguration } from 'vs/platform/sharedProcess/node/sharedProcess';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { NativeStorageService } from 'vs/platform/storage/electron-sandbox/storageService';
import { resolveCommonProperties } from 'vs/platform/telemetry/common/commonProperties';
import { ICustomEndpointTelemetryService, ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { TelemetryAppenderChannel } from 'vs/platform/telemetry/common/telemetryIpc';
@ -65,7 +58,6 @@ import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService'
import { supportsTelemetry, ITelemetryAppender, NullAppender, NullTelemetryService, getPiiPathsFromEnvironment, isInternalTelemetry } from 'vs/platform/telemetry/common/telemetryUtils';
import { CustomEndpointTelemetryService } from 'vs/platform/telemetry/node/customEndpointTelemetryService';
import { LocalReconnectConstants, TerminalIpcChannels, TerminalSettingId } from 'vs/platform/terminal/common/terminal';
import { ILocalPtyService } from 'vs/platform/terminal/electron-sandbox/terminal';
import { PtyHostService } from 'vs/platform/terminal/node/ptyHostService';
import { ExtensionStorageService, IExtensionStorageService } from 'vs/platform/extensionManagement/common/extensionStorage';
import { IgnoredExtensionsManagementService, IIgnoredExtensionsManagementService } from 'vs/platform/userDataSync/common/ignoredExtensions';
@ -79,8 +71,6 @@ import { UserDataSyncEnablementService } from 'vs/platform/userDataSync/common/u
import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService';
import { UserDataSyncChannel } from 'vs/platform/userDataSync/common/userDataSyncServiceIpc';
import { UserDataSyncStoreManagementService, UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService';
import { UserDataAutoSyncService } from 'vs/platform/userDataSync/electron-sandbox/userDataAutoSyncService';
import { UserDataProfileStorageService } from 'vs/platform/userDataProfile/electron-sandbox/userDataProfileStorageService';
import { IUserDataProfileStorageService } from 'vs/platform/userDataProfile/common/userDataProfileStorageService';
import { ActiveWindowManager } from 'vs/platform/windows/node/windowTracker';
import { ISignService } from 'vs/platform/sign/common/sign';
@ -90,7 +80,6 @@ import { SharedTunnelsService } from 'vs/platform/tunnel/node/tunnelService';
import { ipcSharedProcessTunnelChannelName, ISharedProcessTunnelService } from 'vs/platform/remote/common/sharedProcessTunnelService';
import { SharedProcessTunnelService } from 'vs/platform/tunnel/node/sharedProcessTunnelService';
import { ISharedProcessWorkerService } from 'vs/platform/sharedProcess/common/sharedProcessWorkerService';
import { SharedProcessWorkerService } from 'vs/platform/sharedProcess/electron-browser/sharedProcessWorkerService';
import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity';
import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService';
import { isLinux } from 'vs/base/common/platform';
@ -104,31 +93,52 @@ import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/use
import { IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService';
import { PolicyChannelClient } from 'vs/platform/policy/common/policyIpc';
import { IPolicyService, NullPolicyService } from 'vs/platform/policy/common/policy';
import { UserDataProfilesNativeService } from 'vs/platform/userDataProfile/electron-sandbox/userDataProfile';
import { SharedProcessRequestService } from 'vs/platform/request/electron-browser/sharedProcessRequestService';
import { UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfileIpc';
import { OneDataSystemAppender } from 'vs/platform/telemetry/node/1dsAppender';
import { UserDataProfilesCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/userDataProfilesCleaner';
import { RemoteTunnelService } from 'vs/platform/remoteTunnel/electron-browser/remoteTunnelService';
import { UserDataProfilesCleaner } from 'vs/code/node/sharedProcess/contrib/userDataProfilesCleaner';
import { IRemoteTunnelService } from 'vs/platform/remoteTunnel/common/remoteTunnel';
import { ISharedProcessLifecycleService, SharedProcessLifecycleService } from 'vs/platform/lifecycle/electron-browser/sharedProcessLifecycleService';
import { UserDataSyncResourceProviderService } from 'vs/platform/userDataSync/common/userDataSyncResourceProvider';
import { ExtensionsContributions } from 'vs/code/electron-browser/sharedProcess/contrib/extensions';
import { ExtensionsProfileScannerService } from 'vs/platform/extensionManagement/electron-sandbox/extensionsProfileScannerService';
import { ExtensionsContributions } from 'vs/code/node/sharedProcess/contrib/extensions';
import { localize } from 'vs/nls';
import { LogService } from 'vs/platform/log/common/logService';
import { ipcUtilityProcessWorkerChannelName, IUtilityProcessWorkerConfiguration } from 'vs/platform/utilityProcess/common/utilityProcessWorkerService';
import { isUtilityProcess } from 'vs/base/parts/sandbox/node/electronTypes';
/* eslint-disable local/code-layering, local/code-import-patterns */
// TODO@bpasero layer is not allowed in utility process
import { Server as BrowserWindowMessagePortServer } from 'vs/base/parts/ipc/electron-browser/ipc.mp';
import { ExtensionTipsService } from 'vs/platform/extensionManagement/electron-sandbox/extensionTipsService';
import { ExtensionRecommendationNotificationServiceChannelClient } from 'vs/platform/extensionRecommendations/electron-sandbox/extensionRecommendationsIpc';
import { MessagePortMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService';
import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services';
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
import { NativeStorageService } from 'vs/platform/storage/electron-sandbox/storageService';
import { ILocalPtyService } from 'vs/platform/terminal/electron-sandbox/terminal';
import { UserDataAutoSyncService } from 'vs/platform/userDataSync/electron-sandbox/userDataAutoSyncService';
import { UserDataProfileStorageService } from 'vs/platform/userDataProfile/electron-sandbox/userDataProfileStorageService';
import { SharedProcessWorkerService } from 'vs/platform/sharedProcess/electron-browser/sharedProcessWorkerService';
import { SharedProcessRequestService } from 'vs/platform/request/electron-browser/sharedProcessRequestService';
import { RemoteTunnelService } from 'vs/platform/remoteTunnel/electron-browser/remoteTunnelService';
import { ISharedProcessLifecycleService, SharedProcessLifecycleService } from 'vs/platform/lifecycle/electron-browser/sharedProcessLifecycleService';
import { ExtensionsProfileScannerService } from 'vs/platform/extensionManagement/electron-sandbox/extensionsProfileScannerService';
class SharedProcessMain extends Disposable {
private server = this._register(new MessagePortServer());
private readonly server: IPCServer;
private sharedProcessWorkerService: ISharedProcessWorkerService | undefined = undefined;
private lifecycleService: SharedProcessLifecycleService | undefined = undefined;
constructor(private configuration: ISharedProcessConfiguration) {
constructor(private configuration: ISharedProcessConfiguration, private ipcRenderer?: typeof import('electron').ipcRenderer) {
super();
if (isUtilityProcess(process)) {
this.server = this._register(new UtilityProcessMessagePortServer());
} else {
this.server = this._register(new BrowserWindowMessagePortServer());
}
this.registerListeners();
}
@ -140,25 +150,32 @@ class SharedProcessMain extends Disposable {
this.dispose();
};
process.once('exit', onExit);
ipcRenderer.once('vscode:electron-main->shared-process=exit', onExit);
if (isUtilityProcess(process)) {
once(process.parentPort, 'vscode:electron-main->shared-process=exit', onExit);
} else {
this.ipcRenderer!.once('vscode:electron-main->shared-process=exit', onExit);
}
// Shared process worker lifecycle
//
// We dispose the listener when the shared process is
// disposed to avoid disposing workers when the entire
// application is shutting down anyways.
//
const eventName = 'vscode:electron-main->shared-process=disposeWorker';
const onDisposeWorker = (event: unknown, configuration: IUtilityProcessWorkerConfiguration) => { this.onDisposeWorker(configuration); };
ipcRenderer.on(eventName, onDisposeWorker);
this._register(toDisposable(() => ipcRenderer.removeListener(eventName, onDisposeWorker)));
if (!isUtilityProcess(process)) {
// Shared process worker lifecycle
//
// We dispose the listener when the shared process is
// disposed to avoid disposing workers when the entire
// application is shutting down anyways.
const eventName = 'vscode:electron-main->shared-process=disposeWorker';
const onDisposeWorker = (event: unknown, configuration: IUtilityProcessWorkerConfiguration) => { this.onDisposeWorker(configuration); };
this.ipcRenderer!.on(eventName, onDisposeWorker);
this._register(toDisposable(() => this.ipcRenderer!.removeListener(eventName, onDisposeWorker)));
}
}
private onDisposeWorker(configuration: IUtilityProcessWorkerConfiguration): void {
this.sharedProcessWorkerService?.disposeWorker(configuration);
}
async open(): Promise<void> {
async init(): Promise<void> {
// Services
const instantiationService = await this.initServices();
@ -249,7 +266,7 @@ class SharedProcessMain extends Disposable {
fileService.registerProvider(Schemas.vscodeUserData, userDataFileSystemProvider);
// User Data Profiles
const userDataProfilesService = this._register(new UserDataProfilesNativeService(this.configuration.profiles, mainProcessService, environmentService));
const userDataProfilesService = this._register(new UserDataProfilesService(this.configuration.profiles.all, URI.revive(this.configuration.profiles.home), mainProcessService.getChannel('userDataProfiles')));
services.set(IUserDataProfilesService, userDataProfilesService);
// Configuration
@ -272,7 +289,7 @@ class SharedProcessMain extends Disposable {
services.set(IUriIdentityService, uriIdentityService);
// Request
services.set(IRequestService, new SharedProcessRequestService(mainProcessService, configurationService, productService, logService));
services.set(IRequestService, new SharedProcessRequestService(mainProcessService, configurationService, logService));
// Checksum
services.set(IChecksumService, new SyncDescriptor(ChecksumService, undefined, false /* proxied to other processes */));
@ -456,15 +473,20 @@ class SharedProcessMain extends Disposable {
private registerErrorHandler(logService: ILogService): void {
// Listen on unhandled rejection events
window.addEventListener('unhandledrejection', (event: PromiseRejectionEvent) => {
// Listen on global error events
if (isUtilityProcess(process)) {
process.on('uncaughtException', error => onUnexpectedError(error));
process.on('unhandledRejection', (reason: unknown) => onUnexpectedError(reason));
} else {
(globalThis as any).addEventListener('unhandledrejection', (event: any) => {
// See https://developer.mozilla.org/en-US/docs/Web/API/PromiseRejectionEvent
onUnexpectedError(event.reason);
// See https://developer.mozilla.org/en-US/docs/Web/API/PromiseRejectionEvent
onUnexpectedError(event.reason);
// Prevent the printing of this event to the console
event.preventDefault();
});
// Prevent the printing of this event to the console
event.preventDefault();
});
}
// Install handler for unexpected errors
setUnexpectedErrorHandler(error => {
@ -482,10 +504,32 @@ export async function main(configuration: ISharedProcessConfiguration): Promise<
// create shared process and signal back to main that we are
// ready to accept message ports as client connections
const sharedProcess = new SharedProcessMain(configuration);
ipcRenderer.send('vscode:shared-process->electron-main=ipc-ready');
let ipcRenderer: typeof import('electron').ipcRenderer | undefined = undefined;
if (!isUtilityProcess(process)) {
ipcRenderer = (await import('electron')).ipcRenderer;
}
const sharedProcess = new SharedProcessMain(configuration, ipcRenderer);
if (isUtilityProcess(process)) {
process.parentPort.postMessage('vscode:shared-process->electron-main=ipc-ready');
} else {
ipcRenderer!.send('vscode:shared-process->electron-main=ipc-ready');
}
// await initialization and signal this back to electron-main
await sharedProcess.open();
ipcRenderer.send('vscode:shared-process->electron-main=init-done');
await sharedProcess.init();
if (isUtilityProcess(process)) {
process.parentPort.postMessage('vscode:shared-process->electron-main=init-done');
} else {
ipcRenderer!.send('vscode:shared-process->electron-main=init-done');
}
}
if (isUtilityProcess(process)) {
process.parentPort.once('message', (e: Electron.MessageEvent) => {
main(e.data as ISharedProcessConfiguration);
});
}

View file

@ -86,6 +86,7 @@ export class EditorConfiguration extends Disposable implements IEditorConfigurat
this._register(FontMeasurements.onDidChange(() => this._recomputeOptions()));
this._register(browser.PixelRatio.onDidChange(() => this._recomputeOptions()));
this._register(this._accessibilityService.onDidChangeScreenReaderOptimized(() => this._recomputeOptions()));
TabFocus.setTabFocusMode(this.options.get(EditorOption.tabFocusMode));
}
private _recomputeOptions(): void {

View file

@ -863,7 +863,8 @@ export interface ICodeEditor extends editorCommon.IEditor {
/**
* All decorations added through this call will get the ownerId of this editor.
* @deprecated
* @deprecated Use `createDecorationsCollection`
* @see createDecorationsCollection
*/
deltaDecorations(oldDecorations: string[], newDecorations: IModelDeltaDecoration[]): string[];

View file

@ -25,10 +25,9 @@ import { IDisposable } from 'vs/base/common/lifecycle';
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
import { ILogService } from 'vs/platform/log/common/log';
export type ServicesAccessor = InstantiationServicesAccessor;
export type IEditorContributionCtor = IConstructorSignature<IEditorContribution, [ICodeEditor]>;
export type IDiffEditorContributionCtor = IConstructorSignature<IDiffEditorContribution, [IDiffEditor]>;
export type EditorContributionCtor = IConstructorSignature<IEditorContribution, [ICodeEditor]>;
export type DiffEditorContributionCtor = IConstructorSignature<IDiffEditorContribution, [IDiffEditor]>;
export const enum EditorContributionInstantiation {
/**
@ -65,13 +64,13 @@ export const enum EditorContributionInstantiation {
export interface IEditorContributionDescription {
readonly id: string;
readonly ctor: IEditorContributionCtor;
readonly ctor: EditorContributionCtor;
readonly instantiation: EditorContributionInstantiation;
}
export interface IDiffEditorContributionDescription {
id: string;
ctor: IDiffEditorContributionCtor;
ctor: DiffEditorContributionCtor;
}
//#region Command
@ -515,10 +514,18 @@ export function registerInstantiatedEditorAction(editorAction: EditorAction): vo
EditorContributionRegistry.INSTANCE.registerEditorAction(editorAction);
}
/**
* Registers an editor contribution. Editor contributions have a lifecycle which is bound
* to a specific code editor instance.
*/
export function registerEditorContribution<Services extends BrandedService[]>(id: string, ctor: { new(editor: ICodeEditor, ...services: Services): IEditorContribution }, instantiation: EditorContributionInstantiation): void {
EditorContributionRegistry.INSTANCE.registerEditorContribution(id, ctor, instantiation);
}
/**
* Registers a diff editor contribution. Diff editor contributions have a lifecycle which
* is bound to a specific diff editor instance.
*/
export function registerDiffEditorContribution<Services extends BrandedService[]>(id: string, ctor: { new(editor: IDiffEditor, ...services: Services): IEditorContribution }): void {
EditorContributionRegistry.INSTANCE.registerDiffEditorContribution(id, ctor);
}
@ -555,20 +562,16 @@ class EditorContributionRegistry {
public static readonly INSTANCE = new EditorContributionRegistry();
private readonly editorContributions: IEditorContributionDescription[];
private readonly diffEditorContributions: IDiffEditorContributionDescription[];
private readonly editorActions: EditorAction[];
private readonly editorCommands: { [commandId: string]: EditorCommand };
private readonly editorContributions: IEditorContributionDescription[] = [];
private readonly diffEditorContributions: IDiffEditorContributionDescription[] = [];
private readonly editorActions: EditorAction[] = [];
private readonly editorCommands: { [commandId: string]: EditorCommand } = Object.create(null);
constructor() {
this.editorContributions = [];
this.diffEditorContributions = [];
this.editorActions = [];
this.editorCommands = Object.create(null);
}
public registerEditorContribution<Services extends BrandedService[]>(id: string, ctor: { new(editor: ICodeEditor, ...services: Services): IEditorContribution }, instantiation: EditorContributionInstantiation): void {
this.editorContributions.push({ id, ctor: ctor as IEditorContributionCtor, instantiation });
this.editorContributions.push({ id, ctor: ctor as EditorContributionCtor, instantiation });
}
public getEditorContributions(): IEditorContributionDescription[] {
@ -576,7 +579,7 @@ class EditorContributionRegistry {
}
public registerDiffEditorContribution<Services extends BrandedService[]>(id: string, ctor: { new(editor: IDiffEditor, ...services: Services): IEditorContribution }): void {
this.diffEditorContributions.push({ id, ctor: ctor as IDiffEditorContributionCtor });
this.diffEditorContributions.push({ id, ctor: ctor as DiffEditorContributionCtor });
}
public getDiffEditorContributions(): IDiffEditorContributionDescription[] {

View file

@ -21,12 +21,18 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC
declare readonly _serviceBrand: undefined;
private readonly _onWillCreateCodeEditor = this._register(new Emitter<void>());
public readonly onWillCreateCodeEditor = this._onWillCreateCodeEditor.event;
private readonly _onCodeEditorAdd: Emitter<ICodeEditor> = this._register(new Emitter<ICodeEditor>());
public readonly onCodeEditorAdd: Event<ICodeEditor> = this._onCodeEditorAdd.event;
private readonly _onCodeEditorRemove: Emitter<ICodeEditor> = this._register(new Emitter<ICodeEditor>());
public readonly onCodeEditorRemove: Event<ICodeEditor> = this._onCodeEditorRemove.event;
private readonly _onWillCreateDiffEditor = this._register(new Emitter<void>());
public readonly onWillCreateDiffEditor = this._onWillCreateDiffEditor.event;
private readonly _onDiffEditorAdd: Emitter<IDiffEditor> = this._register(new Emitter<IDiffEditor>());
public readonly onDiffEditorAdd: Event<IDiffEditor> = this._onDiffEditorAdd.event;
@ -55,6 +61,10 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC
this._globalStyleSheet = null;
}
willCreateCodeEditor(): void {
this._onWillCreateCodeEditor.fire();
}
addCodeEditor(editor: ICodeEditor): void {
this._codeEditors[editor.getId()] = editor;
this._onCodeEditorAdd.fire(editor);
@ -70,6 +80,10 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC
return Object.keys(this._codeEditors).map(id => this._codeEditors[id]);
}
willCreateDiffEditor(): void {
this._onWillCreateDiffEditor.fire();
}
addDiffEditor(editor: IDiffEditor): void {
this._diffEditors[editor.getId()] = editor;
this._onDiffEditorAdd.fire(editor);

View file

@ -17,20 +17,23 @@ export const ICodeEditorService = createDecorator<ICodeEditorService>('codeEdito
export interface ICodeEditorService {
readonly _serviceBrand: undefined;
readonly onWillCreateCodeEditor: Event<void>;
readonly onCodeEditorAdd: Event<ICodeEditor>;
readonly onCodeEditorRemove: Event<ICodeEditor>;
readonly onWillCreateDiffEditor: Event<void>;
readonly onDiffEditorAdd: Event<IDiffEditor>;
readonly onDiffEditorRemove: Event<IDiffEditor>;
readonly onDidChangeTransientModelProperty: Event<ITextModel>;
readonly onDecorationTypeRegistered: Event<string>;
willCreateCodeEditor(): void;
addCodeEditor(editor: ICodeEditor): void;
removeCodeEditor(editor: ICodeEditor): void;
listCodeEditors(): readonly ICodeEditor[];
willCreateDiffEditor(): void;
addDiffEditor(editor: IDiffEditor): void;
removeDiffEditor(editor: IDiffEditor): void;
listDiffEditors(): readonly IDiffEditor[];

View file

@ -284,6 +284,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
@ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService,
) {
super();
codeEditorService.willCreateCodeEditor();
const options = { ..._options };

View file

@ -252,6 +252,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
@IEditorProgressService private readonly _editorProgressService: IEditorProgressService
) {
super();
codeEditorService.willCreateDiffEditor();
this._documentDiffProvider = this._register(instantiationService.createInstance(WorkerBasedDocumentDiffProvider, options));
this._register(this._documentDiffProvider.onDidChange(e => this._beginUpdateDecorationsSoon()));

View file

@ -698,6 +698,11 @@ export interface IEditorOptions {
* When enabled, this shows a preview of the drop location and triggers an `onDropIntoEditor` event.
*/
dropIntoEditor?: IDropIntoEditorOptions;
/**
* Controls whether the editor receives tabs or defers them to the workbench for navigation.
*/
tabFocusMode?: boolean;
}
/**
@ -4569,22 +4574,6 @@ class SmartSelect extends BaseEditorOption<EditorOption.smartSelect, ISmartSelec
//#endregion
//#region tabFocusMode
class EditorTabFocusMode extends ComputedEditorOption<EditorOption.tabFocusMode, boolean> {
constructor() {
super(EditorOption.tabFocusMode);
}
public compute(env: IEnvironmentalOptions, options: IComputedEditorOptions, _: boolean): boolean {
const readOnly = options.get(EditorOption.readOnly);
return (readOnly ? true : env.tabFocusMode);
}
}
//#endregion
//#region wrappingIndent
/**
@ -5619,7 +5608,9 @@ export const EditorOptions = {
// Leave these at the end (because they have dependencies!)
editorClassName: register(new EditorClassName()),
pixelRatio: register(new EditorPixelRatio()),
tabFocusMode: register(new EditorTabFocusMode()),
tabFocusMode: register(new EditorBooleanOption(EditorOption.tabFocusMode, 'tabFocusMode', false,
{ markdownDescription: nls.localize('tabFocusMode', "Controls whether the editor receives tabs or defers them to the workbench for navigation.") }
)),
layoutInfo: register(new EditorLayoutInfoComputer()),
wrappingInfo: register(new EditorWrappingInfoComputer()),
wrappingIndent: register(new WrappingIndentOption()),

View file

@ -0,0 +1,29 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { BrandedService, IConstructorSignature } from 'vs/platform/instantiation/common/instantiation';
/**
* A feature that will be loaded when the first code editor is constructed and disposed when the system shuts down.
*/
export interface IEditorFeature {
// Marker Interface
}
export type EditorFeatureCtor = IConstructorSignature<IEditorFeature>;
const editorFeatures: EditorFeatureCtor[] = [];
/**
* Registers an editor feature. Editor features will be instantiated only once, as soon as
* the first code editor is instantiated.
*/
export function registerEditorFeature<Services extends BrandedService[]>(ctor: { new(...services: Services): IEditorFeature }): void {
editorFeatures.push(ctor as EditorFeatureCtor);
}
export function getEditorFeatures(): Iterable<EditorFeatureCtor> {
return editorFeatures.slice(0);
}

View file

@ -78,6 +78,9 @@ export class EncodedTokenizationResult {
}
}
/**
* @internal
*/
export interface IBackgroundTokenizer extends IDisposable {
/**
* Instructs the background tokenizer to set the tokens for the given range again.

View file

@ -176,8 +176,8 @@ export class TokenizationStateStore {
}
}
isTokenizationComplete(_textModel: ITextModel): boolean {
return this.invalidLineStartIndex >= _textModel.getLineCount();
isTokenizationComplete(textModel: ITextModel): boolean {
return this.invalidLineStartIndex >= textModel.getLineCount();
}
}
@ -264,8 +264,6 @@ export class TextModelTokenization extends Disposable {
},
};
this.backgroundTokenizer.clear();
if (tokenizationSupport && tokenizationSupport.createBackgroundTokenizer) {
this.backgroundTokenizer.value = tokenizationSupport.createBackgroundTokenizer(this._textModel, b);
}
@ -273,14 +271,12 @@ export class TextModelTokenization extends Disposable {
this.backgroundTokenizer.value = this._defaultBackgroundTokenizer =
new DefaultBackgroundTokenizer(
this._textModel,
this._tokenizationStateStore!,
this._tokenizationStateStore,
b,
this._languageIdCodec
);
this._defaultBackgroundTokenizer.handleChanges();
}
} else {
this.backgroundTokenizer.clear();
}
}

View file

@ -5,7 +5,6 @@
import { Emitter, Event } from 'vs/base/common/event';
import { CharCode } from 'vs/base/common/charCode';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IPosition, Position } from 'vs/editor/common/core/position';
import { IRange, Range } from 'vs/editor/common/core/range';
import { getWordAtText, IWordAtPosition } from 'vs/editor/common/core/wordHelper';
@ -34,7 +33,6 @@ export class TokenizationTextModelPart extends TextModelPart implements ITokeniz
private readonly _onDidChangeTokens: Emitter<IModelTokensChangedEvent> = this._register(new Emitter<IModelTokensChangedEvent>());
public readonly onDidChangeTokens: Event<IModelTokensChangedEvent> = this._onDidChangeTokens.event;
private readonly _languageRegistryListener: IDisposable;
private readonly _tokens: ContiguousTokensStore;
private readonly _semanticTokens: SparseTokensStore;
private readonly _tokenization: TextModelTokenization;
@ -54,25 +52,19 @@ export class TokenizationTextModelPart extends TextModelPart implements ITokeniz
this._semanticTokens = new SparseTokensStore(
this._languageService.languageIdCodec
);
this._tokenization = new TextModelTokenization(
this._tokenization = this._register(new TextModelTokenization(
_textModel,
this,
this._languageService.languageIdCodec
);
));
this._languageRegistryListener = this._languageConfigurationService.onDidChange(
this._register(this._languageConfigurationService.onDidChange(
e => {
if (e.affects(this._languageId)) {
this._onDidChangeLanguageConfiguration.fire({});
}
}
);
}
public override dispose(): void {
this._languageRegistryListener.dispose();
this._tokenization.dispose();
super.dispose();
));
}
_hasListeners(): boolean {

View file

@ -9,7 +9,6 @@ import { ITextBufferFactory, ITextModel, ITextModelCreationOptions } from 'vs/ed
import { ILanguageSelection } from 'vs/editor/common/languages/language';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { DocumentSemanticTokensProvider, DocumentRangeSemanticTokensProvider } from 'vs/editor/common/languages';
import { SemanticTokensProviderStyling } from 'vs/editor/common/services/semanticTokensProviderStyling';
export const IModelService = createDecorator<IModelService>('modelService');
@ -32,8 +31,6 @@ export interface IModelService {
getModel(resource: URI): ITextModel | null;
getSemanticTokensProviderStyling(provider: DocumentTokensProvider): SemanticTokensProviderStyling;
onModelAdded: Event<ITextModel>;
onModelRemoved: Event<ITextModel>;

View file

@ -4,42 +4,26 @@
*--------------------------------------------------------------------------------------------*/
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable, IDisposable, DisposableStore, dispose } from 'vs/base/common/lifecycle';
import { Disposable, IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import * as platform from 'vs/base/common/platform';
import * as errors from 'vs/base/common/errors';
import { URI } from 'vs/base/common/uri';
import { EditOperation, ISingleEditOperation } from 'vs/editor/common/core/editOperation';
import { Range } from 'vs/editor/common/core/range';
import { DefaultEndOfLine, EndOfLinePreference, EndOfLineSequence, ITextBuffer, ITextBufferFactory, ITextModel, ITextModelCreationOptions } from 'vs/editor/common/model';
import { TextModel, createTextBuffer } from 'vs/editor/common/model/textModel';
import { EDITOR_MODEL_DEFAULTS } from 'vs/editor/common/core/textModelDefaults';
import { IModelLanguageChangedEvent, IModelContentChangedEvent } from 'vs/editor/common/textModelEvents';
import { DocumentSemanticTokensProvider, SemanticTokens, SemanticTokensEdits } from 'vs/editor/common/languages';
import { IModelLanguageChangedEvent } from 'vs/editor/common/textModelEvents';
import { PLAINTEXT_LANGUAGE_ID } from 'vs/editor/common/languages/modesRegistry';
import { ILanguageSelection, ILanguageService } from 'vs/editor/common/languages/language';
import { IModelService, DocumentTokensProvider } from 'vs/editor/common/services/model';
import { IModelService } from 'vs/editor/common/services/model';
import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfiguration';
import { IConfigurationChangeEvent, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { RunOnceScheduler } from 'vs/base/common/async';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { ILogService } from 'vs/platform/log/common/log';
import { IUndoRedoService, ResourceEditStackSnapshot } from 'vs/platform/undoRedo/common/undoRedo';
import { StringSHA1 } from 'vs/base/common/hash';
import { isEditStackElement } from 'vs/editor/common/model/editStack';
import { Schemas } from 'vs/base/common/network';
import { SemanticTokensProviderStyling, toMultilineTokens2 } from 'vs/editor/common/services/semanticTokensProviderStyling';
import { getDocumentSemanticTokens, hasDocumentSemanticTokensProvider, isSemanticTokens, isSemanticTokensEdits } from 'vs/editor/common/services/getSemanticTokens';
import { equals } from 'vs/base/common/objects';
import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry';
import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from 'vs/editor/common/services/languageFeatureDebounce';
import { StopWatch } from 'vs/base/common/stopwatch';
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
import { LanguageFeatureRegistry } from 'vs/editor/common/languageFeatureRegistry';
export interface IEditorSemanticHighlightingOptions {
enabled: true | false | 'configuredByTheme';
}
function MODEL_ID(resource: URI): string {
return resource.toString();
@ -153,30 +137,22 @@ export class ModelService extends Disposable implements IModelService {
private readonly _models: { [modelId: string]: ModelData };
private readonly _disposedModels: Map<string, DisposedModelInfo>;
private _disposedModelsHeapSize: number;
private readonly _semanticStyling: SemanticStyling;
constructor(
@IConfigurationService private readonly _configurationService: IConfigurationService,
@ITextResourcePropertiesService private readonly _resourcePropertiesService: ITextResourcePropertiesService,
@IThemeService private readonly _themeService: IThemeService,
@ILogService private readonly _logService: ILogService,
@IUndoRedoService private readonly _undoRedoService: IUndoRedoService,
@ILanguageService private readonly _languageService: ILanguageService,
@ILanguageConfigurationService private readonly _languageConfigurationService: ILanguageConfigurationService,
@ILanguageFeatureDebounceService private readonly _languageFeatureDebounceService: ILanguageFeatureDebounceService,
@ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService,
) {
super();
this._modelCreationOptionsByLanguageAndResource = Object.create(null);
this._models = {};
this._disposedModels = new Map<string, DisposedModelInfo>();
this._disposedModelsHeapSize = 0;
this._semanticStyling = this._register(new SemanticStyling(this._themeService, this._languageService, this._logService));
this._register(this._configurationService.onDidChangeConfiguration(e => this._updateModelOptions(e)));
this._updateModelOptions(undefined);
this._register(new SemanticColoringFeature(this._semanticStyling, this, this._themeService, this._configurationService, this._languageFeatureDebounceService, languageFeaturesService));
}
private static _readModelOptions(config: IRawConfig, isForSimpleWidget: boolean): ITextModelCreationOptions {
@ -554,10 +530,6 @@ export class ModelService extends Disposable implements IModelService {
return modelData.model;
}
public getSemanticTokensProviderStyling(provider: DocumentTokensProvider): SemanticTokensProviderStyling {
return this._semanticStyling.get(provider);
}
// --- end IModelService
protected _schemaShouldMaintainUndoRedoElements(resource: URI) {
@ -636,401 +608,3 @@ export class ModelService extends Disposable implements IModelService {
this._onModelModeChanged.fire({ model, oldLanguageId: oldLanguageId });
}
}
export const SEMANTIC_HIGHLIGHTING_SETTING_ID = 'editor.semanticHighlighting';
export function isSemanticColoringEnabled(model: ITextModel, themeService: IThemeService, configurationService: IConfigurationService): boolean {
const setting = configurationService.getValue<IEditorSemanticHighlightingOptions>(SEMANTIC_HIGHLIGHTING_SETTING_ID, { overrideIdentifier: model.getLanguageId(), resource: model.uri })?.enabled;
if (typeof setting === 'boolean') {
return setting;
}
return themeService.getColorTheme().semanticHighlighting;
}
class SemanticColoringFeature extends Disposable {
private readonly _watchers: Record<string, ModelSemanticColoring>;
private readonly _semanticStyling: SemanticStyling;
constructor(
semanticStyling: SemanticStyling,
@IModelService modelService: IModelService,
@IThemeService themeService: IThemeService,
@IConfigurationService configurationService: IConfigurationService,
@ILanguageFeatureDebounceService languageFeatureDebounceService: ILanguageFeatureDebounceService,
@ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService,
) {
super();
this._watchers = Object.create(null);
this._semanticStyling = semanticStyling;
const register = (model: ITextModel) => {
this._watchers[model.uri.toString()] = new ModelSemanticColoring(model, this._semanticStyling, themeService, languageFeatureDebounceService, languageFeaturesService);
};
const deregister = (model: ITextModel, modelSemanticColoring: ModelSemanticColoring) => {
modelSemanticColoring.dispose();
delete this._watchers[model.uri.toString()];
};
const handleSettingOrThemeChange = () => {
for (const model of modelService.getModels()) {
const curr = this._watchers[model.uri.toString()];
if (isSemanticColoringEnabled(model, themeService, configurationService)) {
if (!curr) {
register(model);
}
} else {
if (curr) {
deregister(model, curr);
}
}
}
};
this._register(modelService.onModelAdded((model) => {
if (isSemanticColoringEnabled(model, themeService, configurationService)) {
register(model);
}
}));
this._register(modelService.onModelRemoved((model) => {
const curr = this._watchers[model.uri.toString()];
if (curr) {
deregister(model, curr);
}
}));
this._register(configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration(SEMANTIC_HIGHLIGHTING_SETTING_ID)) {
handleSettingOrThemeChange();
}
}));
this._register(themeService.onDidColorThemeChange(handleSettingOrThemeChange));
}
override dispose(): void {
// Dispose all watchers
for (const watcher of Object.values(this._watchers)) {
watcher.dispose();
}
super.dispose();
}
}
class SemanticStyling extends Disposable {
private _caches: WeakMap<DocumentTokensProvider, SemanticTokensProviderStyling>;
constructor(
private readonly _themeService: IThemeService,
private readonly _languageService: ILanguageService,
private readonly _logService: ILogService
) {
super();
this._caches = new WeakMap<DocumentTokensProvider, SemanticTokensProviderStyling>();
this._register(this._themeService.onDidColorThemeChange(() => {
this._caches = new WeakMap<DocumentTokensProvider, SemanticTokensProviderStyling>();
}));
}
public get(provider: DocumentTokensProvider): SemanticTokensProviderStyling {
if (!this._caches.has(provider)) {
this._caches.set(provider, new SemanticTokensProviderStyling(provider.getLegend(), this._themeService, this._languageService, this._logService));
}
return this._caches.get(provider)!;
}
}
class SemanticTokensResponse {
constructor(
public readonly provider: DocumentSemanticTokensProvider,
public readonly resultId: string | undefined,
public readonly data: Uint32Array
) { }
public dispose(): void {
this.provider.releaseDocumentSemanticTokens(this.resultId);
}
}
class ModelSemanticColoring extends Disposable {
public static REQUEST_MIN_DELAY = 300;
public static REQUEST_MAX_DELAY = 2000;
private _isDisposed: boolean;
private readonly _model: ITextModel;
private readonly _semanticStyling: SemanticStyling;
private readonly _provider: LanguageFeatureRegistry<DocumentSemanticTokensProvider>;
private readonly _debounceInformation: IFeatureDebounceInformation;
private readonly _fetchDocumentSemanticTokens: RunOnceScheduler;
private _currentDocumentResponse: SemanticTokensResponse | null;
private _currentDocumentRequestCancellationTokenSource: CancellationTokenSource | null;
private _documentProvidersChangeListeners: IDisposable[];
private _providersChangedDuringRequest: boolean;
constructor(
model: ITextModel,
stylingProvider: SemanticStyling,
@IThemeService themeService: IThemeService,
@ILanguageFeatureDebounceService languageFeatureDebounceService: ILanguageFeatureDebounceService,
@ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService,
) {
super();
this._isDisposed = false;
this._model = model;
this._semanticStyling = stylingProvider;
this._provider = languageFeaturesService.documentSemanticTokensProvider;
this._debounceInformation = languageFeatureDebounceService.for(this._provider, 'DocumentSemanticTokens', { min: ModelSemanticColoring.REQUEST_MIN_DELAY, max: ModelSemanticColoring.REQUEST_MAX_DELAY });
this._fetchDocumentSemanticTokens = this._register(new RunOnceScheduler(() => this._fetchDocumentSemanticTokensNow(), ModelSemanticColoring.REQUEST_MIN_DELAY));
this._currentDocumentResponse = null;
this._currentDocumentRequestCancellationTokenSource = null;
this._documentProvidersChangeListeners = [];
this._providersChangedDuringRequest = false;
this._register(this._model.onDidChangeContent(() => {
if (!this._fetchDocumentSemanticTokens.isScheduled()) {
this._fetchDocumentSemanticTokens.schedule(this._debounceInformation.get(this._model));
}
}));
this._register(this._model.onDidChangeLanguage(() => {
// clear any outstanding state
if (this._currentDocumentResponse) {
this._currentDocumentResponse.dispose();
this._currentDocumentResponse = null;
}
if (this._currentDocumentRequestCancellationTokenSource) {
this._currentDocumentRequestCancellationTokenSource.cancel();
this._currentDocumentRequestCancellationTokenSource = null;
}
this._setDocumentSemanticTokens(null, null, null, []);
this._fetchDocumentSemanticTokens.schedule(0);
}));
const bindDocumentChangeListeners = () => {
dispose(this._documentProvidersChangeListeners);
this._documentProvidersChangeListeners = [];
for (const provider of this._provider.all(model)) {
if (typeof provider.onDidChange === 'function') {
this._documentProvidersChangeListeners.push(provider.onDidChange(() => {
if (this._currentDocumentRequestCancellationTokenSource) {
// there is already a request running,
this._providersChangedDuringRequest = true;
return;
}
this._fetchDocumentSemanticTokens.schedule(0);
}));
}
}
};
bindDocumentChangeListeners();
this._register(this._provider.onDidChange(() => {
bindDocumentChangeListeners();
this._fetchDocumentSemanticTokens.schedule(this._debounceInformation.get(this._model));
}));
this._register(themeService.onDidColorThemeChange(_ => {
// clear out existing tokens
this._setDocumentSemanticTokens(null, null, null, []);
this._fetchDocumentSemanticTokens.schedule(this._debounceInformation.get(this._model));
}));
this._fetchDocumentSemanticTokens.schedule(0);
}
public override dispose(): void {
if (this._currentDocumentResponse) {
this._currentDocumentResponse.dispose();
this._currentDocumentResponse = null;
}
if (this._currentDocumentRequestCancellationTokenSource) {
this._currentDocumentRequestCancellationTokenSource.cancel();
this._currentDocumentRequestCancellationTokenSource = null;
}
this._setDocumentSemanticTokens(null, null, null, []);
this._isDisposed = true;
super.dispose();
}
private _fetchDocumentSemanticTokensNow(): void {
if (this._currentDocumentRequestCancellationTokenSource) {
// there is already a request running, let it finish...
return;
}
if (!hasDocumentSemanticTokensProvider(this._provider, this._model)) {
// there is no provider
if (this._currentDocumentResponse) {
// there are semantic tokens set
this._model.tokenization.setSemanticTokens(null, false);
}
return;
}
const cancellationTokenSource = new CancellationTokenSource();
const lastProvider = this._currentDocumentResponse ? this._currentDocumentResponse.provider : null;
const lastResultId = this._currentDocumentResponse ? this._currentDocumentResponse.resultId || null : null;
const request = getDocumentSemanticTokens(this._provider, this._model, lastProvider, lastResultId, cancellationTokenSource.token);
this._currentDocumentRequestCancellationTokenSource = cancellationTokenSource;
this._providersChangedDuringRequest = false;
const pendingChanges: IModelContentChangedEvent[] = [];
const contentChangeListener = this._model.onDidChangeContent((e) => {
pendingChanges.push(e);
});
const sw = new StopWatch(false);
request.then((res) => {
this._debounceInformation.update(this._model, sw.elapsed());
this._currentDocumentRequestCancellationTokenSource = null;
contentChangeListener.dispose();
if (!res) {
this._setDocumentSemanticTokens(null, null, null, pendingChanges);
} else {
const { provider, tokens } = res;
const styling = this._semanticStyling.get(provider);
this._setDocumentSemanticTokens(provider, tokens || null, styling, pendingChanges);
}
}, (err) => {
const isExpectedError = err && (errors.isCancellationError(err) || (typeof err.message === 'string' && err.message.indexOf('busy') !== -1));
if (!isExpectedError) {
errors.onUnexpectedError(err);
}
// Semantic tokens eats up all errors and considers errors to mean that the result is temporarily not available
// The API does not have a special error kind to express this...
this._currentDocumentRequestCancellationTokenSource = null;
contentChangeListener.dispose();
if (pendingChanges.length > 0 || this._providersChangedDuringRequest) {
// More changes occurred while the request was running
if (!this._fetchDocumentSemanticTokens.isScheduled()) {
this._fetchDocumentSemanticTokens.schedule(this._debounceInformation.get(this._model));
}
}
});
}
private static _copy(src: Uint32Array, srcOffset: number, dest: Uint32Array, destOffset: number, length: number): void {
// protect against overflows
length = Math.min(length, dest.length - destOffset, src.length - srcOffset);
for (let i = 0; i < length; i++) {
dest[destOffset + i] = src[srcOffset + i];
}
}
private _setDocumentSemanticTokens(provider: DocumentSemanticTokensProvider | null, tokens: SemanticTokens | SemanticTokensEdits | null, styling: SemanticTokensProviderStyling | null, pendingChanges: IModelContentChangedEvent[]): void {
const currentResponse = this._currentDocumentResponse;
const rescheduleIfNeeded = () => {
if ((pendingChanges.length > 0 || this._providersChangedDuringRequest) && !this._fetchDocumentSemanticTokens.isScheduled()) {
this._fetchDocumentSemanticTokens.schedule(this._debounceInformation.get(this._model));
}
};
if (this._currentDocumentResponse) {
this._currentDocumentResponse.dispose();
this._currentDocumentResponse = null;
}
if (this._isDisposed) {
// disposed!
if (provider && tokens) {
provider.releaseDocumentSemanticTokens(tokens.resultId);
}
return;
}
if (!provider || !styling) {
this._model.tokenization.setSemanticTokens(null, false);
return;
}
if (!tokens) {
this._model.tokenization.setSemanticTokens(null, true);
rescheduleIfNeeded();
return;
}
if (isSemanticTokensEdits(tokens)) {
if (!currentResponse) {
// not possible!
this._model.tokenization.setSemanticTokens(null, true);
return;
}
if (tokens.edits.length === 0) {
// nothing to do!
tokens = {
resultId: tokens.resultId,
data: currentResponse.data
};
} else {
let deltaLength = 0;
for (const edit of tokens.edits) {
deltaLength += (edit.data ? edit.data.length : 0) - edit.deleteCount;
}
const srcData = currentResponse.data;
const destData = new Uint32Array(srcData.length + deltaLength);
let srcLastStart = srcData.length;
let destLastStart = destData.length;
for (let i = tokens.edits.length - 1; i >= 0; i--) {
const edit = tokens.edits[i];
if (edit.start > srcData.length) {
styling.warnInvalidEditStart(currentResponse.resultId, tokens.resultId, i, edit.start, srcData.length);
// The edits are invalid and there's no way to recover
this._model.tokenization.setSemanticTokens(null, true);
return;
}
const copyCount = srcLastStart - (edit.start + edit.deleteCount);
if (copyCount > 0) {
ModelSemanticColoring._copy(srcData, srcLastStart - copyCount, destData, destLastStart - copyCount, copyCount);
destLastStart -= copyCount;
}
if (edit.data) {
ModelSemanticColoring._copy(edit.data, 0, destData, destLastStart - edit.data.length, edit.data.length);
destLastStart -= edit.data.length;
}
srcLastStart = edit.start;
}
if (srcLastStart > 0) {
ModelSemanticColoring._copy(srcData, 0, destData, 0, srcLastStart);
}
tokens = {
resultId: tokens.resultId,
data: destData
};
}
}
if (isSemanticTokens(tokens)) {
this._currentDocumentResponse = new SemanticTokensResponse(provider, tokens.resultId, tokens.data);
const result = toMultilineTokens2(tokens, styling, this._model.getLanguageId());
// Adjust incoming semantic tokens
if (pendingChanges.length > 0) {
// More changes occurred while the request was running
// We need to:
// 1. Adjust incoming semantic tokens
// 2. Request them again
for (const change of pendingChanges) {
for (const area of result) {
for (const singleChange of change.changes) {
area.applyEdit(singleChange.range, singleChange.text);
}
}
}
}
this._model.tokenization.setSemanticTokens(result, true);
} else {
this._model.tokenization.setSemanticTokens(null, true);
}
rescheduleIfNeeded();
}
}

View file

@ -0,0 +1,18 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { DocumentSemanticTokensProvider, DocumentRangeSemanticTokensProvider } from 'vs/editor/common/languages';
import { SemanticTokensProviderStyling } from 'vs/editor/common/services/semanticTokensProviderStyling';
export const ISemanticTokensStylingService = createDecorator<ISemanticTokensStylingService>('semanticTokensStylingService');
export type DocumentTokensProvider = DocumentSemanticTokensProvider | DocumentRangeSemanticTokensProvider;
export interface ISemanticTokensStylingService {
readonly _serviceBrand: undefined;
getStyling(provider: DocumentTokensProvider): SemanticTokensProviderStyling;
}

View file

@ -0,0 +1,41 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable } from 'vs/base/common/lifecycle';
import { ILanguageService } from 'vs/editor/common/languages/language';
import { DocumentTokensProvider } from 'vs/editor/common/services/model';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { ILogService } from 'vs/platform/log/common/log';
import { SemanticTokensProviderStyling } from 'vs/editor/common/services/semanticTokensProviderStyling';
import { ISemanticTokensStylingService } from 'vs/editor/common/services/semanticTokensStyling';
import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions';
export class SemanticTokensStylingService extends Disposable implements ISemanticTokensStylingService {
public _serviceBrand: undefined;
private _caches: WeakMap<DocumentTokensProvider, SemanticTokensProviderStyling>;
constructor(
@IThemeService private readonly _themeService: IThemeService,
@ILogService private readonly _logService: ILogService,
@ILanguageService private readonly _languageService: ILanguageService,
) {
super();
this._caches = new WeakMap<DocumentTokensProvider, SemanticTokensProviderStyling>();
this._register(this._themeService.onDidColorThemeChange(() => {
this._caches = new WeakMap<DocumentTokensProvider, SemanticTokensProviderStyling>();
}));
}
public getStyling(provider: DocumentTokensProvider): SemanticTokensProviderStyling {
if (!this._caches.has(provider)) {
this._caches.set(provider, new SemanticTokensProviderStyling(provider.getLegend(), this._themeService, this._languageService, this._logService));
}
return this._caches.get(provider)!;
}
}
registerSingleton(ISemanticTokensStylingService, SemanticTokensStylingService, InstantiationType.Delayed);

View file

@ -40,7 +40,6 @@ class FormatOnType implements IEditorContribution {
@ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService,
@IEditorWorkerService private readonly _workerService: IEditorWorkerService
) {
this._disposables.add(_languageFeaturesService.onTypeFormattingEditProvider.onDidChange(this._update, this));
this._disposables.add(_editor.onDidChangeModel(() => this._update()));
this._disposables.add(_editor.onDidChangeModelLanguage(() => this._update()));
@ -49,6 +48,7 @@ class FormatOnType implements IEditorContribution {
this._update();
}
}));
this._update();
}
dispose(): void {

View file

@ -112,6 +112,7 @@ export abstract class PeekViewWidget extends ZoneWidget {
private disposed?: true;
protected _headElement?: HTMLDivElement;
protected _titleElement?: HTMLDivElement;
protected _primaryHeading?: HTMLElement;
protected _secondaryHeading?: HTMLElement;
protected _metaHeading?: HTMLElement;
@ -180,18 +181,18 @@ export abstract class PeekViewWidget extends ZoneWidget {
}
protected _fillHead(container: HTMLElement, noCloseAction?: boolean): void {
const titleElement = dom.$('.peekview-title');
this._titleElement = dom.$('.peekview-title');
if ((this.options as IPeekViewOptions).supportOnTitleClick) {
titleElement.classList.add('clickable');
dom.addStandardDisposableListener(titleElement, 'click', event => this._onTitleClick(event));
this._titleElement.classList.add('clickable');
dom.addStandardDisposableListener(this._titleElement, 'click', event => this._onTitleClick(event));
}
dom.append(this._headElement!, titleElement);
dom.append(this._headElement!, this._titleElement);
this._fillTitleIcon(titleElement);
this._fillTitleIcon(this._titleElement);
this._primaryHeading = dom.$('span.filename');
this._secondaryHeading = dom.$('span.dirname');
this._metaHeading = dom.$('span.meta');
dom.append(titleElement, this._primaryHeading, this._secondaryHeading, this._metaHeading);
dom.append(this._titleElement, this._primaryHeading, this._secondaryHeading, this._metaHeading);
const actionsContainer = dom.$('.peekview-actions');
dom.append(this._headElement!, actionsContainer);

View file

@ -0,0 +1,386 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import * as errors from 'vs/base/common/errors';
import { ITextModel } from 'vs/editor/common/model';
import { IModelContentChangedEvent } from 'vs/editor/common/textModelEvents';
import { DocumentSemanticTokensProvider, SemanticTokens, SemanticTokensEdits } from 'vs/editor/common/languages';
import { IModelService } from 'vs/editor/common/services/model';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { RunOnceScheduler } from 'vs/base/common/async';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { SemanticTokensProviderStyling, toMultilineTokens2 } from 'vs/editor/common/services/semanticTokensProviderStyling';
import { getDocumentSemanticTokens, hasDocumentSemanticTokensProvider, isSemanticTokens, isSemanticTokensEdits } from 'vs/editor/contrib/semanticTokens/common/getSemanticTokens';
import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from 'vs/editor/common/services/languageFeatureDebounce';
import { StopWatch } from 'vs/base/common/stopwatch';
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
import { LanguageFeatureRegistry } from 'vs/editor/common/languageFeatureRegistry';
import { ISemanticTokensStylingService } from 'vs/editor/common/services/semanticTokensStyling';
import { registerEditorFeature } from 'vs/editor/common/editorFeatures';
import { SEMANTIC_HIGHLIGHTING_SETTING_ID, isSemanticColoringEnabled } from 'vs/editor/contrib/semanticTokens/common/semanticTokensConfig';
export class DocumentSemanticTokensFeature extends Disposable {
private readonly _watchers: Record<string, ModelSemanticColoring>;
constructor(
@ISemanticTokensStylingService semanticTokensStylingService: ISemanticTokensStylingService,
@IModelService modelService: IModelService,
@IThemeService themeService: IThemeService,
@IConfigurationService configurationService: IConfigurationService,
@ILanguageFeatureDebounceService languageFeatureDebounceService: ILanguageFeatureDebounceService,
@ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService,
) {
super();
this._watchers = Object.create(null);
const register = (model: ITextModel) => {
this._watchers[model.uri.toString()] = new ModelSemanticColoring(model, semanticTokensStylingService, themeService, languageFeatureDebounceService, languageFeaturesService);
};
const deregister = (model: ITextModel, modelSemanticColoring: ModelSemanticColoring) => {
modelSemanticColoring.dispose();
delete this._watchers[model.uri.toString()];
};
const handleSettingOrThemeChange = () => {
for (const model of modelService.getModels()) {
const curr = this._watchers[model.uri.toString()];
if (isSemanticColoringEnabled(model, themeService, configurationService)) {
if (!curr) {
register(model);
}
} else {
if (curr) {
deregister(model, curr);
}
}
}
};
this._register(modelService.onModelAdded((model) => {
if (isSemanticColoringEnabled(model, themeService, configurationService)) {
register(model);
}
}));
this._register(modelService.onModelRemoved((model) => {
const curr = this._watchers[model.uri.toString()];
if (curr) {
deregister(model, curr);
}
}));
this._register(configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration(SEMANTIC_HIGHLIGHTING_SETTING_ID)) {
handleSettingOrThemeChange();
}
}));
this._register(themeService.onDidColorThemeChange(handleSettingOrThemeChange));
}
override dispose(): void {
// Dispose all watchers
for (const watcher of Object.values(this._watchers)) {
watcher.dispose();
}
super.dispose();
}
}
class ModelSemanticColoring extends Disposable {
public static REQUEST_MIN_DELAY = 300;
public static REQUEST_MAX_DELAY = 2000;
private _isDisposed: boolean;
private readonly _model: ITextModel;
private readonly _provider: LanguageFeatureRegistry<DocumentSemanticTokensProvider>;
private readonly _debounceInformation: IFeatureDebounceInformation;
private readonly _fetchDocumentSemanticTokens: RunOnceScheduler;
private _currentDocumentResponse: SemanticTokensResponse | null;
private _currentDocumentRequestCancellationTokenSource: CancellationTokenSource | null;
private _documentProvidersChangeListeners: IDisposable[];
private _providersChangedDuringRequest: boolean;
constructor(
model: ITextModel,
@ISemanticTokensStylingService private readonly _semanticTokensStylingService: ISemanticTokensStylingService,
@IThemeService themeService: IThemeService,
@ILanguageFeatureDebounceService languageFeatureDebounceService: ILanguageFeatureDebounceService,
@ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService,
) {
super();
this._isDisposed = false;
this._model = model;
this._provider = languageFeaturesService.documentSemanticTokensProvider;
this._debounceInformation = languageFeatureDebounceService.for(this._provider, 'DocumentSemanticTokens', { min: ModelSemanticColoring.REQUEST_MIN_DELAY, max: ModelSemanticColoring.REQUEST_MAX_DELAY });
this._fetchDocumentSemanticTokens = this._register(new RunOnceScheduler(() => this._fetchDocumentSemanticTokensNow(), ModelSemanticColoring.REQUEST_MIN_DELAY));
this._currentDocumentResponse = null;
this._currentDocumentRequestCancellationTokenSource = null;
this._documentProvidersChangeListeners = [];
this._providersChangedDuringRequest = false;
this._register(this._model.onDidChangeContent(() => {
if (!this._fetchDocumentSemanticTokens.isScheduled()) {
this._fetchDocumentSemanticTokens.schedule(this._debounceInformation.get(this._model));
}
}));
this._register(this._model.onDidChangeLanguage(() => {
// clear any outstanding state
if (this._currentDocumentResponse) {
this._currentDocumentResponse.dispose();
this._currentDocumentResponse = null;
}
if (this._currentDocumentRequestCancellationTokenSource) {
this._currentDocumentRequestCancellationTokenSource.cancel();
this._currentDocumentRequestCancellationTokenSource = null;
}
this._setDocumentSemanticTokens(null, null, null, []);
this._fetchDocumentSemanticTokens.schedule(0);
}));
const bindDocumentChangeListeners = () => {
dispose(this._documentProvidersChangeListeners);
this._documentProvidersChangeListeners = [];
for (const provider of this._provider.all(model)) {
if (typeof provider.onDidChange === 'function') {
this._documentProvidersChangeListeners.push(provider.onDidChange(() => {
if (this._currentDocumentRequestCancellationTokenSource) {
// there is already a request running,
this._providersChangedDuringRequest = true;
return;
}
this._fetchDocumentSemanticTokens.schedule(0);
}));
}
}
};
bindDocumentChangeListeners();
this._register(this._provider.onDidChange(() => {
bindDocumentChangeListeners();
this._fetchDocumentSemanticTokens.schedule(this._debounceInformation.get(this._model));
}));
this._register(themeService.onDidColorThemeChange(_ => {
// clear out existing tokens
this._setDocumentSemanticTokens(null, null, null, []);
this._fetchDocumentSemanticTokens.schedule(this._debounceInformation.get(this._model));
}));
this._fetchDocumentSemanticTokens.schedule(0);
}
public override dispose(): void {
if (this._currentDocumentResponse) {
this._currentDocumentResponse.dispose();
this._currentDocumentResponse = null;
}
if (this._currentDocumentRequestCancellationTokenSource) {
this._currentDocumentRequestCancellationTokenSource.cancel();
this._currentDocumentRequestCancellationTokenSource = null;
}
this._setDocumentSemanticTokens(null, null, null, []);
this._isDisposed = true;
super.dispose();
}
private _fetchDocumentSemanticTokensNow(): void {
if (this._currentDocumentRequestCancellationTokenSource) {
// there is already a request running, let it finish...
return;
}
if (!hasDocumentSemanticTokensProvider(this._provider, this._model)) {
// there is no provider
if (this._currentDocumentResponse) {
// there are semantic tokens set
this._model.tokenization.setSemanticTokens(null, false);
}
return;
}
const cancellationTokenSource = new CancellationTokenSource();
const lastProvider = this._currentDocumentResponse ? this._currentDocumentResponse.provider : null;
const lastResultId = this._currentDocumentResponse ? this._currentDocumentResponse.resultId || null : null;
const request = getDocumentSemanticTokens(this._provider, this._model, lastProvider, lastResultId, cancellationTokenSource.token);
this._currentDocumentRequestCancellationTokenSource = cancellationTokenSource;
this._providersChangedDuringRequest = false;
const pendingChanges: IModelContentChangedEvent[] = [];
const contentChangeListener = this._model.onDidChangeContent((e) => {
pendingChanges.push(e);
});
const sw = new StopWatch(false);
request.then((res) => {
this._debounceInformation.update(this._model, sw.elapsed());
this._currentDocumentRequestCancellationTokenSource = null;
contentChangeListener.dispose();
if (!res) {
this._setDocumentSemanticTokens(null, null, null, pendingChanges);
} else {
const { provider, tokens } = res;
const styling = this._semanticTokensStylingService.getStyling(provider);
this._setDocumentSemanticTokens(provider, tokens || null, styling, pendingChanges);
}
}, (err) => {
const isExpectedError = err && (errors.isCancellationError(err) || (typeof err.message === 'string' && err.message.indexOf('busy') !== -1));
if (!isExpectedError) {
errors.onUnexpectedError(err);
}
// Semantic tokens eats up all errors and considers errors to mean that the result is temporarily not available
// The API does not have a special error kind to express this...
this._currentDocumentRequestCancellationTokenSource = null;
contentChangeListener.dispose();
if (pendingChanges.length > 0 || this._providersChangedDuringRequest) {
// More changes occurred while the request was running
if (!this._fetchDocumentSemanticTokens.isScheduled()) {
this._fetchDocumentSemanticTokens.schedule(this._debounceInformation.get(this._model));
}
}
});
}
private static _copy(src: Uint32Array, srcOffset: number, dest: Uint32Array, destOffset: number, length: number): void {
// protect against overflows
length = Math.min(length, dest.length - destOffset, src.length - srcOffset);
for (let i = 0; i < length; i++) {
dest[destOffset + i] = src[srcOffset + i];
}
}
private _setDocumentSemanticTokens(provider: DocumentSemanticTokensProvider | null, tokens: SemanticTokens | SemanticTokensEdits | null, styling: SemanticTokensProviderStyling | null, pendingChanges: IModelContentChangedEvent[]): void {
const currentResponse = this._currentDocumentResponse;
const rescheduleIfNeeded = () => {
if ((pendingChanges.length > 0 || this._providersChangedDuringRequest) && !this._fetchDocumentSemanticTokens.isScheduled()) {
this._fetchDocumentSemanticTokens.schedule(this._debounceInformation.get(this._model));
}
};
if (this._currentDocumentResponse) {
this._currentDocumentResponse.dispose();
this._currentDocumentResponse = null;
}
if (this._isDisposed) {
// disposed!
if (provider && tokens) {
provider.releaseDocumentSemanticTokens(tokens.resultId);
}
return;
}
if (!provider || !styling) {
this._model.tokenization.setSemanticTokens(null, false);
return;
}
if (!tokens) {
this._model.tokenization.setSemanticTokens(null, true);
rescheduleIfNeeded();
return;
}
if (isSemanticTokensEdits(tokens)) {
if (!currentResponse) {
// not possible!
this._model.tokenization.setSemanticTokens(null, true);
return;
}
if (tokens.edits.length === 0) {
// nothing to do!
tokens = {
resultId: tokens.resultId,
data: currentResponse.data
};
} else {
let deltaLength = 0;
for (const edit of tokens.edits) {
deltaLength += (edit.data ? edit.data.length : 0) - edit.deleteCount;
}
const srcData = currentResponse.data;
const destData = new Uint32Array(srcData.length + deltaLength);
let srcLastStart = srcData.length;
let destLastStart = destData.length;
for (let i = tokens.edits.length - 1; i >= 0; i--) {
const edit = tokens.edits[i];
if (edit.start > srcData.length) {
styling.warnInvalidEditStart(currentResponse.resultId, tokens.resultId, i, edit.start, srcData.length);
// The edits are invalid and there's no way to recover
this._model.tokenization.setSemanticTokens(null, true);
return;
}
const copyCount = srcLastStart - (edit.start + edit.deleteCount);
if (copyCount > 0) {
ModelSemanticColoring._copy(srcData, srcLastStart - copyCount, destData, destLastStart - copyCount, copyCount);
destLastStart -= copyCount;
}
if (edit.data) {
ModelSemanticColoring._copy(edit.data, 0, destData, destLastStart - edit.data.length, edit.data.length);
destLastStart -= edit.data.length;
}
srcLastStart = edit.start;
}
if (srcLastStart > 0) {
ModelSemanticColoring._copy(srcData, 0, destData, 0, srcLastStart);
}
tokens = {
resultId: tokens.resultId,
data: destData
};
}
}
if (isSemanticTokens(tokens)) {
this._currentDocumentResponse = new SemanticTokensResponse(provider, tokens.resultId, tokens.data);
const result = toMultilineTokens2(tokens, styling, this._model.getLanguageId());
// Adjust incoming semantic tokens
if (pendingChanges.length > 0) {
// More changes occurred while the request was running
// We need to:
// 1. Adjust incoming semantic tokens
// 2. Request them again
for (const change of pendingChanges) {
for (const area of result) {
for (const singleChange of change.changes) {
area.applyEdit(singleChange.range, singleChange.text);
}
}
}
}
this._model.tokenization.setSemanticTokens(result, true);
} else {
this._model.tokenization.setSemanticTokens(null, true);
}
rescheduleIfNeeded();
}
}
class SemanticTokensResponse {
constructor(
public readonly provider: DocumentSemanticTokensProvider,
public readonly resultId: string | undefined,
public readonly data: Uint32Array
) { }
public dispose(): void {
this.provider.releaseDocumentSemanticTokens(this.resultId);
}
}
registerEditorFeature(DocumentSemanticTokensFeature);

View file

@ -10,9 +10,8 @@ import { EditorContributionInstantiation, registerEditorContribution } from 'vs/
import { Range } from 'vs/editor/common/core/range';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { ITextModel } from 'vs/editor/common/model';
import { getDocumentRangeSemanticTokens, hasDocumentRangeSemanticTokensProvider } from 'vs/editor/common/services/getSemanticTokens';
import { IModelService } from 'vs/editor/common/services/model';
import { isSemanticColoringEnabled, SEMANTIC_HIGHLIGHTING_SETTING_ID } from 'vs/editor/common/services/modelService';
import { getDocumentRangeSemanticTokens, hasDocumentRangeSemanticTokensProvider } from 'vs/editor/contrib/semanticTokens/common/getSemanticTokens';
import { isSemanticColoringEnabled, SEMANTIC_HIGHLIGHTING_SETTING_ID } from 'vs/editor/contrib/semanticTokens/common/semanticTokensConfig';
import { toMultilineTokens2 } from 'vs/editor/common/services/semanticTokensProviderStyling';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IThemeService } from 'vs/platform/theme/common/themeService';
@ -21,6 +20,7 @@ import { StopWatch } from 'vs/base/common/stopwatch';
import { LanguageFeatureRegistry } from 'vs/editor/common/languageFeatureRegistry';
import { DocumentRangeSemanticTokensProvider } from 'vs/editor/common/languages';
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
import { ISemanticTokensStylingService } from 'vs/editor/common/services/semanticTokensStyling';
class ViewportSemanticTokensContribution extends Disposable implements IEditorContribution {
@ -38,7 +38,7 @@ class ViewportSemanticTokensContribution extends Disposable implements IEditorCo
constructor(
editor: ICodeEditor,
@IModelService private readonly _modelService: IModelService,
@ISemanticTokensStylingService private readonly _semanticTokensStylingService: ISemanticTokensStylingService,
@IThemeService private readonly _themeService: IThemeService,
@IConfigurationService private readonly _configurationService: IConfigurationService,
@ILanguageFeatureDebounceService languageFeatureDebounceService: ILanguageFeatureDebounceService,
@ -134,7 +134,7 @@ class ViewportSemanticTokensContribution extends Disposable implements IEditorCo
return;
}
const { provider, tokens: result } = r;
const styling = this._modelService.getSemanticTokensProviderStyling(provider);
const styling = this._semanticTokensStylingService.getStyling(provider);
model.tokenization.setPartialSemanticTokens(range, toMultilineTokens2(result, styling, model.getLanguageId()));
}).then(() => this._removeOutstandingRequest(request), () => this._removeOutstandingRequest(request));
return request;

View file

@ -0,0 +1,22 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ITextModel } from 'vs/editor/common/model';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IThemeService } from 'vs/platform/theme/common/themeService';
export const SEMANTIC_HIGHLIGHTING_SETTING_ID = 'editor.semanticHighlighting';
export interface IEditorSemanticHighlightingOptions {
enabled: true | false | 'configuredByTheme';
}
export function isSemanticColoringEnabled(model: ITextModel, themeService: IThemeService, configurationService: IConfigurationService): boolean {
const setting = configurationService.getValue<IEditorSemanticHighlightingOptions>(SEMANTIC_HIGHLIGHTING_SETTING_ID, { overrideIdentifier: model.getLanguageId(), resource: model.uri })?.enabled;
if (typeof setting === 'boolean') {
return setting;
}
return themeService.getColorTheme().semanticHighlighting;
}

View file

@ -0,0 +1,278 @@
/*---------------------------------------------------------------------------------------------
* 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 { Emitter, Event } from 'vs/base/common/event';
import { Range } from 'vs/editor/common/core/range';
import { ITextModel } from 'vs/editor/common/model';
import { ModelService } from 'vs/editor/common/services/modelService';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { TestColorTheme, TestThemeService } from 'vs/platform/theme/test/common/testThemeService';
import { NullLogService } from 'vs/platform/log/common/log';
import { UndoRedoService } from 'vs/platform/undoRedo/common/undoRedoService';
import { TestDialogService } from 'vs/platform/dialogs/test/common/testDialogService';
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { DocumentSemanticTokensProvider, SemanticTokens, SemanticTokensEdits, SemanticTokensLegend } from 'vs/editor/common/languages';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Barrier, timeout } from 'vs/base/common/async';
import { LanguageService } from 'vs/editor/common/services/languageService';
import { ColorScheme } from 'vs/platform/theme/common/theme';
import { IModelService } from 'vs/editor/common/services/model';
import { ILanguageService } from 'vs/editor/common/languages/language';
import { TestTextResourcePropertiesService } from 'vs/editor/test/common/services/testTextResourcePropertiesService';
import { TestLanguageConfigurationService } from 'vs/editor/test/common/modes/testLanguageConfigurationService';
import { getDocumentSemanticTokens, isSemanticTokens } from 'vs/editor/contrib/semanticTokens/common/getSemanticTokens';
import { LanguageFeatureDebounceService } from 'vs/editor/common/services/languageFeatureDebounce';
import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler';
import { LanguageFeaturesService } from 'vs/editor/common/services/languageFeaturesService';
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
import { SemanticTokensStylingService } from 'vs/editor/common/services/semanticTokensStylingService';
import { DocumentSemanticTokensFeature } from 'vs/editor/contrib/semanticTokens/browser/documentSemanticTokens';
suite('ModelSemanticColoring', () => {
const disposables = new DisposableStore();
let modelService: IModelService;
let languageService: ILanguageService;
let languageFeaturesService: ILanguageFeaturesService;
setup(() => {
const configService = new TestConfigurationService({ editor: { semanticHighlighting: true } });
const themeService = new TestThemeService();
themeService.setTheme(new TestColorTheme({}, ColorScheme.DARK, true));
const logService = new NullLogService();
languageFeaturesService = new LanguageFeaturesService();
languageService = disposables.add(new LanguageService(false));
const semanticTokensStylingService = disposables.add(new SemanticTokensStylingService(themeService, logService, languageService));
modelService = disposables.add(new ModelService(
configService,
new TestTextResourcePropertiesService(configService),
new UndoRedoService(new TestDialogService(), new TestNotificationService()),
languageService,
new TestLanguageConfigurationService(),
));
disposables.add(new DocumentSemanticTokensFeature(semanticTokensStylingService, modelService, themeService, configService, new LanguageFeatureDebounceService(logService), languageFeaturesService));
});
teardown(() => {
disposables.clear();
});
test('DocumentSemanticTokens should be fetched when the result is empty if there are pending changes', async () => {
await runWithFakedTimers({}, async () => {
disposables.add(languageService.registerLanguage({ id: 'testMode' }));
const inFirstCall = new Barrier();
const delayFirstResult = new Barrier();
const secondResultProvided = new Barrier();
let callCount = 0;
disposables.add(languageFeaturesService.documentSemanticTokensProvider.register('testMode', new class implements DocumentSemanticTokensProvider {
getLegend(): SemanticTokensLegend {
return { tokenTypes: ['class'], tokenModifiers: [] };
}
async provideDocumentSemanticTokens(model: ITextModel, lastResultId: string | null, token: CancellationToken): Promise<SemanticTokens | SemanticTokensEdits | null> {
callCount++;
if (callCount === 1) {
assert.ok('called once');
inFirstCall.open();
await delayFirstResult.wait();
await timeout(0); // wait for the simple scheduler to fire to check that we do actually get rescheduled
return null;
}
if (callCount === 2) {
assert.ok('called twice');
secondResultProvided.open();
return null;
}
assert.fail('Unexpected call');
}
releaseDocumentSemanticTokens(resultId: string | undefined): void {
}
}));
const textModel = disposables.add(modelService.createModel('Hello world', languageService.createById('testMode')));
// wait for the provider to be called
await inFirstCall.wait();
// the provider is now in the provide call
// change the text buffer while the provider is running
textModel.applyEdits([{ range: new Range(1, 1, 1, 1), text: 'x' }]);
// let the provider finish its first result
delayFirstResult.open();
// we need to check that the provider is called again, even if it returns null
await secondResultProvided.wait();
// assert that it got called twice
assert.strictEqual(callCount, 2);
});
});
test('issue #149412: VS Code hangs when bad semantic token data is received', async () => {
await runWithFakedTimers({}, async () => {
disposables.add(languageService.registerLanguage({ id: 'testMode' }));
let lastResult: SemanticTokens | SemanticTokensEdits | null = null;
disposables.add(languageFeaturesService.documentSemanticTokensProvider.register('testMode', new class implements DocumentSemanticTokensProvider {
getLegend(): SemanticTokensLegend {
return { tokenTypes: ['class'], tokenModifiers: [] };
}
async provideDocumentSemanticTokens(model: ITextModel, lastResultId: string | null, token: CancellationToken): Promise<SemanticTokens | SemanticTokensEdits | null> {
if (!lastResultId) {
// this is the first call
lastResult = {
resultId: '1',
data: new Uint32Array([4294967293, 0, 7, 16, 0, 1, 4, 3, 11, 1])
};
} else {
// this is the second call
lastResult = {
resultId: '2',
edits: [{
start: 4294967276,
deleteCount: 0,
data: new Uint32Array([2, 0, 3, 11, 0])
}]
};
}
return lastResult;
}
releaseDocumentSemanticTokens(resultId: string | undefined): void {
}
}));
const textModel = disposables.add(modelService.createModel('', languageService.createById('testMode')));
// wait for the semantic tokens to be fetched
await Event.toPromise(textModel.onDidChangeTokens);
assert.strictEqual(lastResult!.resultId, '1');
// edit the text
textModel.applyEdits([{ range: new Range(1, 1, 1, 1), text: 'foo' }]);
// wait for the semantic tokens to be fetched again
await Event.toPromise(textModel.onDidChangeTokens);
assert.strictEqual(lastResult!.resultId, '2');
});
});
test('issue #161573: onDidChangeSemanticTokens doesn\'t consistently trigger provideDocumentSemanticTokens', async () => {
await runWithFakedTimers({}, async () => {
disposables.add(languageService.registerLanguage({ id: 'testMode' }));
const emitter = new Emitter<void>();
let requestCount = 0;
disposables.add(languageFeaturesService.documentSemanticTokensProvider.register('testMode', new class implements DocumentSemanticTokensProvider {
onDidChange = emitter.event;
getLegend(): SemanticTokensLegend {
return { tokenTypes: ['class'], tokenModifiers: [] };
}
async provideDocumentSemanticTokens(model: ITextModel, lastResultId: string | null, token: CancellationToken): Promise<SemanticTokens | SemanticTokensEdits | null> {
requestCount++;
if (requestCount === 1) {
await timeout(1000);
// send a change event
emitter.fire();
await timeout(1000);
return null;
}
return null;
}
releaseDocumentSemanticTokens(resultId: string | undefined): void {
}
}));
disposables.add(modelService.createModel('', languageService.createById('testMode')));
await timeout(5000);
assert.deepStrictEqual(requestCount, 2);
});
});
test('DocumentSemanticTokens should be pick the token provider with actual items', async () => {
await runWithFakedTimers({}, async () => {
let callCount = 0;
disposables.add(languageService.registerLanguage({ id: 'testMode2' }));
disposables.add(languageFeaturesService.documentSemanticTokensProvider.register('testMode2', new class implements DocumentSemanticTokensProvider {
getLegend(): SemanticTokensLegend {
return { tokenTypes: ['class1'], tokenModifiers: [] };
}
async provideDocumentSemanticTokens(model: ITextModel, lastResultId: string | null, token: CancellationToken): Promise<SemanticTokens | SemanticTokensEdits | null> {
callCount++;
// For a secondary request return a different value
if (lastResultId) {
return {
data: new Uint32Array([2, 1, 1, 1, 1, 0, 2, 1, 1, 1])
};
}
return {
resultId: '1',
data: new Uint32Array([0, 1, 1, 1, 1, 0, 2, 1, 1, 1])
};
}
releaseDocumentSemanticTokens(resultId: string | undefined): void {
}
}));
disposables.add(languageFeaturesService.documentSemanticTokensProvider.register('testMode2', new class implements DocumentSemanticTokensProvider {
getLegend(): SemanticTokensLegend {
return { tokenTypes: ['class2'], tokenModifiers: [] };
}
async provideDocumentSemanticTokens(model: ITextModel, lastResultId: string | null, token: CancellationToken): Promise<SemanticTokens | SemanticTokensEdits | null> {
callCount++;
return null;
}
releaseDocumentSemanticTokens(resultId: string | undefined): void {
}
}));
function toArr(arr: Uint32Array): number[] {
const result: number[] = [];
for (let i = 0; i < arr.length; i++) {
result[i] = arr[i];
}
return result;
}
const textModel = modelService.createModel('Hello world 2', languageService.createById('testMode2'));
try {
let result = await getDocumentSemanticTokens(languageFeaturesService.documentSemanticTokensProvider, textModel, null, null, CancellationToken.None);
assert.ok(result, `We should have tokens (1)`);
assert.ok(result.tokens, `Tokens are found from multiple providers (1)`);
assert.ok(isSemanticTokens(result.tokens), `Tokens are full (1)`);
assert.ok(result.tokens.resultId, `Token result id found from multiple providers (1)`);
assert.deepStrictEqual(toArr(result.tokens.data), [0, 1, 1, 1, 1, 0, 2, 1, 1, 1], `Token data returned for multiple providers (1)`);
assert.deepStrictEqual(callCount, 2, `Called both token providers (1)`);
assert.deepStrictEqual(result.provider.getLegend(), { tokenTypes: ['class1'], tokenModifiers: [] }, `Legend matches the tokens (1)`);
// Make a second request. Make sure we get the secondary value
result = await getDocumentSemanticTokens(languageFeaturesService.documentSemanticTokensProvider, textModel, result.provider, result.tokens.resultId, CancellationToken.None);
assert.ok(result, `We should have tokens (2)`);
assert.ok(result.tokens, `Tokens are found from multiple providers (2)`);
assert.ok(isSemanticTokens(result.tokens), `Tokens are full (2)`);
assert.ok(!result.tokens.resultId, `Token result id found from multiple providers (2)`);
assert.deepStrictEqual(toArr(result.tokens.data), [2, 1, 1, 1, 1, 0, 2, 1, 1, 1], `Token data returned for multiple providers (2)`);
assert.deepStrictEqual(callCount, 4, `Called both token providers (2)`);
assert.deepStrictEqual(result.provider.getLegend(), { tokenTypes: ['class1'], tokenModifiers: [] }, `Legend matches the tokens (2)`);
} finally {
disposables.clear();
// Wait for scheduler to finish
await timeout(0);
// Now dispose the text model
textModel.dispose();
}
});
});
});

View file

@ -9,7 +9,7 @@ import { canceled } from 'vs/base/common/errors';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { ITextModel } from 'vs/editor/common/model';
import { DocumentSemanticTokensProvider, ProviderResult, SemanticTokens, SemanticTokensEdits, SemanticTokensLegend } from 'vs/editor/common/languages';
import { getDocumentSemanticTokens } from 'vs/editor/common/services/getSemanticTokens';
import { getDocumentSemanticTokens } from 'vs/editor/contrib/semanticTokens/common/getSemanticTokens';
import { createTextModel } from 'vs/editor/test/common/testTextModel';
import { LanguageFeatureRegistry } from 'vs/editor/common/languageFeatureRegistry';

View file

@ -4,36 +4,41 @@
*--------------------------------------------------------------------------------------------*/
.monaco-editor .sticky-line {
color : var(--vscode-editorLineNumber-foreground);
overflow : hidden;
white-space : nowrap;
display : inline-block;
color: var(--vscode-editorLineNumber-foreground);
overflow: hidden;
white-space: nowrap;
display: inline-block;
}
.monaco-editor .sticky-line-number {
text-align : right;
float : left;
text-align: right;
float: left;
}
.monaco-editor .sticky-line-root {
background-color : inherit;
overflow : hidden;
white-space : nowrap;
width : 100%;
background-color: inherit;
overflow: hidden;
white-space: nowrap;
width: 100%;
}
.monaco-editor.hc-black .sticky-widget,
.monaco-editor.hc-white .sticky-widget {
border-bottom: 1px solid var(--vscode-contrastBorder);
}
.monaco-editor .sticky-line-root:hover {
background-color: var(--vscode-editorStickyScrollHover-background);
cursor : pointer;
cursor: pointer;
}
.monaco-editor .sticky-widget {
width : 100%;
box-shadow : var(--vscode-scrollbar-shadow) 0 3px 2px -2px;
z-index : 11;
background-color : var(--vscode-editorStickyScroll-background);
width: 100%;
box-shadow: var(--vscode-scrollbar-shadow) 0 3px 2px -2px;
z-index: 11;
background-color: var(--vscode-editorStickyScroll-background);
}
.monaco-editor .sticky-widget.peek {
background-color : var(--vscode-peekViewEditorStickyScroll-background);
background-color: var(--vscode-peekViewEditorStickyScroll-background);
}

View file

@ -283,6 +283,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget {
}
const editorLineHeight = this._editor.getOption(EditorOption.lineHeight);
const widgetHeight: number = this._lineNumbers.length * editorLineHeight + this._lastLineRelativePosition;
this._rootDomNode.style.display = widgetHeight > 0 ? 'block' : 'none';
this._rootDomNode.style.height = widgetHeight.toString() + 'px';
const minimapSide = this._editor.getOption(EditorOption.minimap).side;
if (minimapSide === 'left') {

View file

@ -18,13 +18,14 @@ export class ToggleTabFocusModeAction extends Action2 {
constructor() {
super({
id: ToggleTabFocusModeAction.ID,
title: nls.localize({ key: 'toggle.tabMovesFocus', comment: ['Turn on/off use of tab key for moving focus around VS Code'] }, "Toggle Tab Key Moves Focus"),
title: { value: nls.localize({ key: 'toggle.tabMovesFocus', comment: ['Turn on/off use of tab key for moving focus around VS Code'] }, 'Toggle Tab Key Moves Focus'), original: 'Toggle Tab Key Moves Focus' },
precondition: undefined,
keybinding: {
primary: KeyMod.CtrlCmd | KeyCode.KeyM,
mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.KeyM },
weight: KeybindingWeight.EditorContrib
}
},
f1: true
});
}

View file

@ -42,16 +42,17 @@ import 'vs/editor/contrib/longLinesHelper/browser/longLinesHelper';
import 'vs/editor/contrib/multicursor/browser/multicursor';
import 'vs/editor/contrib/parameterHints/browser/parameterHints';
import 'vs/editor/contrib/rename/browser/rename';
import 'vs/editor/contrib/stickyScroll/browser/stickyScrollContribution';
import 'vs/editor/contrib/semanticTokens/browser/documentSemanticTokens';
import 'vs/editor/contrib/semanticTokens/browser/viewportSemanticTokens';
import 'vs/editor/contrib/smartSelect/browser/smartSelect';
import 'vs/editor/contrib/snippet/browser/snippetController2';
import 'vs/editor/contrib/stickyScroll/browser/stickyScrollContribution';
import 'vs/editor/contrib/suggest/browser/suggestController';
import 'vs/editor/contrib/suggest/browser/suggestInlineCompletions';
import 'vs/editor/contrib/tokenization/browser/tokenization';
import 'vs/editor/contrib/toggleTabFocusMode/browser/toggleTabFocusMode';
import 'vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter';
import 'vs/editor/contrib/unusualLineTerminators/browser/unusualLineTerminators';
import 'vs/editor/contrib/viewportSemanticTokens/browser/viewportSemanticTokens';
import 'vs/editor/contrib/wordHighlighter/browser/wordHighlighter';
import 'vs/editor/contrib/wordOperations/browser/wordOperations';
import 'vs/editor/contrib/wordPartOperations/browser/wordPartOperations';

View file

@ -8,6 +8,8 @@ import 'vs/editor/standalone/browser/standaloneCodeEditorService';
import 'vs/editor/standalone/browser/standaloneLayoutService';
import 'vs/platform/undoRedo/common/undoRedoService';
import 'vs/editor/common/services/languageFeatureDebounce';
import 'vs/editor/common/services/semanticTokensStylingService';
import 'vs/editor/common/services/languageFeaturesService';
import * as strings from 'vs/base/common/strings';
import * as dom from 'vs/base/browser/dom';
@ -84,12 +86,12 @@ import { MarkerService } from 'vs/platform/markers/common/markerService';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { IStorageService, InMemoryStorageService } from 'vs/platform/storage/common/storage';
import 'vs/editor/common/services/languageFeaturesService';
import { DefaultConfiguration } from 'vs/platform/configuration/common/configurations';
import { WorkspaceEdit } from 'vs/editor/common/languages';
import { AudioCue, IAudioCueService, Sound } from 'vs/platform/audioCues/browser/audioCueService';
import { LogService } from 'vs/platform/log/common/logService';
import { getEditorFeatures } from 'vs/editor/common/editorFeatures';
import { onUnexpectedError } from 'vs/base/common/errors';
class SimpleModel implements IResolvedTextEditorModel {
@ -1125,6 +1127,16 @@ export module StandaloneServices {
}
}
// Instantiate all editor features
const editorFeatures = getEditorFeatures();
for (const feature of editorFeatures) {
try {
instantiationService.createInstance(feature);
} catch (err) {
onUnexpectedError(err);
}
}
return instantiationService;
}
}

View file

@ -5,38 +5,19 @@
import * as assert from 'assert';
import { CharCode } from 'vs/base/common/charCode';
import { Emitter, Event } from 'vs/base/common/event';
import * as platform from 'vs/base/common/platform';
import { URI } from 'vs/base/common/uri';
import { EditOperation } from 'vs/editor/common/core/editOperation';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { StringBuilder } from 'vs/editor/common/core/stringBuilder';
import { DefaultEndOfLine, ITextModel } from 'vs/editor/common/model';
import { DefaultEndOfLine } from 'vs/editor/common/model';
import { createTextBuffer } from 'vs/editor/common/model/textModel';
import { ModelService } from 'vs/editor/common/services/modelService';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { TestColorTheme, TestThemeService } from 'vs/platform/theme/test/common/testThemeService';
import { NullLogService } from 'vs/platform/log/common/log';
import { UndoRedoService } from 'vs/platform/undoRedo/common/undoRedoService';
import { TestDialogService } from 'vs/platform/dialogs/test/common/testDialogService';
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
import { createModelServices, createTextModel } from 'vs/editor/test/common/testTextModel';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { DocumentSemanticTokensProvider, SemanticTokens, SemanticTokensEdits, SemanticTokensLegend } from 'vs/editor/common/languages';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Barrier, timeout } from 'vs/base/common/async';
import { LanguageService } from 'vs/editor/common/services/languageService';
import { ColorScheme } from 'vs/platform/theme/common/theme';
import { IModelService } from 'vs/editor/common/services/model';
import { ILanguageService } from 'vs/editor/common/languages/language';
import { TestTextResourcePropertiesService } from 'vs/editor/test/common/services/testTextResourcePropertiesService';
import { TestLanguageConfigurationService } from 'vs/editor/test/common/modes/testLanguageConfigurationService';
import { getDocumentSemanticTokens, isSemanticTokens } from 'vs/editor/common/services/getSemanticTokens';
import { LanguageFeatureDebounceService } from 'vs/editor/common/services/languageFeatureDebounce';
import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler';
import { LanguageFeaturesService } from 'vs/editor/common/services/languageFeaturesService';
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
@ -408,253 +389,6 @@ suite('ModelService', () => {
});
});
suite('ModelSemanticColoring', () => {
const disposables = new DisposableStore();
let modelService: IModelService;
let languageService: ILanguageService;
let languageFeaturesService: ILanguageFeaturesService;
setup(() => {
const configService = new TestConfigurationService({ editor: { semanticHighlighting: true } });
const themeService = new TestThemeService();
themeService.setTheme(new TestColorTheme({}, ColorScheme.DARK, true));
const logService = new NullLogService();
languageFeaturesService = new LanguageFeaturesService();
modelService = disposables.add(new ModelService(
configService,
new TestTextResourcePropertiesService(configService),
themeService,
logService,
new UndoRedoService(new TestDialogService(), new TestNotificationService()),
disposables.add(new LanguageService()),
new TestLanguageConfigurationService(),
new LanguageFeatureDebounceService(logService),
languageFeaturesService
));
languageService = disposables.add(new LanguageService(false));
});
teardown(() => {
disposables.clear();
});
test('DocumentSemanticTokens should be fetched when the result is empty if there are pending changes', async () => {
await runWithFakedTimers({}, async () => {
disposables.add(languageService.registerLanguage({ id: 'testMode' }));
const inFirstCall = new Barrier();
const delayFirstResult = new Barrier();
const secondResultProvided = new Barrier();
let callCount = 0;
disposables.add(languageFeaturesService.documentSemanticTokensProvider.register('testMode', new class implements DocumentSemanticTokensProvider {
getLegend(): SemanticTokensLegend {
return { tokenTypes: ['class'], tokenModifiers: [] };
}
async provideDocumentSemanticTokens(model: ITextModel, lastResultId: string | null, token: CancellationToken): Promise<SemanticTokens | SemanticTokensEdits | null> {
callCount++;
if (callCount === 1) {
assert.ok('called once');
inFirstCall.open();
await delayFirstResult.wait();
await timeout(0); // wait for the simple scheduler to fire to check that we do actually get rescheduled
return null;
}
if (callCount === 2) {
assert.ok('called twice');
secondResultProvided.open();
return null;
}
assert.fail('Unexpected call');
}
releaseDocumentSemanticTokens(resultId: string | undefined): void {
}
}));
const textModel = disposables.add(modelService.createModel('Hello world', languageService.createById('testMode')));
// wait for the provider to be called
await inFirstCall.wait();
// the provider is now in the provide call
// change the text buffer while the provider is running
textModel.applyEdits([{ range: new Range(1, 1, 1, 1), text: 'x' }]);
// let the provider finish its first result
delayFirstResult.open();
// we need to check that the provider is called again, even if it returns null
await secondResultProvided.wait();
// assert that it got called twice
assert.strictEqual(callCount, 2);
});
});
test('issue #149412: VS Code hangs when bad semantic token data is received', async () => {
await runWithFakedTimers({}, async () => {
disposables.add(languageService.registerLanguage({ id: 'testMode' }));
let lastResult: SemanticTokens | SemanticTokensEdits | null = null;
disposables.add(languageFeaturesService.documentSemanticTokensProvider.register('testMode', new class implements DocumentSemanticTokensProvider {
getLegend(): SemanticTokensLegend {
return { tokenTypes: ['class'], tokenModifiers: [] };
}
async provideDocumentSemanticTokens(model: ITextModel, lastResultId: string | null, token: CancellationToken): Promise<SemanticTokens | SemanticTokensEdits | null> {
if (!lastResultId) {
// this is the first call
lastResult = {
resultId: '1',
data: new Uint32Array([4294967293, 0, 7, 16, 0, 1, 4, 3, 11, 1])
};
} else {
// this is the second call
lastResult = {
resultId: '2',
edits: [{
start: 4294967276,
deleteCount: 0,
data: new Uint32Array([2, 0, 3, 11, 0])
}]
};
}
return lastResult;
}
releaseDocumentSemanticTokens(resultId: string | undefined): void {
}
}));
const textModel = disposables.add(modelService.createModel('', languageService.createById('testMode')));
// wait for the semantic tokens to be fetched
await Event.toPromise(textModel.onDidChangeTokens);
assert.strictEqual(lastResult!.resultId, '1');
// edit the text
textModel.applyEdits([{ range: new Range(1, 1, 1, 1), text: 'foo' }]);
// wait for the semantic tokens to be fetched again
await Event.toPromise(textModel.onDidChangeTokens);
assert.strictEqual(lastResult!.resultId, '2');
});
});
test('issue #161573: onDidChangeSemanticTokens doesn\'t consistently trigger provideDocumentSemanticTokens', async () => {
await runWithFakedTimers({}, async () => {
disposables.add(languageService.registerLanguage({ id: 'testMode' }));
const emitter = new Emitter<void>();
let requestCount = 0;
disposables.add(languageFeaturesService.documentSemanticTokensProvider.register('testMode', new class implements DocumentSemanticTokensProvider {
onDidChange = emitter.event;
getLegend(): SemanticTokensLegend {
return { tokenTypes: ['class'], tokenModifiers: [] };
}
async provideDocumentSemanticTokens(model: ITextModel, lastResultId: string | null, token: CancellationToken): Promise<SemanticTokens | SemanticTokensEdits | null> {
requestCount++;
if (requestCount === 1) {
await timeout(1000);
// send a change event
emitter.fire();
await timeout(1000);
return null;
}
return null;
}
releaseDocumentSemanticTokens(resultId: string | undefined): void {
}
}));
disposables.add(modelService.createModel('', languageService.createById('testMode')));
await timeout(5000);
assert.deepStrictEqual(requestCount, 2);
});
});
test('DocumentSemanticTokens should be pick the token provider with actual items', async () => {
await runWithFakedTimers({}, async () => {
let callCount = 0;
disposables.add(languageService.registerLanguage({ id: 'testMode2' }));
disposables.add(languageFeaturesService.documentSemanticTokensProvider.register('testMode2', new class implements DocumentSemanticTokensProvider {
getLegend(): SemanticTokensLegend {
return { tokenTypes: ['class1'], tokenModifiers: [] };
}
async provideDocumentSemanticTokens(model: ITextModel, lastResultId: string | null, token: CancellationToken): Promise<SemanticTokens | SemanticTokensEdits | null> {
callCount++;
// For a secondary request return a different value
if (lastResultId) {
return {
data: new Uint32Array([2, 1, 1, 1, 1, 0, 2, 1, 1, 1])
};
}
return {
resultId: '1',
data: new Uint32Array([0, 1, 1, 1, 1, 0, 2, 1, 1, 1])
};
}
releaseDocumentSemanticTokens(resultId: string | undefined): void {
}
}));
disposables.add(languageFeaturesService.documentSemanticTokensProvider.register('testMode2', new class implements DocumentSemanticTokensProvider {
getLegend(): SemanticTokensLegend {
return { tokenTypes: ['class2'], tokenModifiers: [] };
}
async provideDocumentSemanticTokens(model: ITextModel, lastResultId: string | null, token: CancellationToken): Promise<SemanticTokens | SemanticTokensEdits | null> {
callCount++;
return null;
}
releaseDocumentSemanticTokens(resultId: string | undefined): void {
}
}));
function toArr(arr: Uint32Array): number[] {
const result: number[] = [];
for (let i = 0; i < arr.length; i++) {
result[i] = arr[i];
}
return result;
}
const textModel = modelService.createModel('Hello world 2', languageService.createById('testMode2'));
try {
let result = await getDocumentSemanticTokens(languageFeaturesService.documentSemanticTokensProvider, textModel, null, null, CancellationToken.None);
assert.ok(result, `We should have tokens (1)`);
assert.ok(result.tokens, `Tokens are found from multiple providers (1)`);
assert.ok(isSemanticTokens(result.tokens), `Tokens are full (1)`);
assert.ok(result.tokens.resultId, `Token result id found from multiple providers (1)`);
assert.deepStrictEqual(toArr(result.tokens.data), [0, 1, 1, 1, 1, 0, 2, 1, 1, 1], `Token data returned for multiple providers (1)`);
assert.deepStrictEqual(callCount, 2, `Called both token providers (1)`);
assert.deepStrictEqual(result.provider.getLegend(), { tokenTypes: ['class1'], tokenModifiers: [] }, `Legend matches the tokens (1)`);
// Make a second request. Make sure we get the secondary value
result = await getDocumentSemanticTokens(languageFeaturesService.documentSemanticTokensProvider, textModel, result.provider, result.tokens.resultId, CancellationToken.None);
assert.ok(result, `We should have tokens (2)`);
assert.ok(result.tokens, `Tokens are found from multiple providers (2)`);
assert.ok(isSemanticTokens(result.tokens), `Tokens are full (2)`);
assert.ok(!result.tokens.resultId, `Token result id found from multiple providers (2)`);
assert.deepStrictEqual(toArr(result.tokens.data), [2, 1, 1, 1, 1, 0, 2, 1, 1, 1], `Token data returned for multiple providers (2)`);
assert.deepStrictEqual(callCount, 4, `Called both token providers (2)`);
assert.deepStrictEqual(result.provider.getLegend(), { tokenTypes: ['class1'], tokenModifiers: [] }, `Legend matches the tokens (2)`);
} finally {
disposables.clear();
// Wait for scheduler to finish
await timeout(0);
// Now dispose the text model
textModel.dispose();
}
});
});
});
function assertComputeEdits(lines1: string[], lines2: string[]): void {
const model = createTextModel(lines1.join('\n'));
const textBuffer = createTextBuffer(lines2.join('\n'), DefaultEndOfLine.LF).textBuffer;

17
src/vs/monaco.d.ts vendored
View file

@ -3666,6 +3666,10 @@ declare namespace monaco.editor {
* When enabled, this shows a preview of the drop location and triggers an `onDropIntoEditor` event.
*/
dropIntoEditor?: IDropIntoEditorOptions;
/**
* Controls whether the editor receives tabs or defers them to the workbench for navigation.
*/
tabFocusMode?: boolean;
}
export interface IDiffEditorBaseOptions {
@ -5612,7 +5616,8 @@ declare namespace monaco.editor {
getDecorationsInRange(range: Range): IModelDecoration[] | null;
/**
* All decorations added through this call will get the ownerId of this editor.
* @deprecated
* @deprecated Use `createDecorationsCollection`
* @see createDecorationsCollection
*/
deltaDecorations(oldDecorations: string[], newDecorations: IModelDeltaDecoration[]): string[];
/**
@ -6409,16 +6414,6 @@ declare namespace monaco.languages {
removeText?: number;
}
export interface IBackgroundTokenizer extends IDisposable {
/**
* Instructs the background tokenizer to set the tokens for the given range again.
*
* This might be necessary if the renderer overwrote those tokens with heuristically computed ones for some viewport,
* when the change does not even propagate to that viewport.
*/
requestTokens(startLineNumber: number, endLineNumberExclusive: number): void;
}
/**
* The state of the tokenizer between two lines.
* It is useful to store flags such as in multiline comment, etc.

View file

@ -294,6 +294,12 @@ export class AudioCue {
settingsKey: 'audioCues.taskFailed'
});
public static readonly terminalCommandFailed = AudioCue.register({
name: localize('audioCues.terminalCommandFailed', 'Terminal Command Failed'),
sound: Sound.taskFailed,
settingsKey: 'audioCues.terminalCommandFailed'
});
public static readonly terminalBell = AudioCue.register({
name: localize('audioCues.terminalBell', 'Terminal Bell'),
sound: Sound.terminalBell,

View file

@ -124,50 +124,50 @@ export abstract class ContextKeyExpr {
return ContextKeySmallerEqualsExpr.create(key, value);
}
public static deserialize(serialized: string | null | undefined, strict: boolean = false): ContextKeyExpression | undefined {
public static deserialize(serialized: string | null | undefined): ContextKeyExpression | undefined {
if (!serialized) {
return undefined;
}
return this._deserializeOrExpression(serialized, strict);
return this._deserializeOrExpression(serialized);
}
private static _deserializeOrExpression(serialized: string, strict: boolean): ContextKeyExpression | undefined {
private static _deserializeOrExpression(serialized: string): ContextKeyExpression | undefined {
const pieces = serialized.split('||');
return ContextKeyOrExpr.create(pieces.map(p => this._deserializeAndExpression(p, strict)), null, true);
return ContextKeyOrExpr.create(pieces.map(p => this._deserializeAndExpression(p)), null, true);
}
private static _deserializeAndExpression(serialized: string, strict: boolean): ContextKeyExpression | undefined {
private static _deserializeAndExpression(serialized: string): ContextKeyExpression | undefined {
const pieces = serialized.split('&&');
return ContextKeyAndExpr.create(pieces.map(p => this._deserializeOne(p, strict)), null, true);
return ContextKeyAndExpr.create(pieces.map(p => this._deserializeOne(p)), null, true);
}
private static _deserializeOne(serializedOne: string, strict: boolean): ContextKeyExpression {
private static _deserializeOne(serializedOne: string): ContextKeyExpression {
serializedOne = serializedOne.trim();
if (serializedOne.indexOf('!=') >= 0) {
const pieces = serializedOne.split('!=');
return ContextKeyNotEqualsExpr.create(pieces[0].trim(), this._deserializeValue(pieces[1], strict));
return ContextKeyNotEqualsExpr.create(pieces[0].trim(), this._deserializeValue(pieces[1]));
}
if (serializedOne.indexOf('==') >= 0) {
const pieces = serializedOne.split('==');
return ContextKeyEqualsExpr.create(pieces[0].trim(), this._deserializeValue(pieces[1], strict));
return ContextKeyEqualsExpr.create(pieces[0].trim(), this._deserializeValue(pieces[1]));
}
if (serializedOne.indexOf('=~') >= 0) {
const pieces = serializedOne.split('=~');
return ContextKeyRegexExpr.create(pieces[0].trim(), this._deserializeRegexValue(pieces[1], strict));
return ContextKeyRegexExpr.create(pieces[0].trim(), this._deserializeRegexValue(pieces[1]));
}
if (serializedOne.indexOf(' not in ') >= 0) {
if (serializedOne.indexOf(' not in ') >= 0) { // careful: this must come before `in`
const pieces = serializedOne.split(' not in ');
return ContextKeyNotInExpr.create(pieces[0].trim(), this._deserializeValue(pieces[1], strict));
return ContextKeyNotInExpr.create(pieces[0].trim(), this._deserializeValue(pieces[1]));
}
if (serializedOne.indexOf(' in ') >= 0) {
const pieces = serializedOne.split(' in ');
return ContextKeyInExpr.create(pieces[0].trim(), this._deserializeValue(pieces[1], strict));
return ContextKeyInExpr.create(pieces[0].trim(), this._deserializeValue(pieces[1]));
}
if (/^[^<=>]+>=[^<=>]+$/.test(serializedOne)) {
@ -197,7 +197,7 @@ export abstract class ContextKeyExpr {
return ContextKeyDefinedExpr.create(serializedOne);
}
private static _deserializeValue(serializedValue: string, strict: boolean): any {
private static _deserializeValue(serializedValue: string): any {
serializedValue = serializedValue.trim();
if (serializedValue === 'true') {
@ -216,25 +216,17 @@ export abstract class ContextKeyExpr {
return serializedValue;
}
private static _deserializeRegexValue(serializedValue: string, strict: boolean): RegExp | null {
private static _deserializeRegexValue(serializedValue: string): RegExp | null {
if (isFalsyOrWhitespace(serializedValue)) {
if (strict) {
throw new Error('missing regexp-value for =~-expression');
} else {
console.warn('missing regexp-value for =~-expression');
}
console.warn('missing regexp-value for =~-expression');
return null;
}
const start = serializedValue.indexOf('/');
const end = serializedValue.lastIndexOf('/');
if (start === end || start < 0 /* || to < 0 */) {
if (strict) {
throw new Error(`bad regexp-value '${serializedValue}', missing /-enclosure`);
} else {
console.warn(`bad regexp-value '${serializedValue}', missing /-enclosure`);
}
console.warn(`bad regexp-value '${serializedValue}', missing /-enclosure`);
return null;
}
@ -243,11 +235,7 @@ export abstract class ContextKeyExpr {
try {
return new RegExp(value, caseIgnoreFlag);
} catch (e) {
if (strict) {
throw new Error(`bad regexp-value '${serializedValue}', parse error: ${e}`);
} else {
console.warn(`bad regexp-value '${serializedValue}', parse error: ${e}`);
}
console.warn(`bad regexp-value '${serializedValue}', parse error: ${e}`);
return null;
}
}

View file

@ -67,6 +67,8 @@ export interface NativeParsedArgs {
'inspect-brk-search'?: string;
'inspect-ptyhost'?: string;
'inspect-brk-ptyhost'?: string;
'inspect-sharedprocess'?: string;
'inspect-brk-sharedprocess'?: string;
'disable-extensions'?: boolean;
'disable-extension'?: string[]; // undefined or array of 1 or more
'list-extensions'?: boolean;

View file

@ -11,7 +11,7 @@ import { env } from 'vs/base/common/process';
import { joinPath } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
import { ExtensionKind, IDebugParams, IExtensionHostDebugParams, INativeEnvironmentService } from 'vs/platform/environment/common/environment';
import { ExtensionKind, IExtensionHostDebugParams, INativeEnvironmentService } from 'vs/platform/environment/common/environment';
import { IProductService } from 'vs/platform/product/common/productService';
export const EXTENSION_IDENTIFIER_WITH_LOG_REGEX = /^([^.]+\..+):(.+)$/;
@ -213,7 +213,7 @@ export abstract class AbstractNativeEnvironmentService implements INativeEnviron
}
@memoize
get debugExtensionHost(): IExtensionHostDebugParams { return parseExtensionHostPort(this.args, this.isBuilt); }
get debugExtensionHost(): IExtensionHostDebugParams { return parseExtensionHostDebugPort(this.args, this.isBuilt); }
get debugRenderer(): boolean { return !!this.args.debugRenderer; }
get isBuilt(): boolean { return !env['VSCODE_DEV']; }
@ -278,17 +278,13 @@ export abstract class AbstractNativeEnvironmentService implements INativeEnviron
) { }
}
export function parseExtensionHostPort(args: NativeParsedArgs, isBuild: boolean): IExtensionHostDebugParams {
export function parseExtensionHostDebugPort(args: NativeParsedArgs, isBuild: boolean): IExtensionHostDebugParams {
return parseDebugParams(args['inspect-extensions'], args['inspect-brk-extensions'], 5870, isBuild, args.debugId, args.extensionEnvironment);
}
export function parsePtyHostPort(args: NativeParsedArgs, isBuild: boolean): IDebugParams {
return parseDebugParams(args['inspect-ptyhost'], args['inspect-brk-ptyhost'], 5877, isBuild, args.extensionEnvironment);
}
function parseDebugParams(debugArg: string | undefined, debugBrkArg: string | undefined, defaultBuildPort: number, isBuild: boolean, debugId?: string, environmentString?: string): IExtensionHostDebugParams {
export function parseDebugParams(debugArg: string | undefined, debugBrkArg: string | undefined, defaultBuildPort: number, isBuilt: boolean, debugId?: string, environmentString?: string): IExtensionHostDebugParams {
const portStr = debugBrkArg || debugArg;
const port = Number(portStr) || (!isBuild ? defaultBuildPort : null);
const port = Number(portStr) || (!isBuilt ? defaultBuildPort : null);
const brk = port ? Boolean(!!debugBrkArg) : false;
let env: Record<string, string> | undefined;
if (environmentString) {

View file

@ -128,6 +128,8 @@ export const OPTIONS: OptionDescriptions<Required<NativeParsedArgs>> = {
'inspect-brk-ptyhost': { type: 'string', allowEmptyValue: true },
'inspect-search': { type: 'string', deprecates: ['debugSearch'], allowEmptyValue: true },
'inspect-brk-search': { type: 'string', deprecates: ['debugBrkSearch'], allowEmptyValue: true },
'inspect-sharedprocess': { type: 'string', allowEmptyValue: true },
'inspect-brk-sharedprocess': { type: 'string', allowEmptyValue: true },
'export-default-configuration': { type: 'string' },
'install-source': { type: 'string' },
'enable-smoke-test-driver': { type: 'boolean' },

View file

@ -5,7 +5,8 @@
import { homedir, tmpdir } from 'os';
import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
import { AbstractNativeEnvironmentService } from 'vs/platform/environment/common/environmentService';
import { IDebugParams } from 'vs/platform/environment/common/environment';
import { AbstractNativeEnvironmentService, parseDebugParams } from 'vs/platform/environment/common/environmentService';
import { getUserDataPath } from 'vs/platform/environment/node/userDataPath';
import { IProductService } from 'vs/platform/product/common/productService';
@ -19,3 +20,11 @@ export class NativeEnvironmentService extends AbstractNativeEnvironmentService {
}, productService);
}
}
export function parsePtyHostDebugPort(args: NativeParsedArgs, isBuild: boolean): IDebugParams {
return parseDebugParams(args['inspect-ptyhost'], args['inspect-brk-ptyhost'], 5877, isBuild, args.extensionEnvironment);
}
export function parseSharedProcessDebugPort(args: NativeParsedArgs, isBuild: boolean): IDebugParams {
return parseDebugParams(args['inspect-sharedprocess'], args['inspect-brk-sharedprocess'], 5879, isBuild, args.extensionEnvironment);
}

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { parseExtensionHostPort } from 'vs/platform/environment/common/environmentService';
import { parseExtensionHostDebugPort } from 'vs/platform/environment/common/environmentService';
import { OPTIONS, parseArgs } from 'vs/platform/environment/node/argv';
import { NativeEnvironmentService } from 'vs/platform/environment/node/environmentService';
import product from 'vs/platform/product/common/product';
@ -12,7 +12,7 @@ import product from 'vs/platform/product/common/product';
suite('EnvironmentService', () => {
test('parseExtensionHostPort when built', () => {
const parse = (a: string[]) => parseExtensionHostPort(parseArgs(a, OPTIONS), true);
const parse = (a: string[]) => parseExtensionHostDebugPort(parseArgs(a, OPTIONS), true);
assert.deepStrictEqual(parse([]), { port: null, break: false, env: undefined, debugId: undefined });
assert.deepStrictEqual(parse(['--debugPluginHost']), { port: null, break: false, env: undefined, debugId: undefined });
@ -30,7 +30,7 @@ suite('EnvironmentService', () => {
});
test('parseExtensionHostPort when unbuilt', () => {
const parse = (a: string[]) => parseExtensionHostPort(parseArgs(a, OPTIONS), false);
const parse = (a: string[]) => parseExtensionHostDebugPort(parseArgs(a, OPTIONS), false);
assert.deepStrictEqual(parse([]), { port: 5870, break: false, env: undefined, debugId: undefined });
assert.deepStrictEqual(parse(['--debugPluginHost']), { port: 5870, break: false, env: undefined, debugId: undefined });

View file

@ -653,13 +653,14 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
abstract getManifest(vsix: URI): Promise<IExtensionManifest>;
abstract install(vsix: URI, options?: InstallVSIXOptions): Promise<ILocalExtension>;
abstract installFromLocation(location: URI, profileLocation: URI): Promise<ILocalExtension>;
abstract installExtensionsFromProfile(extensions: IExtensionIdentifier[], fromProfileLocation: URI, toProfileLocation: URI): Promise<ILocalExtension[]>;
abstract getInstalled(type?: ExtensionType, profileLocation?: URI): Promise<ILocalExtension[]>;
abstract copyExtensions(fromProfileLocation: URI, toProfileLocation: URI): Promise<void>;
abstract download(extension: IGalleryExtension, operation: InstallOperation): Promise<URI>;
abstract reinstallFromGallery(extension: ILocalExtension): Promise<ILocalExtension>;
abstract cleanUp(): Promise<void>;
abstract onDidUpdateExtensionMetadata: Event<ILocalExtension>;
abstract getMetadata(extension: ILocalExtension, profileLocation?: URI): Promise<Metadata | undefined>;
abstract updateMetadata(local: ILocalExtension, metadata: Partial<Metadata>, profileLocation?: URI): Promise<ILocalExtension>;
protected abstract getCurrentExtensionsManifestLocation(): URI;

View file

@ -454,12 +454,12 @@ export interface IExtensionManagementService {
canInstall(extension: IGalleryExtension): Promise<boolean>;
installFromGallery(extension: IGalleryExtension, options?: InstallOptions): Promise<ILocalExtension>;
installFromLocation(location: URI, profileLocation: URI): Promise<ILocalExtension>;
installExtensionsFromProfile(extensions: IExtensionIdentifier[], fromProfileLocation: URI, toProfileLocation: URI): Promise<ILocalExtension[]>;
uninstall(extension: ILocalExtension, options?: UninstallOptions): Promise<void>;
reinstallFromGallery(extension: ILocalExtension): Promise<ILocalExtension>;
getInstalled(type?: ExtensionType, profileLocation?: URI): Promise<ILocalExtension[]>;
getExtensionsControlManifest(): Promise<IExtensionsControlManifest>;
getMetadata(extension: ILocalExtension, profileLocation?: URI): Promise<Metadata | undefined>;
copyExtensions(fromProfileLocation: URI, toProfileLocation: URI): Promise<void>;
updateMetadata(local: ILocalExtension, metadata: Partial<Metadata>, profileLocation?: URI): Promise<ILocalExtension>;
download(extension: IGalleryExtension, operation: InstallOperation): Promise<URI>;

View file

@ -5,7 +5,6 @@
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { revive } from 'vs/base/common/marshalling';
import { cloneAndChange } from 'vs/base/common/objects';
import { URI, UriComponents } from 'vs/base/common/uri';
import { DefaultURITransformer, IURITransformer, transformAndReviveIncomingURIs } from 'vs/base/common/uriIpc';
@ -13,8 +12,10 @@ import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
import { IExtensionIdentifier, IExtensionTipsService, IGalleryExtension, ILocalExtension, IExtensionsControlManifest, isTargetPlatformCompatible, InstallOptions, InstallVSIXOptions, UninstallOptions, Metadata, IExtensionManagementService, DidUninstallExtensionEvent, InstallExtensionEvent, InstallExtensionResult, UninstallExtensionEvent, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionType, IExtensionManifest, TargetPlatform } from 'vs/platform/extensions/common/extensions';
function transformIncomingURI(uri: UriComponents, transformer: IURITransformer | null): URI {
return URI.revive(transformer ? transformer.transformIncoming(uri) : uri);
function transformIncomingURI(uri: UriComponents, transformer: IURITransformer | null): URI;
function transformIncomingURI(uri: UriComponents | undefined, transformer: IURITransformer | null): URI | undefined;
function transformIncomingURI(uri: UriComponents | undefined, transformer: IURITransformer | null): URI | undefined {
return uri ? URI.revive(transformer ? transformer.transformIncoming(uri) : uri) : undefined;
}
function transformOutgoingURI(uri: URI, transformer: IURITransformer | null): URI {
@ -28,6 +29,10 @@ function transformIncomingExtension(extension: ILocalExtension, transformer: IUR
return { ...transformed, ...{ manifest } };
}
function transformIncomingOptions<O extends { profileLocation?: UriComponents }>(options: O | undefined, transformer: IURITransformer | null): O | undefined {
return options?.profileLocation ? transformAndReviveIncomingURIs(options, transformer ?? DefaultURITransformer) : options;
}
function transformOutgoingExtension(extension: ILocalExtension, transformer: IURITransformer | null): ILocalExtension {
return transformer ? cloneAndChange(extension, value => value instanceof URI ? transformer.transformOutgoingURI(value) : undefined) : extension;
}
@ -51,41 +56,112 @@ export class ExtensionManagementChannel implements IServerChannel {
listen(context: any, event: string): Event<any> {
const uriTransformer = this.getUriTransformer(context);
switch (event) {
case 'onInstallExtension': return this.onInstallExtension;
case 'onDidInstallExtensions': return Event.map(this.onDidInstallExtensions, results => results.map(i => ({ ...i, local: i.local ? transformOutgoingExtension(i.local, uriTransformer) : i.local })));
case 'onUninstallExtension': return this.onUninstallExtension;
case 'onDidUninstallExtension': return this.onDidUninstallExtension;
case 'onDidUpdateExtensionMetadata': return this.onDidUpdateExtensionMetadata;
case 'onInstallExtension': {
return Event.map<InstallExtensionEvent, InstallExtensionEvent>(this.onInstallExtension, e => {
return {
...e,
profileLocation: e.profileLocation ? transformOutgoingURI(e.profileLocation, uriTransformer) : e.profileLocation
};
});
}
case 'onDidInstallExtensions': {
return Event.map<readonly InstallExtensionResult[], readonly InstallExtensionResult[]>(this.onDidInstallExtensions, results =>
results.map(i => ({
...i,
local: i.local ? transformOutgoingExtension(i.local, uriTransformer) : i.local,
profileLocation: i.profileLocation ? transformOutgoingURI(i.profileLocation, uriTransformer) : i.profileLocation
})));
}
case 'onUninstallExtension': {
return Event.map<UninstallExtensionEvent, UninstallExtensionEvent>(this.onUninstallExtension, e => {
return {
...e,
profileLocation: e.profileLocation ? transformOutgoingURI(e.profileLocation, uriTransformer) : e.profileLocation
};
});
}
case 'onDidUninstallExtension': {
return Event.map<DidUninstallExtensionEvent, DidUninstallExtensionEvent>(this.onDidUninstallExtension, e => {
return {
...e,
profileLocation: e.profileLocation ? transformOutgoingURI(e.profileLocation, uriTransformer) : e.profileLocation
};
});
}
case 'onDidUpdateExtensionMetadata': {
return Event.map<ILocalExtension, ILocalExtension>(this.onDidUpdateExtensionMetadata, e => transformOutgoingExtension(e, uriTransformer));
}
}
throw new Error('Invalid listen');
}
call(context: any, command: string, args?: any): Promise<any> {
async call(context: any, command: string, args?: any): Promise<any> {
const uriTransformer: IURITransformer | null = this.getUriTransformer(context);
switch (command) {
case 'zip': return this.service.zip(transformIncomingExtension(args[0], uriTransformer)).then(uri => transformOutgoingURI(uri, uriTransformer));
case 'unzip': return this.service.unzip(transformIncomingURI(args[0], uriTransformer));
case 'install': return this.service.install(transformIncomingURI(args[0], uriTransformer), revive(args[1]));
case 'installFromLocation': return this.service.installFromLocation(transformIncomingURI(args[0], uriTransformer), URI.revive(args[1]));
case 'getManifest': return this.service.getManifest(transformIncomingURI(args[0], uriTransformer));
case 'getTargetPlatform': return this.service.getTargetPlatform();
case 'canInstall': return this.service.canInstall(args[0]);
case 'installFromGallery': return this.service.installFromGallery(args[0], revive(args[1]));
case 'uninstall': return this.service.uninstall(transformIncomingExtension(args[0], uriTransformer), revive(args[1]));
case 'reinstallFromGallery': return this.service.reinstallFromGallery(transformIncomingExtension(args[0], uriTransformer));
case 'getInstalled': return this.service.getInstalled(args[0], URI.revive(args[1])).then(extensions => extensions.map(e => transformOutgoingExtension(e, uriTransformer)));
case 'getMetadata': return this.service.getMetadata(transformIncomingExtension(args[0], uriTransformer), URI.revive(args[1]));
case 'updateMetadata': return this.service.updateMetadata(transformIncomingExtension(args[0], uriTransformer), args[1], URI.revive(args[2])).then(e => transformOutgoingExtension(e, uriTransformer));
case 'getExtensionsControlManifest': return this.service.getExtensionsControlManifest();
case 'download': return this.service.download(args[0], args[1]);
case 'cleanUp': return this.service.cleanUp();
case 'zip': {
const extension = transformIncomingExtension(args[0], uriTransformer);
const uri = await this.service.zip(extension);
return transformOutgoingURI(uri, uriTransformer);
}
case 'unzip': {
return this.service.unzip(transformIncomingURI(args[0], uriTransformer));
}
case 'install': {
return this.service.install(transformIncomingURI(args[0], uriTransformer), transformIncomingOptions(args[1], uriTransformer));
}
case 'installFromLocation': {
return this.service.installFromLocation(transformIncomingURI(args[0], uriTransformer), transformIncomingURI(args[1], uriTransformer));
}
case 'installExtensionsFromProfile': {
return this.service.installExtensionsFromProfile(args[0], transformIncomingURI(args[1], uriTransformer), transformIncomingURI(args[2], uriTransformer));
}
case 'getManifest': {
return this.service.getManifest(transformIncomingURI(args[0], uriTransformer));
}
case 'getTargetPlatform': {
return this.service.getTargetPlatform();
}
case 'canInstall': {
return this.service.canInstall(args[0]);
}
case 'installFromGallery': {
return this.service.installFromGallery(args[0], transformIncomingOptions(args[1], uriTransformer));
}
case 'uninstall': {
return this.service.uninstall(transformIncomingExtension(args[0], uriTransformer), transformIncomingOptions(args[1], uriTransformer));
}
case 'reinstallFromGallery': {
return this.service.reinstallFromGallery(transformIncomingExtension(args[0], uriTransformer));
}
case 'getInstalled': {
const extensions = await this.service.getInstalled(args[0], transformIncomingURI(args[1], uriTransformer));
return extensions.map(e => transformOutgoingExtension(e, uriTransformer));
}
case 'copyExtensions': {
return this.service.copyExtensions(transformIncomingURI(args[0], uriTransformer), transformIncomingURI(args[1], uriTransformer));
}
case 'updateMetadata': {
const e = await this.service.updateMetadata(transformIncomingExtension(args[0], uriTransformer), args[1], transformIncomingURI(args[2], uriTransformer));
return transformOutgoingExtension(e, uriTransformer);
}
case 'getExtensionsControlManifest': {
return this.service.getExtensionsControlManifest();
}
case 'download': {
return this.service.download(args[0], args[1]);
}
case 'cleanUp': {
return this.service.cleanUp();
}
}
throw new Error('Invalid call');
}
}
export type ExtensionEventResult = InstallExtensionEvent | InstallExtensionResult | UninstallExtensionEvent | DidUninstallExtensionEvent;
export class ExtensionManagementChannelClient extends Disposable implements IExtensionManagementService {
declare readonly _serviceBrand: undefined;
@ -107,13 +183,23 @@ export class ExtensionManagementChannelClient extends Disposable implements IExt
constructor(private readonly channel: IChannel) {
super();
this._register(this.channel.listen<InstallExtensionEvent>('onInstallExtension')(e => this._onInstallExtension.fire({ identifier: e.identifier, source: this.isUriComponents(e.source) ? URI.revive(e.source) : e.source, profileLocation: URI.revive(e.profileLocation) })));
this._register(this.channel.listen<readonly InstallExtensionResult[]>('onDidInstallExtensions')(results => this._onDidInstallExtensions.fire(results.map(e => ({ ...e, local: e.local ? transformIncomingExtension(e.local, null) : e.local, source: this.isUriComponents(e.source) ? URI.revive(e.source) : e.source, profileLocation: URI.revive(e.profileLocation) })))));
this._register(this.channel.listen<UninstallExtensionEvent>('onUninstallExtension')(e => this._onUninstallExtension.fire({ identifier: e.identifier, profileLocation: URI.revive(e.profileLocation) })));
this._register(this.channel.listen<DidUninstallExtensionEvent>('onDidUninstallExtension')(e => this._onDidUninstallExtension.fire({ ...e, profileLocation: URI.revive(e.profileLocation) })));
this._register(this.channel.listen<InstallExtensionEvent>('onInstallExtension')(e => this.fireEvent(this._onInstallExtension, { ...e, source: this.isUriComponents(e.source) ? URI.revive(e.source) : e.source, profileLocation: URI.revive(e.profileLocation) })));
this._register(this.channel.listen<readonly InstallExtensionResult[]>('onDidInstallExtensions')(results => this.fireEvent(this._onDidInstallExtensions, results.map(e => ({ ...e, local: e.local ? transformIncomingExtension(e.local, null) : e.local, source: this.isUriComponents(e.source) ? URI.revive(e.source) : e.source, profileLocation: URI.revive(e.profileLocation) })))));
this._register(this.channel.listen<UninstallExtensionEvent>('onUninstallExtension')(e => this.fireEvent(this._onUninstallExtension, { ...e, profileLocation: URI.revive(e.profileLocation) })));
this._register(this.channel.listen<DidUninstallExtensionEvent>('onDidUninstallExtension')(e => this.fireEvent(this._onDidUninstallExtension, { ...e, profileLocation: URI.revive(e.profileLocation) })));
this._register(this.channel.listen<ILocalExtension>('onDidUpdateExtensionMetadata')(e => this._onDidUpdateExtensionMetadata.fire(transformIncomingExtension(e, null))));
}
protected fireEvent(event: Emitter<InstallExtensionEvent>, data: InstallExtensionEvent): void;
protected fireEvent(event: Emitter<readonly InstallExtensionResult[]>, data: InstallExtensionResult[]): void;
protected fireEvent(event: Emitter<UninstallExtensionEvent>, data: UninstallExtensionEvent): void;
protected fireEvent(event: Emitter<DidUninstallExtensionEvent>, data: DidUninstallExtensionEvent): void;
protected fireEvent(event: Emitter<ExtensionEventResult>, data: ExtensionEventResult): void;
protected fireEvent(event: Emitter<ExtensionEventResult[]>, data: ExtensionEventResult[]): void;
protected fireEvent<E>(event: Emitter<E>, data: E): void {
event.fire(data);
}
private isUriComponents(thing: unknown): thing is UriComponents {
if (!thing) {
return false;
@ -151,6 +237,11 @@ export class ExtensionManagementChannelClient extends Disposable implements IExt
return Promise.resolve(this.channel.call<ILocalExtension>('installFromLocation', [location, profileLocation])).then(local => transformIncomingExtension(local, null));
}
async installExtensionsFromProfile(extensions: IExtensionIdentifier[], fromProfileLocation: URI, toProfileLocation: URI): Promise<ILocalExtension[]> {
const result = await this.channel.call<ILocalExtension[]>('installExtensionsFromProfile', [extensions, fromProfileLocation, toProfileLocation]);
return result.map(local => transformIncomingExtension(local, null));
}
getManifest(vsix: URI): Promise<IExtensionManifest> {
return Promise.resolve(this.channel.call<IExtensionManifest>('getManifest', [vsix]));
}
@ -172,15 +263,15 @@ export class ExtensionManagementChannelClient extends Disposable implements IExt
.then(extensions => extensions.map(extension => transformIncomingExtension(extension, null)));
}
getMetadata(local: ILocalExtension, extensionsProfileResource?: URI): Promise<Metadata | undefined> {
return Promise.resolve(this.channel.call<Metadata>('getMetadata', [local, extensionsProfileResource]));
}
updateMetadata(local: ILocalExtension, metadata: Partial<Metadata>, extensionsProfileResource?: URI): Promise<ILocalExtension> {
return Promise.resolve(this.channel.call<ILocalExtension>('updateMetadata', [local, metadata, extensionsProfileResource]))
.then(extension => transformIncomingExtension(extension, null));
}
copyExtensions(fromProfileLocation: URI, toProfileLocation: URI): Promise<void> {
return this.channel.call<void>('copyExtensions', [fromProfileLocation, toProfileLocation]);
}
getExtensionsControlManifest(): Promise<IExtensionsControlManifest> {
return Promise.resolve(this.channel.call<IExtensionsControlManifest>('getExtensionsControlManifest'));
}

View file

@ -14,12 +14,13 @@ import { areSameExtensions } from 'vs/platform/extensionManagement/common/extens
import { IExtension, IExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { FileOperationResult, IFileService, toFileOperationResult } from 'vs/platform/files/common/files';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { ILogService } from 'vs/platform/log/common/log';
import { ILogService, LogLevel } from 'vs/platform/log/common/log';
import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity';
import { Mutable, isObject, isString, isUndefined } from 'vs/base/common/types';
import { getErrorMessage } from 'vs/base/common/errors';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { Schemas } from 'vs/base/common/network';
interface IStoredProfileExtension {
identifier: IExtensionIdentifier;
@ -248,7 +249,7 @@ export abstract class AbstractExtensionsProfileScannerService extends Disposable
this.reportAndThrowInvalidConentError(file);
}
let location: URI;
if (isString(e.relativeLocation)) {
if (isString(e.relativeLocation) && e.relativeLocation) {
// Extension in new format. No migration needed.
location = this.resolveExtensionLocation(e.relativeLocation);
} else if (isString(e.location)) {
@ -309,9 +310,14 @@ export abstract class AbstractExtensionsProfileScannerService extends Disposable
}
private toRelativePath(extensionLocation: URI): string | undefined {
return this.uriIdentityService.extUri.isEqualOrParent(extensionLocation, this.extensionsLocation)
? this.uriIdentityService.extUri.relativePath(this.extensionsLocation, extensionLocation)
: undefined;
if (this.uriIdentityService.extUri.isEqualOrParent(extensionLocation, this.extensionsLocation)) {
const relativePath = this.uriIdentityService.extUri.relativePath(this.extensionsLocation, extensionLocation);
if (this.extensionsLocation.scheme === Schemas.file && this.logService.getLevel() === LogLevel.Trace) {
this.logService.trace('Relative path', extensionLocation.fsPath, this.extensionsLocation.fsPath, relativePath);
}
return relativePath;
}
return undefined;
}
private resolveExtensionLocation(path: string): URI {
@ -396,7 +402,7 @@ export abstract class AbstractExtensionsProfileScannerService extends Disposable
function isStoredProfileExtension(candidate: any): candidate is IStoredProfileExtension {
return isObject(candidate)
&& isIExtensionIdentifier(candidate.identifier)
&& (isUriComponents(candidate.location) || isString(candidate.location))
&& (isUriComponents(candidate.location) || (isString(candidate.location) && candidate.location))
&& (isUndefined(candidate.relativeLocation) || isString(candidate.relativeLocation))
&& candidate.version && isString(candidate.version);
}

View file

@ -164,13 +164,20 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi
if (!local) {
throw new Error(`Cannot find a valid extension from the location ${location.toString()}`);
}
await this.addExtensionsToProfile([local], profileLocation);
await this.addExtensionsToProfile([[local, undefined]], profileLocation);
this.logService.info('Successfully installed extension', local.identifier.id, profileLocation.toString());
return local;
}
getMetadata(extension: ILocalExtension, profileLocation: URI = this.userDataProfilesService.defaultProfile.extensionsResource): Promise<Metadata | undefined> {
return this.extensionsScanner.scanMetadata(extension, profileLocation);
async installExtensionsFromProfile(extensions: IExtensionIdentifier[], fromProfileLocation: URI, toProfileLocation: URI): Promise<ILocalExtension[]> {
this.logService.trace('ExtensionManagementService#installExtensionsFromProfile', extensions, fromProfileLocation.toString(), toProfileLocation.toString());
const extensionsToInstall = (await this.extensionsScanner.scanExtensions(ExtensionType.User, fromProfileLocation)).filter(e => extensions.some(id => areSameExtensions(id, e.identifier)));
if (extensionsToInstall.length) {
const metadata = await Promise.all(extensionsToInstall.map(e => this.extensionsScanner.scanMetadata(e, fromProfileLocation)));
await this.addExtensionsToProfile(extensionsToInstall.map((e, index) => [e, metadata[index]]), toProfileLocation);
this.logService.info('Successfully installed extensions', extensionsToInstall.map(e => e.identifier.id), toProfileLocation.toString());
}
return extensionsToInstall;
}
async updateMetadata(local: ILocalExtension, metadata: Partial<Metadata>, profileLocation: URI = this.userDataProfilesService.defaultProfile.extensionsResource): Promise<ILocalExtension> {
@ -209,6 +216,10 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi
return this.installFromGallery(galleryExtension);
}
copyExtensions(fromProfileLocation: URI, toProfileLocation: URI): Promise<void> {
return this.extensionsScanner.copyExtensions(fromProfileLocation, toProfileLocation);
}
markAsUninstalled(...extensions: IExtension[]): Promise<void> {
return this.extensionsScanner.setUninstalled(...extensions);
}
@ -364,15 +375,16 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi
}
if (added.length) {
await this.addExtensionsToProfile(added, this.userDataProfilesService.defaultProfile.extensionsResource);
await this.addExtensionsToProfile(added.map(e => [e, undefined]), this.userDataProfilesService.defaultProfile.extensionsResource);
this.logService.info('Added extensions to default profile from external source', added.map(e => e.identifier.id));
}
}
private async addExtensionsToProfile(extensions: ILocalExtension[], profileLocation: URI): Promise<void> {
await this.setInstalled(extensions);
await this.extensionsProfileScannerService.addExtensionsToProfile(extensions.map(local => ([local, undefined])), profileLocation);
this._onDidInstallExtensions.fire(extensions.map(local => ({ local, identifier: local.identifier, operation: InstallOperation.None, profileLocation })));
private async addExtensionsToProfile(extensions: [ILocalExtension, Metadata | undefined][], profileLocation: URI): Promise<void> {
const localExtensions = extensions.map(e => e[0]);
await this.setInstalled(localExtensions);
await this.extensionsProfileScannerService.addExtensionsToProfile(extensions, profileLocation);
this._onDidInstallExtensions.fire(localExtensions.map(local => ({ local, identifier: local.identifier, operation: InstallOperation.None, profileLocation })));
}
private async setInstalled(extensions: ILocalExtension[]): Promise<void> {
@ -530,6 +542,14 @@ export class ExtensionsScanner extends Disposable {
await this.withUninstalledExtensions(uninstalled => delete uninstalled[ExtensionKey.create(extension).toString()]);
}
async copyExtensions(fromProfileLocation: URI, toProfileLocation: URI): Promise<void> {
const fromExtensions = await this.scanExtensions(ExtensionType.User, fromProfileLocation);
const extensions: [ILocalExtension, Metadata | undefined][] = await Promise.all(fromExtensions
.filter(e => !e.isApplicationScoped) /* remove application scoped extensions */
.map(async e => ([e, await this.scanMetadata(e, fromProfileLocation)])));
await this.extensionsProfileScannerService.addExtensionsToProfile(extensions, toProfileLocation);
}
private async withUninstalledExtensions(updateFn?: (uninstalled: IStringDictionary<boolean>) => void): Promise<IStringDictionary<boolean>> {
return this.uninstalledFileLimiter.queue(async () => {
let raw: string | undefined;

View file

@ -414,7 +414,7 @@ suite('ExtensionsProfileScannerService', () => {
test('read extension when manifest has empty lines and spaces', async () => {
const extensionsManifest = joinPath(extensionsLocation, 'extensions.json');
await instantiationService.get(IFileService).writeFile(extensionsManifest, VSBuffer.fromString(`
await instantiationService.get(IFileService).writeFile(extensionsManifest, VSBuffer.fromString(`
`));
@ -423,6 +423,25 @@ suite('ExtensionsProfileScannerService', () => {
assert.deepStrictEqual(actual, []);
});
test('read extension when the relative location is empty', async () => {
const extensionsManifest = joinPath(extensionsLocation, 'extensions.json');
const extension = aExtension('pub.a', joinPath(extensionsLocation, 'pub.a-1.0.0'));
await instantiationService.get(IFileService).writeFile(extensionsManifest, VSBuffer.fromString(JSON.stringify([{
identifier: extension.identifier,
location: extension.location.toJSON(),
relativeLocation: '',
version: extension.manifest.version,
}])));
const testObject = instantiationService.createInstance(TestObject, extensionsLocation);
const actual = await testObject.scanProfileExtensions(extensionsManifest);
assert.deepStrictEqual(actual.map(a => ({ ...a, location: a.location.toJSON() })), [{ identifier: extension.identifier, location: extension.location.toJSON(), version: extension.manifest.version, metadata: undefined }]);
const manifestContent = JSON.parse((await instantiationService.get(IFileService).readFile(extensionsManifest)).value.toString());
assert.deepStrictEqual(manifestContent, [{ identifier: extension.identifier, location: extension.location.toJSON(), relativeLocation: 'pub.a-1.0.0', version: extension.manifest.version }]);
});
function aExtension(id: string, location: URI, e?: Partial<IExtension>): IExtension {
return {
identifier: { id },

View file

@ -409,7 +409,7 @@ export function isAuthenticationProviderExtension(manifest: IExtensionManifest):
export function isResolverExtension(manifest: IExtensionManifest, remoteAuthority: string | undefined): boolean {
if (remoteAuthority) {
const activationEvent = `onResolveRemoteAuthority:${getRemoteName(remoteAuthority)}`;
return manifest.activationEvents?.indexOf(activationEvent) !== -1;
return !!manifest.activationEvents?.includes(activationEvent);
}
return false;
}

View file

@ -18,7 +18,7 @@ import { mixin } from 'vs/base/common/objects';
import * as platform from 'vs/base/common/platform';
import { cwd } from 'vs/base/common/process';
import { canUseUtilityProcess } from 'vs/base/parts/sandbox/electron-main/electronTypes';
import { UtilityProcess } from 'vs/platform/utilityProcess/electron-main/utilityProcess';
import { WindowUtilityProcess } from 'vs/platform/utilityProcess/electron-main/utilityProcess';
import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
@ -27,7 +27,7 @@ export class ExtensionHostStarter implements IDisposable, IExtensionHostStarter
private static _lastId: number = 0;
protected readonly _extHosts: Map<string, ExtensionHostProcess | UtilityProcess>;
protected readonly _extHosts: Map<string, ExtensionHostProcess | WindowUtilityProcess>;
private _shutdown = false;
constructor(
@ -36,7 +36,7 @@ export class ExtensionHostStarter implements IDisposable, IExtensionHostStarter
@IWindowsMainService private readonly _windowsMainService: IWindowsMainService,
@ITelemetryService private readonly _telemetryService: ITelemetryService,
) {
this._extHosts = new Map<string, ExtensionHostProcess | UtilityProcess>();
this._extHosts = new Map<string, ExtensionHostProcess | WindowUtilityProcess>();
// On shutdown: gracefully await extension host shutdowns
this._lifecycleMainService.onWillShutdown((e) => {
@ -49,7 +49,7 @@ export class ExtensionHostStarter implements IDisposable, IExtensionHostStarter
// Intentionally not killing the extension host processes
}
private _getExtHost(id: string): ExtensionHostProcess | UtilityProcess {
private _getExtHost(id: string): ExtensionHostProcess | WindowUtilityProcess {
const extHostProcess = this._extHosts.get(id);
if (!extHostProcess) {
throw new Error(`Unknown extension host!`);
@ -71,7 +71,7 @@ export class ExtensionHostStarter implements IDisposable, IExtensionHostStarter
onDynamicError(id: string): Event<{ error: SerializedError }> {
const exthost = this._getExtHost(id);
if (exthost instanceof UtilityProcess) {
if (exthost instanceof WindowUtilityProcess) {
return Event.None;
}
@ -91,12 +91,12 @@ export class ExtensionHostStarter implements IDisposable, IExtensionHostStarter
throw canceled();
}
const id = String(++ExtensionHostStarter._lastId);
let extHost: UtilityProcess | ExtensionHostProcess;
let extHost: WindowUtilityProcess | ExtensionHostProcess;
if (useUtilityProcess) {
if (!canUseUtilityProcess) {
throw new Error(`Cannot use UtilityProcess!`);
}
extHost = new UtilityProcess(this._logService, this._windowsMainService, this._telemetryService, this._lifecycleMainService);
extHost = new WindowUtilityProcess(this._logService, this._windowsMainService, this._telemetryService, this._lifecycleMainService);
} else {
extHost = new ExtensionHostProcess(id, this._logService);
}
@ -118,6 +118,7 @@ export class ExtensionHostStarter implements IDisposable, IExtensionHostStarter
this._getExtHost(id).start({
...opts,
type: 'extensionHost',
entryPoint: 'vs/workbench/api/node/extensionHostProcess',
args: ['--skipWorkspaceStorageLock'],
execArgv: opts.execArgv,
allowLoadingUnsignedLibraries: true,

View file

@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { Language } from 'vs/base/common/platform';
import { URI } from 'vs/base/common/uri';
import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionResourceLoaderService } from 'vs/platform/extensionResourceLoader/common/extensionResourceLoader';
@ -20,7 +19,7 @@ export class WebLanguagePacksService extends LanguagePackBaseService {
super(extensionGalleryService);
}
async getBuiltInExtensionTranslationsUri(id: string): Promise<URI | undefined> {
async getBuiltInExtensionTranslationsUri(id: string, language: string): Promise<URI | undefined> {
const queryTimeout = new CancellationTokenSource();
setTimeout(() => queryTimeout.cancel(), 1000);
@ -29,7 +28,7 @@ export class WebLanguagePacksService extends LanguagePackBaseService {
let result;
try {
result = await this.extensionGalleryService.query({
text: `tag:"lp-${Language.value()}"`,
text: `tag:"lp-${language}"`,
pageSize: 5
}, queryTimeout.token);
} catch (err) {
@ -39,7 +38,7 @@ export class WebLanguagePacksService extends LanguagePackBaseService {
const languagePackExtensions = result.firstPage.find(e => e.properties.localizedLanguages?.length);
if (!languagePackExtensions) {
this.logService.trace(`No language pack found for language ${Language.value()}`);
this.logService.trace(`No language pack found for language ${language}`);
return undefined;
}
@ -49,7 +48,7 @@ export class WebLanguagePacksService extends LanguagePackBaseService {
const manifest = await this.extensionGalleryService.getManifest(languagePackExtensions, manifestTimeout.token);
// Find the translation from the language pack
const localization = manifest?.contributes?.localizations?.find(l => l.languageId === Language.value());
const localization = manifest?.contributes?.localizations?.find(l => l.languageId === language);
const translation = localization?.translations.find(t => t.id === id);
if (!translation) {
this.logService.trace(`No translation found for id '${id}, in ${manifest?.name}`);

View file

@ -27,7 +27,7 @@ export interface ILanguagePackService {
readonly _serviceBrand: undefined;
getAvailableLanguages(): Promise<Array<ILanguagePackItem>>;
getInstalledLanguages(): Promise<Array<ILanguagePackItem>>;
getBuiltInExtensionTranslationsUri(id: string): Promise<URI | undefined>;
getBuiltInExtensionTranslationsUri(id: string, language: string): Promise<URI | undefined>;
}
export abstract class LanguagePackBaseService extends Disposable implements ILanguagePackService {
@ -37,7 +37,7 @@ export abstract class LanguagePackBaseService extends Disposable implements ILan
super();
}
abstract getBuiltInExtensionTranslationsUri(id: string): Promise<URI | undefined>;
abstract getBuiltInExtensionTranslationsUri(id: string, language: string): Promise<URI | undefined>;
abstract getInstalledLanguages(): Promise<Array<ILanguagePackItem>>;

View file

@ -16,7 +16,6 @@ import { areSameExtensions } from 'vs/platform/extensionManagement/common/extens
import { ILogService } from 'vs/platform/log/common/log';
import { ILocalizationContribution } from 'vs/platform/extensions/common/extensions';
import { ILanguagePackItem, LanguagePackBaseService } from 'vs/platform/languagePacks/common/languagePacks';
import { Language } from 'vs/base/common/platform';
import { URI } from 'vs/base/common/uri';
interface ILanguagePack {
@ -50,11 +49,11 @@ export class NativeLanguagePackService extends LanguagePackBaseService {
});
}
async getBuiltInExtensionTranslationsUri(id: string): Promise<URI | undefined> {
async getBuiltInExtensionTranslationsUri(id: string, language: string): Promise<URI | undefined> {
const packs = await this.cache.getLanguagePacks();
const pack = packs[Language.value()];
const pack = packs[language];
if (!pack) {
this.logService.warn(`No language pack found for ${Language.value()}`);
this.logService.warn(`No language pack found for ${language}`);
return undefined;
}

View file

@ -8,7 +8,7 @@ import { Disposable } from 'vs/base/common/lifecycle';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { ILogService } from 'vs/platform/log/common/log';
export const ISharedProcessLifecycleService = createDecorator<ISharedProcessLifecycleService>('lifecycleSharedProcessService');
export const ISharedProcessLifecycleService = createDecorator<ISharedProcessLifecycleService>('sharedProcessLifecycleService');
export interface ISharedProcessLifecycleService {
readonly _serviceBrand: undefined;

View file

@ -6,7 +6,7 @@
import { AbstractPolicyService, IPolicyService, PolicyDefinition } from 'vs/platform/policy/common/policy';
import { IStringDictionary } from 'vs/base/common/collections';
import { Throttler } from 'vs/base/common/async';
import { createWatcher, PolicyUpdate, Watcher } from 'vscode-policy-watcher';
import { createWatcher, PolicyUpdate, Watcher } from '@vscode/policy-watcher';
import { MutableDisposable } from 'vs/base/common/lifecycle';
import { ILogService } from 'vs/platform/log/common/log';

View file

@ -9,6 +9,7 @@ import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems';
import { Button, IButtonStyles } from 'vs/base/browser/ui/button/button';
import { CountBadge, ICountBadgeStyles } from 'vs/base/browser/ui/countBadge/countBadge';
import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate';
import { IInputBoxStyles } from 'vs/base/browser/ui/inputbox/inputBox';
import { IKeybindingLabelStyles } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel';
import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
@ -51,6 +52,7 @@ export interface IQuickInputOptions {
renderers: IListRenderer<T, any>[],
options: IListOptions<T>,
): List<T>;
hoverDelegate: IHoverDelegate;
styles: IQuickInputStyles;
}
@ -1416,6 +1418,12 @@ export class QuickInputController extends Disposable {
}
}
break;
case KeyCode.Space:
if (event.ctrlKey) {
dom.EventHelper.stop(e, true);
this.getUI().list.toggleHover();
}
break;
}
}));

View file

@ -7,19 +7,23 @@ import * as dom from 'vs/base/browser/dom';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { AriaRole } from 'vs/base/browser/ui/aria/aria';
import { HoverPosition } from 'vs/base/browser/ui/hover/hoverWidget';
import { IHoverWidget } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate';
import { IconLabel, IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel';
import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
import { IListAccessibilityProvider, IListOptions, IListStyles, List } from 'vs/base/browser/ui/list/listWidget';
import { IAction } from 'vs/base/common/actions';
import { range } from 'vs/base/common/arrays';
import { ThrottledDelayer } from 'vs/base/common/async';
import { compareAnything } from 'vs/base/common/comparers';
import { memoize } from 'vs/base/common/decorators';
import { Emitter, Event } from 'vs/base/common/event';
import { IMatch } from 'vs/base/common/filters';
import { IMarkdownString } from 'vs/base/common/htmlContent';
import { getCodiconAriaLabel, IParsedLabelWithIcons, matchesFuzzyIconAware, parseLabelWithIcons } from 'vs/base/common/iconLabels';
import { KeyCode } from 'vs/base/common/keyCodes';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle';
import * as platform from 'vs/base/common/platform';
import { ltrim } from 'vs/base/common/strings';
import { withNullAsUndefined } from 'vs/base/common/types';
@ -41,6 +45,7 @@ interface IListElement {
readonly saneAriaLabel: string;
readonly saneDescription?: string;
readonly saneDetail?: string;
readonly saneTooltip?: string | IMarkdownString | HTMLElement;
readonly labelHighlights?: IMatch[];
readonly descriptionHighlights?: IMatch[];
readonly detailHighlights?: IMatch[];
@ -60,6 +65,8 @@ class ListElement implements IListElement, IDisposable {
saneAriaLabel!: string;
saneDescription?: string;
saneDetail?: string;
saneTooltip?: string | IMarkdownString | HTMLElement;
element?: HTMLElement;
hidden = false;
private readonly _onChecked = new Emitter<boolean>();
onChecked = this._onChecked.event;
@ -159,6 +166,7 @@ class ListElementRenderer implements IListRenderer<ListElement, IListElementTemp
renderElement(element: ListElement, index: number, data: IListElementTemplateData): void {
data.element = element;
element.element = withNullAsUndefined(data.entry);
const mainItem: QuickPickItem = element.item ? element.item : element.separator!;
data.checkbox.checked = element.checked;
@ -306,11 +314,13 @@ export class QuickInputList {
private _fireCheckedEvents = true;
private elementDisposables: IDisposable[] = [];
private disposables: IDisposable[] = [];
private _lastHover: IHoverWidget | undefined;
private _toggleHover: IDisposable | undefined;
constructor(
private parent: HTMLElement,
id: string,
options: IQuickInputOptions,
private options: IQuickInputOptions,
) {
this.id = id;
this.container = dom.append(this.parent, $('.quick-input-list'));
@ -381,6 +391,40 @@ export class QuickInputList {
this.list.setSelection([e.index]);
}
}));
const delayer = new ThrottledDelayer(options.hoverDelegate.delay);
// onMouseOver triggers every time a new element has been moused over
// even if it's on the same list item.
this.disposables.push(this.list.onMouseOver(async e => {
// If we hover over an anchor element, we don't want to show the hover because
// the anchor may have a tooltip that we want to show instead.
if (e.browserEvent.target instanceof HTMLAnchorElement) {
delayer.cancel();
return;
}
if (
// anchors are an exception as called out above so we skip them here
!(e.browserEvent.relatedTarget instanceof HTMLAnchorElement) &&
// check if the mouse is still over the same element
dom.isAncestor(e.browserEvent.relatedTarget as Node, e.element?.element as Node)
) {
return;
}
await delayer.trigger(async () => {
if (e.element) {
this.showHover(e.element);
}
});
}));
this.disposables.push(this.list.onMouseOut(e => {
// onMouseOut triggers every time a new element has been moused over
// even if it's on the same list item. We only want one event, so we
// check if the mouse is still over the same element.
if (dom.isAncestor(e.browserEvent.relatedTarget as Node, e.element?.element as Node)) {
return;
}
delayer.cancel();
}));
this.disposables.push(
this._onChangedAllVisibleChecked,
this._onChangedCheckedCount,
@ -389,7 +433,8 @@ export class QuickInputList {
this._onButtonTriggered,
this._onSeparatorButtonTriggered,
this._onLeave,
this._onKeyDown
this._onKeyDown,
delayer
);
}
@ -475,7 +520,7 @@ export class QuickInputList {
const saneLabel = item.label ? item.label.replace(/\r?\n/g, ' ') : '';
const saneSortLabel = parseLabelWithIcons(saneLabel).text.trim();
let saneMeta, saneDescription, saneDetail, labelHighlights, descriptionHighlights, detailHighlights;
let saneMeta, saneDescription, saneDetail, labelHighlights, descriptionHighlights, detailHighlights, saneTooltip;
if (item.type !== 'separator') {
saneMeta = item.meta && item.meta.replace(/\r?\n/g, ' ');
saneDescription = item.description && item.description.replace(/\r?\n/g, ' ');
@ -483,6 +528,7 @@ export class QuickInputList {
labelHighlights = item.highlights?.label;
descriptionHighlights = item.highlights?.description;
detailHighlights = item.highlights?.detail;
saneTooltip = item.tooltip;
}
const saneAriaLabel = item.ariaLabel || [saneLabel, saneDescription, saneDetail]
.map(s => getCodiconAriaLabel(s))
@ -512,6 +558,7 @@ export class QuickInputList {
saneAriaLabel,
saneDescription,
saneDetail,
saneTooltip,
labelHighlights,
descriptionHighlights,
detailHighlights,
@ -659,6 +706,30 @@ export class QuickInputList {
this.list.domFocus();
}
/**
* Disposes of the hover and shows a new one for the given index if it has a tooltip.
* @param element The element to show the hover for
*/
private showHover(element: ListElement): void {
if (this._lastHover && !this._lastHover.isDisposed) {
this.options.hoverDelegate.onDidHideHover?.();
this._lastHover?.dispose();
}
if (!element.element || !element.saneTooltip) {
return;
}
this._lastHover = this.options.hoverDelegate.showHover({
content: element.saneTooltip!,
target: element.element!,
linkHandler: (url) => {
this.options.linkOpenerDelegate(url);
},
showPointer: true,
container: this.container,
hoverPosition: HoverPosition.RIGHT
}, false);
}
layout(maxHeight?: number): void {
this.list.getHTMLElement().style.maxHeight = maxHeight ? `calc(${Math.floor(maxHeight / 44) * 44}px)` : '';
this.list.layout();
@ -795,6 +866,37 @@ export class QuickInputList {
style(styles: IListStyles) {
this.list.style(styles);
}
toggleHover() {
const element = this.list.getFocusedElements()[0];
if (!element.saneTooltip) {
return;
}
// if there's a hover already, hide it (toggle off)
if (this._lastHover && !this._lastHover.isDisposed) {
this._lastHover.dispose();
return;
}
// If there is no hover, show it (toggle on)
const focused = this.list.getFocusedElements()[0];
if (!focused) {
return;
}
this.showHover(focused);
const store = new DisposableStore();
store.add(this.list.onDidChangeFocus(e => {
if (e.indexes.length) {
this.showHover(e.elements[0]);
}
}));
if (this._lastHover) {
store.add(this._lastHover);
}
this._toggleHover = store;
this.elementDisposables.push(this._toggleHover);
}
}
function matchesContiguousIconAware(query: string, target: IParsedLabelWithIcons): IMatch[] | null {

View file

@ -89,6 +89,12 @@ export class QuickInputService extends Themable implements IQuickInputService {
renderers: IListRenderer<T, any>[],
options: IWorkbenchListOptions<T>
) => this.instantiationService.createInstance(WorkbenchList, user, container, delegate, renderers, options) as List<T>,
hoverDelegate: {
showHover(options, focus) {
return undefined;
},
delay: 200
},
styles: this.computeStyles()
};

View file

@ -14,6 +14,7 @@ import { IDisposable } from 'vs/base/common/lifecycle';
import { Schemas } from 'vs/base/common/network';
import Severity from 'vs/base/common/severity';
import { URI } from 'vs/base/common/uri';
import { IMarkdownString } from 'vs/base/common/htmlContent';
export interface IQuickPickItemHighlights {
label?: IMatch[];
@ -31,6 +32,7 @@ export interface IQuickPickItem {
ariaLabel?: string;
description?: string;
detail?: string;
tooltip?: string | IMarkdownString;
/**
* Allows to show a keybinding next to the item to indicate
* how the item can be triggered outside of the picker using
@ -52,6 +54,7 @@ export interface IQuickPickSeparator {
label?: string;
ariaLabel?: string;
buttons?: readonly IQuickInputButton[];
tooltip?: string | IMarkdownString;
}
export interface IKeyMods {

View file

@ -58,6 +58,12 @@ suite('QuickInput', () => { // https://github.com/microsoft/vscode/issues/147543
renderers: IListRenderer<T, any>[],
options: IListOptions<T>,
) => new List<T>(user, container, delegate, renderers, options),
hoverDelegate: {
showHover(options, focus) {
return undefined;
},
delay: 200
},
styles: {
button: unthemedButtonStyles,
countBadge: unthemedCountStyles,

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