mirror of
https://github.com/Microsoft/vscode
synced 2024-07-17 11:07:22 +00:00
Merge branch 'main' into async-tokenization-2
This commit is contained in:
commit
a42ec7cdd2
|
@ -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",
|
||||
|
|
2
.vscode/notebooks/endgame.github-issues
vendored
2
.vscode/notebooks/endgame.github-issues
vendored
|
@ -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,
|
||||
|
|
2
.vscode/notebooks/my-endgame.github-issues
vendored
2
.vscode/notebooks/my-endgame.github-issues
vendored
|
@ -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,
|
||||
|
|
2
.vscode/notebooks/verification.github-issues
vendored
2
.vscode/notebooks/verification.github-issues
vendored
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -177,4 +177,5 @@ steps:
|
|||
displayName: "Component Detection"
|
||||
inputs:
|
||||
sourceScanPath: $(Build.SourcesDirectory)
|
||||
alertWarningLevel: 'Medium'
|
||||
continueOnError: true
|
||||
|
|
|
@ -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=
|
|
@ -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 => {
|
||||
|
|
|
@ -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' }
|
||||
]
|
||||
}
|
||||
)
|
||||
|
|
|
@ -493,6 +493,10 @@
|
|||
{
|
||||
"name": "vs/workbench/services/userDataProfile",
|
||||
"project": "vscode-profiles"
|
||||
},
|
||||
{
|
||||
"name": "vs/workbench/services/localization",
|
||||
"project": "vscode-workbench"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -6,6 +6,5 @@
|
|||
"devDependencies": {
|
||||
"node-gyp": "^8.4.1"
|
||||
},
|
||||
"scripts": {
|
||||
}
|
||||
"scripts": {}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
"contribEditSessions",
|
||||
"contribViewsWelcome",
|
||||
"editSessionIdentityProvider",
|
||||
"quickDiffProvider",
|
||||
"scmActionButton",
|
||||
"scmSelectedProvider",
|
||||
"scmValidation",
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
},
|
||||
|
|
|
@ -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"
|
||||
},
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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==
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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')
|
||||
];
|
||||
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
});
|
||||
}));
|
||||
|
|
|
@ -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(
|
|
@ -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);
|
||||
},
|
||||
{
|
|
@ -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);
|
||||
});
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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[];
|
||||
|
||||
|
|
|
@ -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[] {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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[];
|
||||
|
|
|
@ -284,6 +284,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
|
|||
@ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService,
|
||||
) {
|
||||
super();
|
||||
codeEditorService.willCreateCodeEditor();
|
||||
|
||||
const options = { ..._options };
|
||||
|
||||
|
|
|
@ -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()));
|
||||
|
|
|
@ -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()),
|
||||
|
|
29
src/vs/editor/common/editorFeatures.ts
Normal file
29
src/vs/editor/common/editorFeatures.ts
Normal 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);
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
18
src/vs/editor/common/services/semanticTokensStyling.ts
Normal file
18
src/vs/editor/common/services/semanticTokensStyling.ts
Normal 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;
|
||||
}
|
|
@ -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);
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
|
@ -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;
|
|
@ -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;
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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';
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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') {
|
||||
|
|
|
@ -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
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
17
src/vs/monaco.d.ts
vendored
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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' },
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 });
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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'));
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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}`);
|
||||
|
|
|
@ -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>>;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}));
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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()
|
||||
};
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
Loading…
Reference in a new issue