Merge branch 'main' into ai-codefixes

This commit is contained in:
Nathan Shively-Sanders 2023-09-20 08:35:13 -07:00
commit ba511b36f1
298 changed files with 6622 additions and 1662 deletions

View file

@ -151,4 +151,12 @@
"application.experimental.rendererProfiling": true,
"editor.experimental.asyncTokenization": true,
"editor.experimental.asyncTokenizationVerification": true,
"typescript.preferences.autoImportFileExcludePatterns": [
"xterm",
"xterm-headless",
"node-pty",
"vscode-notebook-renderer",
"src/vs/workbench/workbench.web.main.ts",
"src/vs/workbench/api/common/extHostTypes.ts"
]
}

File diff suppressed because one or more lines are too long

View file

@ -197,15 +197,19 @@ async function main(): Promise<void> {
const uploadPromises: Promise<void>[] = [];
if (await blobClient.exists()) {
uploadPromises.push(Promise.reject(new Error(`Blob ${quality}, ${blobName} already exists, not publishing again.`)));
} else {
uploadPromises.push(retry(async (attempt) => {
console.log(`Uploading blobs to Azure storage (attempt ${attempt})...`);
await blobClient.uploadFile(filePath, blobOptions);
console.log('Blob successfully uploaded to Azure storage.');
}));
}
uploadPromises.push((async () => {
console.log(`Checking for blob in Azure...`);
if (await retry(() => blobClient.exists())) {
throw new Error(`Blob ${quality}, ${blobName} already exists, not publishing again.`);
} else {
await retry(async (attempt) => {
console.log(`Uploading blobs to Azure storage (attempt ${attempt})...`);
await blobClient.uploadFile(filePath, blobOptions);
console.log('Blob successfully uploaded to Azure storage.');
});
}
})());
const shouldUploadToMooncake = /true/i.test(process.env['VSCODE_PUBLISH_TO_MOONCAKE'] ?? 'true');
@ -215,15 +219,19 @@ async function main(): Promise<void> {
const mooncakeContainerClient = mooncakeBlobServiceClient.getContainerClient(quality);
const mooncakeBlobClient = mooncakeContainerClient.getBlockBlobClient(blobName);
if (await mooncakeBlobClient.exists()) {
uploadPromises.push(Promise.reject(new Error(`Mooncake Blob ${quality}, ${blobName} already exists, not publishing again.`)));
} else {
uploadPromises.push(retry(async (attempt) => {
console.log(`Uploading blobs to Mooncake Azure storage (attempt ${attempt})...`);
await mooncakeBlobClient.uploadFile(filePath, blobOptions);
console.log('Blob successfully uploaded to Mooncake Azure storage.');
}));
}
uploadPromises.push((async () => {
console.log(`Checking for blob in Mooncake Azure...`);
if (await retry(() => mooncakeBlobClient.exists())) {
throw new Error(`Mooncake Blob ${quality}, ${blobName} already exists, not publishing again.`);
} else {
await retry(async (attempt) => {
console.log(`Uploading blobs to Mooncake Azure storage (attempt ${attempt})...`);
await mooncakeBlobClient.uploadFile(filePath, blobOptions);
console.log('Blob successfully uploaded to Mooncake Azure storage.');
});
}
})());
}
const promiseResults = await Promise.allSettled(uploadPromises);

View file

@ -32,8 +32,8 @@ async function main(buildDir) {
'Credits.rtf',
'CodeResources',
'fsevents.node',
'Info.plist',
'MainMenu.nib',
'Info.plist', // TODO@deepak1556: regressed with 11.4.2 internal builds
'MainMenu.nib', // Generated sequence is not deterministic with Xcode 13
'.npmrc'
],
outAppPath,
@ -57,4 +57,4 @@ if (require.main === module) {
process.exit(1);
});
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3JlYXRlLXVuaXZlcnNhbC1hcHAuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJjcmVhdGUtdW5pdmVyc2FsLWFwcC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztnR0FHZ0c7O0FBRWhHLDZCQUE2QjtBQUM3Qix5QkFBeUI7QUFDekIsdUVBQTREO0FBQzVELHFFQUFvRDtBQUVwRCxNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQztBQUVuRCxLQUFLLFVBQVUsSUFBSSxDQUFDLFFBQWlCO0lBQ3BDLE1BQU0sSUFBSSxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsYUFBYSxDQUFDLENBQUM7SUFFeEMsSUFBSSxDQUFDLFFBQVEsRUFBRTtRQUNkLE1BQU0sSUFBSSxLQUFLLENBQUMsd0JBQXdCLENBQUMsQ0FBQztLQUMxQztJQUVELE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxjQUFjLENBQUMsRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDO0lBQ3JGLE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxRQUFRLEdBQUcsTUFBTSxDQUFDO0lBQzFDLE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLG1CQUFtQixFQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQ3JFLE1BQU0sWUFBWSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLHFCQUFxQixFQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQ3pFLE1BQU0sV0FBVyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxFQUFFLFVBQVUsRUFBRSxXQUFXLEVBQUUsS0FBSyxFQUFFLG1CQUFtQixDQUFDLENBQUM7SUFDL0YsTUFBTSxhQUFhLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxZQUFZLEVBQUUsVUFBVSxFQUFFLFdBQVcsRUFBRSxLQUFLLEVBQUUsbUJBQW1CLENBQUMsQ0FBQztJQUNuRyxNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsRUFBRSxpQkFBaUIsSUFBSSxFQUFFLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDekUsTUFBTSxlQUFlLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxVQUFVLEVBQUUsVUFBVSxFQUFFLFdBQVcsRUFBRSxLQUFLLEVBQUUsY0FBYyxDQUFDLENBQUM7SUFFakcsTUFBTSxJQUFBLDJDQUFnQixFQUFDO1FBQ3RCLFVBQVU7UUFDVixZQUFZO1FBQ1osV0FBVztRQUNYLGFBQWE7UUFDYixXQUFXLEVBQUU7WUFDWixjQUFjO1lBQ2QsYUFBYTtZQUNiLGVBQWU7WUFDZixlQUFlO1lBQ2YsWUFBWTtZQUNaLGNBQWM7WUFDZCxRQUFRO1NBQ1I7UUFDRCxVQUFVO1FBQ1YsS0FBSyxFQUFFLElBQUk7S0FDWCxDQUFDLENBQUM7SUFFSCxNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQyxZQUFZLENBQUMsZUFBZSxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUM7SUFDekUsTUFBTSxDQUFDLE1BQU0sQ0FBQyxXQUFXLEVBQUU7UUFDMUIsc0JBQXNCLEVBQUUsa0JBQWtCO0tBQzFDLENBQUMsQ0FBQztJQUNILEVBQUUsQ0FBQyxhQUFhLENBQUMsZUFBZSxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsV0FBVyxFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsQ0FBQyxDQUFDO0lBRTNFLGtEQUFrRDtJQUNsRCxNQUFNLFVBQVUsR0FBRyxNQUFNLElBQUEsMkJBQUssRUFBQyxNQUFNLEVBQUUsQ0FBQyxVQUFVLEVBQUUsT0FBTyxFQUFFLGVBQWUsQ0FBQyxDQUFDLENBQUM7SUFDL0UsTUFBTSxVQUFVLEdBQUcsTUFBTSxJQUFBLDJCQUFLLEVBQUMsTUFBTSxFQUFFLENBQUMsUUFBUSxFQUFFLFVBQVUsQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNsRixJQUFJLFVBQVUsQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxLQUFLLGNBQWMsRUFBRTtRQUNyRCxNQUFNLElBQUksS0FBSyxDQUFDLHVCQUF1QixVQUFVLEVBQUUsQ0FBQyxDQUFDO0tBQ3JEO0FBQ0YsQ0FBQztBQUVELElBQUksT0FBTyxDQUFDLElBQUksS0FBSyxNQUFNLEVBQUU7SUFDNUIsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLEVBQUU7UUFDakMsT0FBTyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUNuQixPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ2pCLENBQUMsQ0FBQyxDQUFDO0NBQ0gifQ==
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3JlYXRlLXVuaXZlcnNhbC1hcHAuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJjcmVhdGUtdW5pdmVyc2FsLWFwcC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztnR0FHZ0c7O0FBRWhHLDZCQUE2QjtBQUM3Qix5QkFBeUI7QUFDekIsdUVBQTREO0FBQzVELHFFQUFvRDtBQUVwRCxNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQztBQUVuRCxLQUFLLFVBQVUsSUFBSSxDQUFDLFFBQWlCO0lBQ3BDLE1BQU0sSUFBSSxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsYUFBYSxDQUFDLENBQUM7SUFFeEMsSUFBSSxDQUFDLFFBQVEsRUFBRTtRQUNkLE1BQU0sSUFBSSxLQUFLLENBQUMsd0JBQXdCLENBQUMsQ0FBQztLQUMxQztJQUVELE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxjQUFjLENBQUMsRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDO0lBQ3JGLE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxRQUFRLEdBQUcsTUFBTSxDQUFDO0lBQzFDLE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLG1CQUFtQixFQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQ3JFLE1BQU0sWUFBWSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLHFCQUFxQixFQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQ3pFLE1BQU0sV0FBVyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxFQUFFLFVBQVUsRUFBRSxXQUFXLEVBQUUsS0FBSyxFQUFFLG1CQUFtQixDQUFDLENBQUM7SUFDL0YsTUFBTSxhQUFhLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxZQUFZLEVBQUUsVUFBVSxFQUFFLFdBQVcsRUFBRSxLQUFLLEVBQUUsbUJBQW1CLENBQUMsQ0FBQztJQUNuRyxNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsRUFBRSxpQkFBaUIsSUFBSSxFQUFFLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDekUsTUFBTSxlQUFlLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxVQUFVLEVBQUUsVUFBVSxFQUFFLFdBQVcsRUFBRSxLQUFLLEVBQUUsY0FBYyxDQUFDLENBQUM7SUFFakcsTUFBTSxJQUFBLDJDQUFnQixFQUFDO1FBQ3RCLFVBQVU7UUFDVixZQUFZO1FBQ1osV0FBVztRQUNYLGFBQWE7UUFDYixXQUFXLEVBQUU7WUFDWixjQUFjO1lBQ2QsYUFBYTtZQUNiLGVBQWU7WUFDZixlQUFlO1lBQ2YsWUFBWSxFQUFFLHlEQUF5RDtZQUN2RSxjQUFjLEVBQUUsd0RBQXdEO1lBQ3hFLFFBQVE7U0FDUjtRQUNELFVBQVU7UUFDVixLQUFLLEVBQUUsSUFBSTtLQUNYLENBQUMsQ0FBQztJQUVILE1BQU0sV0FBVyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLFlBQVksQ0FBQyxlQUFlLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQztJQUN6RSxNQUFNLENBQUMsTUFBTSxDQUFDLFdBQVcsRUFBRTtRQUMxQixzQkFBc0IsRUFBRSxrQkFBa0I7S0FDMUMsQ0FBQyxDQUFDO0lBQ0gsRUFBRSxDQUFDLGFBQWEsQ0FBQyxlQUFlLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxXQUFXLEVBQUUsSUFBSSxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUM7SUFFM0Usa0RBQWtEO0lBQ2xELE1BQU0sVUFBVSxHQUFHLE1BQU0sSUFBQSwyQkFBSyxFQUFDLE1BQU0sRUFBRSxDQUFDLFVBQVUsRUFBRSxPQUFPLEVBQUUsZUFBZSxDQUFDLENBQUMsQ0FBQztJQUMvRSxNQUFNLFVBQVUsR0FBRyxNQUFNLElBQUEsMkJBQUssRUFBQyxNQUFNLEVBQUUsQ0FBQyxRQUFRLEVBQUUsVUFBVSxDQUFDLE9BQU8sQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ2xGLElBQUksVUFBVSxDQUFDLE9BQU8sQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLEtBQUssY0FBYyxFQUFFO1FBQ3JELE1BQU0sSUFBSSxLQUFLLENBQUMsdUJBQXVCLFVBQVUsRUFBRSxDQUFDLENBQUM7S0FDckQ7QUFDRixDQUFDO0FBRUQsSUFBSSxPQUFPLENBQUMsSUFBSSxLQUFLLE1BQU0sRUFBRTtJQUM1QixJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsRUFBRTtRQUNqQyxPQUFPLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ25CLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDakIsQ0FBQyxDQUFDLENBQUM7Q0FDSCJ9

File diff suppressed because one or more lines are too long

View file

@ -8,11 +8,11 @@ exports.referenceGeneratedDepsByArch = exports.recommendedDeps = exports.additio
// Based on https://source.chromium.org/chromium/chromium/src/+/main:chrome/installer/linux/debian/additional_deps
// Additional dependencies not in the dpkg-shlibdeps output.
exports.additionalDeps = [
'ca-certificates',
'ca-certificates', // Make sure users have SSL certificates.
'libgtk-3-0 (>= 3.9.10) | libgtk-4-1',
'libnss3 (>= 3.26)',
'libcurl3-gnutls | libcurl3-nss | libcurl4 | libcurl3',
'xdg-utils (>= 1.0.2)',
'libcurl3-gnutls | libcurl3-nss | libcurl4 | libcurl3', // For Breakpad crash reports.
'xdg-utils (>= 1.0.2)', // OS integration
'libgssapi-krb5-2',
'libkrb5-3',
];
@ -140,4 +140,4 @@ exports.referenceGeneratedDepsByArch = {
'xdg-utils (>= 1.0.2)'
]
};
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVwLWxpc3RzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiZGVwLWxpc3RzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7O2dHQUdnRzs7O0FBRWhHLGtIQUFrSDtBQUNsSCw0REFBNEQ7QUFDL0MsUUFBQSxjQUFjLEdBQUc7SUFDN0IsaUJBQWlCO0lBQ2pCLHFDQUFxQztJQUNyQyxtQkFBbUI7SUFDbkIsc0RBQXNEO0lBQ3RELHNCQUFzQjtJQUN0QixrQkFBa0I7SUFDbEIsV0FBVztDQUNYLENBQUM7QUFFRixvSEFBb0g7QUFDcEgsMENBQTBDO0FBQzFDLDhEQUE4RDtBQUNqRCxRQUFBLGVBQWUsR0FBRztJQUM5QixZQUFZLENBQUMseUVBQXlFO0NBQ3RGLENBQUM7QUFFVyxRQUFBLDRCQUE0QixHQUFHO0lBQzNDLE9BQU8sRUFBRTtRQUNSLGlCQUFpQjtRQUNqQix3QkFBd0I7UUFDeEIsK0JBQStCO1FBQy9CLHdCQUF3QjtRQUN4QiwyQkFBMkI7UUFDM0IsaUJBQWlCO1FBQ2pCLGlCQUFpQjtRQUNqQixpQkFBaUI7UUFDakIsa0JBQWtCO1FBQ2xCLHNCQUFzQjtRQUN0QixzREFBc0Q7UUFDdEQseUJBQXlCO1FBQ3pCLHFCQUFxQjtRQUNyQixzQkFBc0I7UUFDdEIseUJBQXlCO1FBQ3pCLDBCQUEwQjtRQUMxQixrQkFBa0I7UUFDbEIsd0JBQXdCO1FBQ3hCLHFDQUFxQztRQUNyQyxXQUFXO1FBQ1gsd0JBQXdCO1FBQ3hCLHFCQUFxQjtRQUNyQixtQkFBbUI7UUFDbkIsNEJBQTRCO1FBQzVCLFVBQVU7UUFDViwwQkFBMEI7UUFDMUIsb0JBQW9CO1FBQ3BCLCtCQUErQjtRQUMvQix3QkFBd0I7UUFDeEIsVUFBVTtRQUNWLFlBQVk7UUFDWiwwQkFBMEI7UUFDMUIsYUFBYTtRQUNiLFlBQVk7UUFDWixzQkFBc0I7S0FDdEI7SUFDRCxPQUFPLEVBQUU7UUFDUixpQkFBaUI7UUFDakIsd0JBQXdCO1FBQ3hCLCtCQUErQjtRQUMvQix3QkFBd0I7UUFDeEIsMkJBQTJCO1FBQzNCLGlCQUFpQjtRQUNqQixpQkFBaUI7UUFDakIsaUJBQWlCO1FBQ2pCLGdCQUFnQjtRQUNoQixnQkFBZ0I7UUFDaEIsZ0JBQWdCO1FBQ2hCLHNCQUFzQjtRQUN0QixzREFBc0Q7UUFDdEQseUJBQXlCO1FBQ3pCLHFCQUFxQjtRQUNyQixzQkFBc0I7UUFDdEIseUJBQXlCO1FBQ3pCLDBCQUEwQjtRQUMxQixrQkFBa0I7UUFDbEIsd0JBQXdCO1FBQ3hCLHFDQUFxQztRQUNyQyxXQUFXO1FBQ1gsd0JBQXdCO1FBQ3hCLHFCQUFxQjtRQUNyQixtQkFBbUI7UUFDbkIsNEJBQTRCO1FBQzVCLG1CQUFtQjtRQUNuQixxQkFBcUI7UUFDckIsbUJBQW1CO1FBQ25CLFVBQVU7UUFDViwwQkFBMEI7UUFDMUIsb0JBQW9CO1FBQ3BCLCtCQUErQjtRQUMvQix3QkFBd0I7UUFDeEIsVUFBVTtRQUNWLFlBQVk7UUFDWiwwQkFBMEI7UUFDMUIsYUFBYTtRQUNiLFlBQVk7UUFDWixzQkFBc0I7S0FDdEI7SUFDRCxPQUFPLEVBQUU7UUFDUixpQkFBaUI7UUFDakIsd0JBQXdCO1FBQ3hCLCtCQUErQjtRQUMvQix3QkFBd0I7UUFDeEIsMkJBQTJCO1FBQzNCLGlCQUFpQjtRQUNqQixzQkFBc0I7UUFDdEIsc0RBQXNEO1FBQ3RELHdCQUF3QjtRQUN4QixxQkFBcUI7UUFDckIsc0JBQXNCO1FBQ3RCLHlCQUF5QjtRQUN6QiwwQkFBMEI7UUFDMUIsa0JBQWtCO1FBQ2xCLHdCQUF3QjtRQUN4QixxQ0FBcUM7UUFDckMsV0FBVztRQUNYLHdCQUF3QjtRQUN4QixxQkFBcUI7UUFDckIsbUJBQW1CO1FBQ25CLDRCQUE0QjtRQUM1QixtQkFBbUI7UUFDbkIscUJBQXFCO1FBQ3JCLG1CQUFtQjtRQUNuQixVQUFVO1FBQ1YsMEJBQTBCO1FBQzFCLG9CQUFvQjtRQUNwQiwrQkFBK0I7UUFDL0Isd0JBQXdCO1FBQ3hCLFVBQVU7UUFDVixZQUFZO1FBQ1osMEJBQTBCO1FBQzFCLGFBQWE7UUFDYixZQUFZO1FBQ1osc0JBQXNCO0tBQ3RCO0NBQ0QsQ0FBQyJ9
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVwLWxpc3RzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiZGVwLWxpc3RzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7O2dHQUdnRzs7O0FBRWhHLGtIQUFrSDtBQUNsSCw0REFBNEQ7QUFDL0MsUUFBQSxjQUFjLEdBQUc7SUFDN0IsaUJBQWlCLEVBQUUseUNBQXlDO0lBQzVELHFDQUFxQztJQUNyQyxtQkFBbUI7SUFDbkIsc0RBQXNELEVBQUUsOEJBQThCO0lBQ3RGLHNCQUFzQixFQUFFLGlCQUFpQjtJQUN6QyxrQkFBa0I7SUFDbEIsV0FBVztDQUNYLENBQUM7QUFFRixvSEFBb0g7QUFDcEgsMENBQTBDO0FBQzFDLDhEQUE4RDtBQUNqRCxRQUFBLGVBQWUsR0FBRztJQUM5QixZQUFZLENBQUMseUVBQXlFO0NBQ3RGLENBQUM7QUFFVyxRQUFBLDRCQUE0QixHQUFHO0lBQzNDLE9BQU8sRUFBRTtRQUNSLGlCQUFpQjtRQUNqQix3QkFBd0I7UUFDeEIsK0JBQStCO1FBQy9CLHdCQUF3QjtRQUN4QiwyQkFBMkI7UUFDM0IsaUJBQWlCO1FBQ2pCLGlCQUFpQjtRQUNqQixpQkFBaUI7UUFDakIsa0JBQWtCO1FBQ2xCLHNCQUFzQjtRQUN0QixzREFBc0Q7UUFDdEQseUJBQXlCO1FBQ3pCLHFCQUFxQjtRQUNyQixzQkFBc0I7UUFDdEIseUJBQXlCO1FBQ3pCLDBCQUEwQjtRQUMxQixrQkFBa0I7UUFDbEIsd0JBQXdCO1FBQ3hCLHFDQUFxQztRQUNyQyxXQUFXO1FBQ1gsd0JBQXdCO1FBQ3hCLHFCQUFxQjtRQUNyQixtQkFBbUI7UUFDbkIsNEJBQTRCO1FBQzVCLFVBQVU7UUFDViwwQkFBMEI7UUFDMUIsb0JBQW9CO1FBQ3BCLCtCQUErQjtRQUMvQix3QkFBd0I7UUFDeEIsVUFBVTtRQUNWLFlBQVk7UUFDWiwwQkFBMEI7UUFDMUIsYUFBYTtRQUNiLFlBQVk7UUFDWixzQkFBc0I7S0FDdEI7SUFDRCxPQUFPLEVBQUU7UUFDUixpQkFBaUI7UUFDakIsd0JBQXdCO1FBQ3hCLCtCQUErQjtRQUMvQix3QkFBd0I7UUFDeEIsMkJBQTJCO1FBQzNCLGlCQUFpQjtRQUNqQixpQkFBaUI7UUFDakIsaUJBQWlCO1FBQ2pCLGdCQUFnQjtRQUNoQixnQkFBZ0I7UUFDaEIsZ0JBQWdCO1FBQ2hCLHNCQUFzQjtRQUN0QixzREFBc0Q7UUFDdEQseUJBQXlCO1FBQ3pCLHFCQUFxQjtRQUNyQixzQkFBc0I7UUFDdEIseUJBQXlCO1FBQ3pCLDBCQUEwQjtRQUMxQixrQkFBa0I7UUFDbEIsd0JBQXdCO1FBQ3hCLHFDQUFxQztRQUNyQyxXQUFXO1FBQ1gsd0JBQXdCO1FBQ3hCLHFCQUFxQjtRQUNyQixtQkFBbUI7UUFDbkIsNEJBQTRCO1FBQzVCLG1CQUFtQjtRQUNuQixxQkFBcUI7UUFDckIsbUJBQW1CO1FBQ25CLFVBQVU7UUFDViwwQkFBMEI7UUFDMUIsb0JBQW9CO1FBQ3BCLCtCQUErQjtRQUMvQix3QkFBd0I7UUFDeEIsVUFBVTtRQUNWLFlBQVk7UUFDWiwwQkFBMEI7UUFDMUIsYUFBYTtRQUNiLFlBQVk7UUFDWixzQkFBc0I7S0FDdEI7SUFDRCxPQUFPLEVBQUU7UUFDUixpQkFBaUI7UUFDakIsd0JBQXdCO1FBQ3hCLCtCQUErQjtRQUMvQix3QkFBd0I7UUFDeEIsMkJBQTJCO1FBQzNCLGlCQUFpQjtRQUNqQixzQkFBc0I7UUFDdEIsc0RBQXNEO1FBQ3RELHdCQUF3QjtRQUN4QixxQkFBcUI7UUFDckIsc0JBQXNCO1FBQ3RCLHlCQUF5QjtRQUN6QiwwQkFBMEI7UUFDMUIsa0JBQWtCO1FBQ2xCLHdCQUF3QjtRQUN4QixxQ0FBcUM7UUFDckMsV0FBVztRQUNYLHdCQUF3QjtRQUN4QixxQkFBcUI7UUFDckIsbUJBQW1CO1FBQ25CLDRCQUE0QjtRQUM1QixtQkFBbUI7UUFDbkIscUJBQXFCO1FBQ3JCLG1CQUFtQjtRQUNuQixVQUFVO1FBQ1YsMEJBQTBCO1FBQzFCLG9CQUFvQjtRQUNwQiwrQkFBK0I7UUFDL0Isd0JBQXdCO1FBQ3hCLFVBQVU7UUFDVixZQUFZO1FBQ1osMEJBQTBCO1FBQzFCLGFBQWE7UUFDYixZQUFZO1FBQ1osc0JBQXNCO0tBQ3RCO0NBQ0QsQ0FBQyJ9

File diff suppressed because one or more lines are too long

View file

@ -6,7 +6,7 @@
"git": {
"name": "microsoft/vscode-css",
"repositoryUrl": "https://github.com/microsoft/vscode-css",
"commitHash": "3bd00206f6b0d16eb2eba53fb886462eb8c58baa"
"commitHash": "c216f777497265700ff336f739328e5197e012cd"
}
},
"licenseDetail": [

File diff suppressed because one or more lines are too long

View file

@ -18,6 +18,7 @@
"editSessionIdentityProvider",
"quickDiffProvider",
"scmActionButton",
"scmHistoryProvider",
"scmSelectedProvider",
"scmValidation",
"tabInputTextMerge",

View file

@ -20,24 +20,24 @@ interface ActionButtonState {
readonly repositoryHasChangesToCommit: boolean;
}
export class ActionButtonCommand {
private _onDidChange = new EventEmitter<void>();
abstract class AbstractActionButton {
protected _onDidChange = new EventEmitter<void>();
get onDidChange(): Event<void> { return this._onDidChange.event; }
private _state: ActionButtonState;
private get state() { return this._state; }
private set state(state: ActionButtonState) {
protected get state() { return this._state; }
protected set state(state: ActionButtonState) {
if (JSON.stringify(this._state) !== JSON.stringify(state)) {
this._state = state;
this._onDidChange.fire();
}
}
private disposables: Disposable[] = [];
abstract get button(): SourceControlActionButton | undefined;
constructor(
readonly repository: Repository,
readonly postCommitCommandCenter: CommitCommandsCenter) {
protected disposables: Disposable[] = [];
constructor(readonly repository: Repository) {
this._state = {
HEAD: undefined,
isCheckoutInProgress: false,
@ -50,6 +50,126 @@ export class ActionButtonCommand {
repository.onDidRunGitStatus(this.onDidRunGitStatus, this, this.disposables);
repository.onDidChangeOperations(this.onDidChangeOperations, this, this.disposables);
}
protected getPublishBranchActionButton(): SourceControlActionButton | undefined {
const icon = this.state.isSyncInProgress ? '$(sync~spin)' : '$(cloud-upload)';
return {
command: {
command: 'git.publish',
title: l10n.t({ message: '{0} Publish Branch', args: [icon], comment: ['{Locked="Branch"}', 'Do not translate "Branch" as it is a git term'] }),
tooltip: this.state.isSyncInProgress ?
(this.state.HEAD?.name ?
l10n.t({ message: 'Publishing Branch "{0}"...', args: [this.state.HEAD.name], comment: ['{Locked="Branch"}', 'Do not translate "Branch" as it is a git term'] }) :
l10n.t({ message: 'Publishing Branch...', comment: ['{Locked="Branch"}', 'Do not translate "Branch" as it is a git term'] })) :
(this.repository.HEAD?.name ?
l10n.t({ message: 'Publish Branch "{0}"', args: [this.state.HEAD?.name], comment: ['{Locked="Branch"}', 'Do not translate "Branch" as it is a git term'] }) :
l10n.t({ message: 'Publish Branch', comment: ['{Locked="Branch"}', 'Do not translate "Branch" as it is a git term'] })),
arguments: [this.repository.sourceControl],
},
enabled: !this.state.isCheckoutInProgress && !this.state.isSyncInProgress && !this.state.isCommitInProgress && !this.state.isMergeInProgress && !this.state.isRebaseInProgress
};
}
protected getSyncChangesActionButton(): SourceControlActionButton | undefined {
const branchIsAheadOrBehind = (this.state.HEAD?.behind ?? 0) > 0 || (this.state.HEAD?.ahead ?? 0) > 0;
const ahead = this.state.HEAD?.ahead ? ` ${this.state.HEAD.ahead}$(arrow-up)` : '';
const behind = this.state.HEAD?.behind ? ` ${this.state.HEAD.behind}$(arrow-down)` : '';
const icon = this.state.isSyncInProgress ? '$(sync~spin)' : '$(sync)';
return {
command: {
command: 'git.sync',
title: l10n.t('{0} Sync Changes{1}{2}', icon, behind, ahead),
tooltip: this.state.isSyncInProgress ?
l10n.t('Synchronizing Changes...')
: this.repository.syncTooltip,
arguments: [this.repository.sourceControl],
},
description: `${icon}${behind}${ahead}`,
enabled: !this.state.isCheckoutInProgress && !this.state.isSyncInProgress && !this.state.isCommitInProgress && !this.state.isMergeInProgress && !this.state.isRebaseInProgress && branchIsAheadOrBehind
};
}
private onDidChangeOperations(): void {
const isCheckoutInProgress
= this.repository.operations.isRunning(OperationKind.Checkout) ||
this.repository.operations.isRunning(OperationKind.CheckoutTracking);
const isCommitInProgress =
this.repository.operations.isRunning(OperationKind.Commit) ||
this.repository.operations.isRunning(OperationKind.PostCommitCommand) ||
this.repository.operations.isRunning(OperationKind.RebaseContinue);
const isSyncInProgress =
this.repository.operations.isRunning(OperationKind.Sync) ||
this.repository.operations.isRunning(OperationKind.Push) ||
this.repository.operations.isRunning(OperationKind.Pull);
this.state = { ...this.state, isCheckoutInProgress, isCommitInProgress, isSyncInProgress };
}
private onDidRunGitStatus(): void {
this.state = {
...this.state,
HEAD: this.repository.HEAD,
isMergeInProgress: this.repository.mergeGroup.resourceStates.length !== 0,
isRebaseInProgress: !!this.repository.rebaseCommit,
repositoryHasChangesToCommit: this.repositoryHasChangesToCommit()
};
}
protected repositoryHasChangesToCommit(): boolean {
const config = workspace.getConfiguration('git', Uri.file(this.repository.root));
const enableSmartCommit = config.get<boolean>('enableSmartCommit') === true;
const suggestSmartCommit = config.get<boolean>('suggestSmartCommit') === true;
const smartCommitChanges = config.get<'all' | 'tracked'>('smartCommitChanges', 'all');
const resources = [...this.repository.indexGroup.resourceStates];
if (
// Smart commit enabled (all)
(enableSmartCommit && smartCommitChanges === 'all') ||
// Smart commit disabled, smart suggestion enabled
(!enableSmartCommit && suggestSmartCommit)
) {
resources.push(...this.repository.workingTreeGroup.resourceStates);
}
// Smart commit enabled (tracked only)
if (enableSmartCommit && smartCommitChanges === 'tracked') {
resources.push(...this.repository.workingTreeGroup.resourceStates.filter(r => r.type !== Status.UNTRACKED));
}
return resources.length !== 0;
}
dispose(): void {
this.disposables = dispose(this.disposables);
}
}
export class CommitActionButton extends AbstractActionButton {
override get button(): SourceControlActionButton | undefined {
if (!this.state.HEAD) { return undefined; }
let actionButton: SourceControlActionButton | undefined;
if (this.state.repositoryHasChangesToCommit) {
// Commit Changes (enabled)
actionButton = this.getCommitActionButton();
}
// Commit Changes (enabled) -> Publish Branch -> Sync Changes -> Commit Changes (disabled)
return actionButton ?? this.getPublishBranchActionButton() ?? this.getSyncChangesActionButton() ?? this.getCommitActionButton();
}
constructor(
repository: Repository,
readonly postCommitCommandCenter: CommitCommandsCenter) {
super(repository);
this.disposables.push(repository.onDidChangeBranchProtection(() => this._onDidChange.fire()));
this.disposables.push(postCommitCommandCenter.onDidChange(() => this._onDidChange.fire()));
@ -62,7 +182,8 @@ export class ActionButtonCommand {
this.onDidChangeSmartCommitSettings();
}
if (e.affectsConfiguration('git.branchProtectionPrompt', root) ||
if (e.affectsConfiguration('scm.experimental.showSyncView') ||
e.affectsConfiguration('git.branchProtectionPrompt', root) ||
e.affectsConfiguration('git.postCommitCommand', root) ||
e.affectsConfiguration('git.rememberPostCommitCommand', root) ||
e.affectsConfiguration('git.showActionButton', root)) {
@ -71,20 +192,6 @@ export class ActionButtonCommand {
}));
}
get button(): SourceControlActionButton | undefined {
if (!this.state.HEAD) { return undefined; }
let actionButton: SourceControlActionButton | undefined;
if (this.state.repositoryHasChangesToCommit) {
// Commit Changes (enabled)
actionButton = this.getCommitActionButton();
}
// Commit Changes (enabled) -> Publish Branch -> Sync Changes -> Commit Changes (disabled)
return actionButton ?? this.getPublishBranchActionButton() ?? this.getSyncChangesActionButton() ?? this.getCommitActionButton();
}
private getCommitActionButton(): SourceControlActionButton | undefined {
const config = workspace.getConfiguration('git', Uri.file(this.repository.root));
const showActionButton = config.get<{ commit: boolean }>('showActionButton', { commit: true });
@ -133,34 +240,27 @@ export class ActionButtonCommand {
return commandGroups;
}
private getPublishBranchActionButton(): SourceControlActionButton | undefined {
protected override getPublishBranchActionButton(): SourceControlActionButton | undefined {
const scmConfig = workspace.getConfiguration('scm');
if (scmConfig.get<boolean>('experimental.showSyncView', false)) {
return undefined;
}
const config = workspace.getConfiguration('git', Uri.file(this.repository.root));
const showActionButton = config.get<{ publish: boolean }>('showActionButton', { publish: true });
// Not a branch (tag, detached), branch does have an upstream, commit/merge/rebase is in progress, or the button is disabled
if (this.state.HEAD?.type === RefType.Tag || !this.state.HEAD?.name || this.state.HEAD?.upstream || this.state.isCommitInProgress || this.state.isMergeInProgress || this.state.isRebaseInProgress || !showActionButton.publish) { return undefined; }
// Button icon
const icon = this.state.isSyncInProgress ? '$(sync~spin)' : '$(cloud-upload)';
return {
command: {
command: 'git.publish',
title: l10n.t({ message: '{0} Publish Branch', args: [icon], comment: ['{Locked="Branch"}', 'Do not translate "Branch" as it is a git term'] }),
tooltip: this.state.isSyncInProgress ?
(this.state.HEAD?.name ?
l10n.t({ message: 'Publishing Branch "{0}"...', args: [this.state.HEAD.name], comment: ['{Locked="Branch"}', 'Do not translate "Branch" as it is a git term'] }) :
l10n.t({ message: 'Publishing Branch...', comment: ['{Locked="Branch"}', 'Do not translate "Branch" as it is a git term'] })) :
(this.repository.HEAD?.name ?
l10n.t({ message: 'Publish Branch "{0}"', args: [this.state.HEAD?.name], comment: ['{Locked="Branch"}', 'Do not translate "Branch" as it is a git term'] }) :
l10n.t({ message: 'Publish Branch', comment: ['{Locked="Branch"}', 'Do not translate "Branch" as it is a git term'] })),
arguments: [this.repository.sourceControl],
},
enabled: !this.state.isCheckoutInProgress && !this.state.isSyncInProgress
};
return super.getPublishBranchActionButton();
}
private getSyncChangesActionButton(): SourceControlActionButton | undefined {
protected override getSyncChangesActionButton(): SourceControlActionButton | undefined {
const scmConfig = workspace.getConfiguration('scm');
if (scmConfig.get<boolean>('experimental.showSyncView', false)) {
return undefined;
}
const config = workspace.getConfiguration('git', Uri.file(this.repository.root));
const showActionButton = config.get<{ sync: boolean }>('showActionButton', { sync: true });
const branchIsAheadOrBehind = (this.state.HEAD?.behind ?? 0) > 0 || (this.state.HEAD?.ahead ?? 0) > 0;
@ -168,40 +268,7 @@ export class ActionButtonCommand {
// Branch does not have an upstream, branch is not ahead/behind the remote branch, commit/merge/rebase is in progress, or the button is disabled
if (!this.state.HEAD?.upstream || !branchIsAheadOrBehind || this.state.isCommitInProgress || this.state.isMergeInProgress || this.state.isRebaseInProgress || !showActionButton.sync) { return undefined; }
const ahead = this.state.HEAD.ahead ? ` ${this.state.HEAD.ahead}$(arrow-up)` : '';
const behind = this.state.HEAD.behind ? ` ${this.state.HEAD.behind}$(arrow-down)` : '';
const icon = this.state.isSyncInProgress ? '$(sync~spin)' : '$(sync)';
return {
command: {
command: 'git.sync',
title: l10n.t('{0} Sync Changes{1}{2}', icon, behind, ahead),
tooltip: this.state.isSyncInProgress ?
l10n.t('Synchronizing Changes...')
: this.repository.syncTooltip,
arguments: [this.repository.sourceControl],
},
description: `${icon}${behind}${ahead}`,
enabled: !this.state.isCheckoutInProgress && !this.state.isSyncInProgress
};
}
private onDidChangeOperations(): void {
const isCheckoutInProgress
= this.repository.operations.isRunning(OperationKind.Checkout) ||
this.repository.operations.isRunning(OperationKind.CheckoutTracking);
const isCommitInProgress =
this.repository.operations.isRunning(OperationKind.Commit) ||
this.repository.operations.isRunning(OperationKind.PostCommitCommand) ||
this.repository.operations.isRunning(OperationKind.RebaseContinue);
const isSyncInProgress =
this.repository.operations.isRunning(OperationKind.Sync) ||
this.repository.operations.isRunning(OperationKind.Push) ||
this.repository.operations.isRunning(OperationKind.Pull);
this.state = { ...this.state, isCheckoutInProgress, isCommitInProgress, isSyncInProgress };
return super.getSyncChangesActionButton();
}
private onDidChangeSmartCommitSettings(): void {
@ -210,43 +277,30 @@ export class ActionButtonCommand {
repositoryHasChangesToCommit: this.repositoryHasChangesToCommit()
};
}
}
private onDidRunGitStatus(): void {
this.state = {
...this.state,
HEAD: this.repository.HEAD,
isMergeInProgress: this.repository.mergeGroup.resourceStates.length !== 0,
isRebaseInProgress: !!this.repository.rebaseCommit,
repositoryHasChangesToCommit: this.repositoryHasChangesToCommit()
};
export class SyncActionButton extends AbstractActionButton {
override get button(): SourceControlActionButton | undefined {
if (!this.state.HEAD) { return undefined; }
// Publish Branch -> Sync Changes
return this.getPublishBranchActionButton() ?? this.getSyncChangesActionButton();
}
private repositoryHasChangesToCommit(): boolean {
const config = workspace.getConfiguration('git', Uri.file(this.repository.root));
const enableSmartCommit = config.get<boolean>('enableSmartCommit') === true;
const suggestSmartCommit = config.get<boolean>('suggestSmartCommit') === true;
const smartCommitChanges = config.get<'all' | 'tracked'>('smartCommitChanges', 'all');
constructor(repository: Repository) {
super(repository);
const resources = [...this.repository.indexGroup.resourceStates];
if (
// Smart commit enabled (all)
(enableSmartCommit && smartCommitChanges === 'all') ||
// Smart commit disabled, smart suggestion enabled
(!enableSmartCommit && suggestSmartCommit)
) {
resources.push(...this.repository.workingTreeGroup.resourceStates);
}
// Smart commit enabled (tracked only)
if (enableSmartCommit && smartCommitChanges === 'tracked') {
resources.push(...this.repository.workingTreeGroup.resourceStates.filter(r => r.type !== Status.UNTRACKED));
}
return resources.length !== 0;
this.disposables.push(workspace.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('scm.experimental.showSyncView')) {
this._onDidChange.fire();
}
}));
}
dispose(): void {
this.disposables = dispose(this.disposables);
protected override getPublishBranchActionButton(): SourceControlActionButton | undefined {
// Not a branch (tag, detached), branch does have an upstream
if (this.state.HEAD?.type === RefType.Tag || this.state.HEAD?.upstream) { return undefined; }
return super.getPublishBranchActionButton();
}
}

View file

@ -131,6 +131,8 @@ export interface LogOptions {
readonly path?: string;
/** A commit range, such as "0a47c67f0fb52dd11562af48658bc1dff1d75a38..0bb4bdea78e1db44d728fd6894720071e303304f" */
readonly range?: string;
readonly reverse?: boolean;
readonly sortByAuthorDate?: boolean;
}
export interface CommitOptions {

View file

@ -1023,17 +1023,24 @@ export class Repository {
}
async log(options?: LogOptions): Promise<Commit[]> {
const maxEntries = options?.maxEntries ?? 32;
const args = ['log', `-n${maxEntries}`, `--format=${COMMIT_FORMAT}`, '-z'];
const args = ['log', `--format=${COMMIT_FORMAT}`, '-z'];
if (options?.reverse) {
args.push('--reverse', '--ancestry-path');
}
if (options?.sortByAuthorDate) {
args.push('--author-date-order');
}
if (options?.range) {
args.push(options.range);
} else {
args.push(`-n${options?.maxEntries ?? 32}`);
}
args.push('--');
if (options?.path) {
args.push(options.path);
args.push('--', options.path);
}
const result = await this.exec(args);
@ -1258,7 +1265,7 @@ export class Repository {
diffIndexWithHEAD(): Promise<Change[]>;
diffIndexWithHEAD(path: string): Promise<string>;
diffIndexWithHEAD(path?: string | undefined): Promise<string | Change[]>;
diffIndexWithHEAD(path?: string | undefined): Promise<Change[]>;
async diffIndexWithHEAD(path?: string): Promise<string | Change[]> {
if (!path) {
return await this.diffFiles(true);
@ -1303,6 +1310,17 @@ export class Repository {
return result.stdout.trim();
}
async diffBetweenShortStat(ref1: string, ref2: string): Promise<string> {
const args = ['diff', '--shortstat', `${ref1}...${ref2}`];
const result = await this.exec(args);
if (result.exitCode) {
return '';
}
return result.stdout.trim();
}
private async diffFiles(cached: boolean, ref?: string): Promise<Change[]> {
const args = ['diff', '--name-status', '-z', '--diff-filter=ADMR'];
if (cached) {
@ -2450,6 +2468,15 @@ export class Repository {
return Promise.reject<Branch>(new Error('No such branch'));
}
async getDefaultBranch(): Promise<Branch> {
const result = await this.exec(['symbolic-ref', '--short', 'refs/remotes/origin/HEAD']);
if (!result.stdout) {
throw new Error('No default branch');
}
return this.getBranch(result.stdout.trim());
}
// TODO: Support core.commentChar
stripCommitMessageComments(message: string): string {
return message.replace(/^\s*#.*$\n?/gm, '').trim();
@ -2510,6 +2537,13 @@ export class Repository {
return commits[0];
}
async getCommitCount(range: string): Promise<{ ahead: number; behind: number }> {
const result = await this.exec(['rev-list', '--count', '--left-right', range]);
const [ahead, behind] = result.stdout.trim().split('\t');
return { ahead: Number(ahead) || 0, behind: Number(behind) || 0 };
}
async updateSubmodules(paths: string[]): Promise<void> {
const args = ['submodule', 'update'];

View file

@ -0,0 +1,183 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable, Event, EventEmitter, FileDecoration, FileDecorationProvider, SourceControlActionButton, SourceControlHistoryItem, SourceControlHistoryItemChange, SourceControlHistoryItemGroup, SourceControlHistoryOptions, SourceControlHistoryProvider, ThemeIcon, Uri, window, l10n } from 'vscode';
import { Repository, Resource } from './repository';
import { IDisposable } from './util';
import { toGitUri } from './uri';
import { SyncActionButton } from './actionButton';
import { Status } from './api/git';
export class GitHistoryProvider implements SourceControlHistoryProvider, FileDecorationProvider, IDisposable {
private readonly _onDidChangeActionButton = new EventEmitter<void>();
readonly onDidChangeActionButton: Event<void> = this._onDidChangeActionButton.event;
private readonly _onDidChangeCurrentHistoryItemGroup = new EventEmitter<void>();
readonly onDidChangeCurrentHistoryItemGroup: Event<void> = this._onDidChangeCurrentHistoryItemGroup.event;
private readonly _onDidChangeDecorations = new EventEmitter<Uri[]>();
readonly onDidChangeFileDecorations: Event<Uri[]> = this._onDidChangeDecorations.event;
private _actionButton: SourceControlActionButton | undefined;
get actionButton(): SourceControlActionButton | undefined { return this._actionButton; }
set actionButton(button: SourceControlActionButton | undefined) {
this._actionButton = button;
this._onDidChangeActionButton.fire();
}
private _currentHistoryItemGroup: SourceControlHistoryItemGroup | undefined;
get currentHistoryItemGroup(): SourceControlHistoryItemGroup | undefined { return this._currentHistoryItemGroup; }
set currentHistoryItemGroup(value: SourceControlHistoryItemGroup | undefined) {
this._currentHistoryItemGroup = value;
this._onDidChangeCurrentHistoryItemGroup.fire();
}
private historyItemDecorations = new Map<string, FileDecoration>();
private disposables: Disposable[] = [];
constructor(protected readonly repository: Repository) {
const actionButton = new SyncActionButton(repository);
this.actionButton = actionButton.button;
this.disposables.push(actionButton);
this.disposables.push(repository.onDidRunGitStatus(this.onDidRunGitStatus, this));
this.disposables.push(actionButton.onDidChange(() => this.actionButton = actionButton.button));
this.disposables.push(window.registerFileDecorationProvider(this));
}
private async onDidRunGitStatus(): Promise<void> {
if (!this.repository.HEAD?.name || !this.repository.HEAD?.commit) { return; }
this.currentHistoryItemGroup = {
id: `refs/heads/${this.repository.HEAD.name}`,
label: this.repository.HEAD.name,
upstream: this.repository.HEAD.upstream ?
{
id: `refs/remotes/${this.repository.HEAD.upstream.remote}/${this.repository.HEAD.upstream.name}`,
label: `${this.repository.HEAD.upstream.remote}/${this.repository.HEAD.upstream.name}`,
} : undefined
};
}
async provideHistoryItems(historyItemGroupId: string, options: SourceControlHistoryOptions): Promise<SourceControlHistoryItem[]> {
//TODO@lszomoru - support limit and cursor
if (typeof options.limit === 'number') {
throw new Error('Unsupported options.');
}
if (typeof options.limit?.id !== 'string') {
throw new Error('Unsupported options.');
}
const optionsRef = options.limit.id;
const [commits, summary] = await Promise.all([
this.repository.log({ range: `${optionsRef}..${historyItemGroupId}`, sortByAuthorDate: true }),
this.getSummaryHistoryItem(optionsRef, historyItemGroupId)
]);
const historyItems = commits.length === 0 ? [] : [summary];
historyItems.push(...commits.map(commit => {
const newLineIndex = commit.message.indexOf('\n');
const subject = newLineIndex !== -1 ? commit.message.substring(0, newLineIndex) : commit.message;
return {
id: commit.hash,
parentIds: commit.parents,
label: subject,
description: commit.authorName,
icon: new ThemeIcon('account'),
timestamp: commit.authorDate?.getTime()
};
}));
return historyItems;
}
async provideHistoryItemChanges(historyItemId: string): Promise<SourceControlHistoryItemChange[]> {
const [ref1, ref2] = historyItemId.includes('..')
? historyItemId.split('..')
: [`${historyItemId}^`, historyItemId];
const historyItemChangesUri: Uri[] = [];
const historyItemChanges: SourceControlHistoryItemChange[] = [];
const changes = await this.repository.diffBetween(ref1, ref2);
for (const change of changes) {
const historyItemUri = change.uri.with({
query: `ref=${historyItemId}`
});
// History item change
historyItemChanges.push({
uri: historyItemUri,
originalUri: toGitUri(change.originalUri, ref1),
modifiedUri: toGitUri(change.originalUri, ref2),
renameUri: change.renameUri,
});
// History item change decoration
const fileDecoration = this.historyItemChangeFileDecoration(change.status);
this.historyItemDecorations.set(historyItemUri.toString(), fileDecoration);
historyItemChangesUri.push(historyItemUri);
}
this._onDidChangeDecorations.fire(historyItemChangesUri);
return historyItemChanges;
}
async resolveHistoryItemGroupBase(historyItemGroupId: string): Promise<SourceControlHistoryItemGroup | undefined> {
// TODO - support for all history item groups
if (historyItemGroupId !== this.currentHistoryItemGroup?.id) {
return undefined;
}
if (this.currentHistoryItemGroup?.upstream) {
return this.currentHistoryItemGroup.upstream;
}
// Default branch
const defaultBranch = await this.repository.getDefaultBranch();
return defaultBranch.name ? { id: `refs/heads/${defaultBranch.name}`, label: defaultBranch.name } : undefined;
}
async resolveHistoryItemGroupCommonAncestor(refId1: string, refId2: string): Promise<{ id: string; ahead: number; behind: number } | undefined> {
const ancestor = await this.repository.getMergeBase(refId1, refId2);
if (ancestor === '') {
return undefined;
}
const commitCount = await this.repository.getCommitCount(`${refId1}...${refId2}`);
return { id: ancestor, ahead: commitCount.ahead, behind: commitCount.behind };
}
provideFileDecoration(uri: Uri): FileDecoration | undefined {
return this.historyItemDecorations.get(uri.toString());
}
private historyItemChangeFileDecoration(status: Status): FileDecoration {
const letter = Resource.getStatusLetter(status);
const tooltip = Resource.getStatusText(status);
const color = Resource.getStatusColor(status);
const fileDecoration = new FileDecoration(letter, tooltip, color);
fileDecoration.propagate = status !== Status.DELETED && status !== Status.INDEX_DELETED;
return fileDecoration;
}
private async getSummaryHistoryItem(ref1: string, ref2: string): Promise<SourceControlHistoryItem> {
const diffShortStat = await this.repository.diffBetweenShortStat(ref1, ref2);
return { id: `${ref1}..${ref2}`, parentIds: [], icon: new ThemeIcon('files'), label: l10n.t('All Changes'), description: diffShortStat };
}
dispose(): void {
this.disposables.forEach(d => d.dispose());
}
}

View file

@ -53,6 +53,7 @@ export const enum OperationKind {
RebaseContinue = 'RebaseContinue',
RevertFiles = 'RevertFiles',
RevertFilesNoProgress = 'RevertFilesNoProgress',
RevList = 'RevList',
SetBranchUpstream = 'SetBranchUpstream',
Show = 'Show',
Stage = 'Stage',
@ -69,8 +70,9 @@ export type Operation = AddOperation | ApplyOperation | BlameOperation | BranchO
GetBranchOperation | GetBranchesOperation | GetCommitTemplateOperation | GetObjectDetailsOperation | GetRefsOperation | GetRemoteRefsOperation |
HashObjectOperation | IgnoreOperation | LogOperation | LogFileOperation | MergeOperation | MergeAbortOperation | MergeBaseOperation |
MoveOperation | PostCommitCommandOperation | PullOperation | PushOperation | RemoteOperation | RenameBranchOperation | RemoveOperation |
ResetOperation | RebaseOperation | RebaseAbortOperation | RebaseContinueOperation | RevertFilesOperation | SetBranchUpstreamOperation |
ShowOperation | StageOperation | StatusOperation | StashOperation | SubmoduleUpdateOperation | SyncOperation | TagOperation;
ResetOperation | RebaseOperation | RebaseAbortOperation | RebaseContinueOperation | RevertFilesOperation | RevListOperation |
SetBranchUpstreamOperation | ShowOperation | StageOperation | StatusOperation | StashOperation | SubmoduleUpdateOperation | SyncOperation |
TagOperation;
type BaseOperation = { kind: OperationKind; blocking: boolean; readOnly: boolean; remote: boolean; retry: boolean; showProgress: boolean };
export type AddOperation = BaseOperation & { kind: OperationKind.Add };
@ -116,6 +118,7 @@ export type RebaseOperation = BaseOperation & { kind: OperationKind.Rebase };
export type RebaseAbortOperation = BaseOperation & { kind: OperationKind.RebaseAbort };
export type RebaseContinueOperation = BaseOperation & { kind: OperationKind.RebaseContinue };
export type RevertFilesOperation = BaseOperation & { kind: OperationKind.RevertFiles };
export type RevListOperation = BaseOperation & { kind: OperationKind.RevList };
export type SetBranchUpstreamOperation = BaseOperation & { kind: OperationKind.SetBranchUpstream };
export type ShowOperation = BaseOperation & { kind: OperationKind.Show };
export type StageOperation = BaseOperation & { kind: OperationKind.Stage };
@ -169,6 +172,7 @@ export const Operation = {
RebaseAbort: { kind: OperationKind.RebaseAbort, blocking: false, readOnly: false, remote: false, retry: false, showProgress: true } as RebaseAbortOperation,
RebaseContinue: { kind: OperationKind.RebaseContinue, blocking: false, readOnly: false, remote: false, retry: false, showProgress: true } as RebaseContinueOperation,
RevertFiles: (showProgress: boolean) => ({ kind: OperationKind.RevertFiles, blocking: false, readOnly: false, remote: false, retry: false, showProgress } as RevertFilesOperation),
RevList: { kind: OperationKind.RevList, blocking: false, readOnly: true, remote: false, retry: false, showProgress: false } as RevListOperation,
SetBranchUpstream: { kind: OperationKind.SetBranchUpstream, blocking: false, readOnly: false, remote: false, retry: false, showProgress: true } as SetBranchUpstreamOperation,
Show: { kind: OperationKind.Show, blocking: false, readOnly: true, remote: false, retry: false, showProgress: false } as ShowOperation,
Stage: { kind: OperationKind.Stage, blocking: false, readOnly: false, remote: false, retry: false, showProgress: true } as StageOperation,

View file

@ -19,10 +19,11 @@ import { IFileWatcher, watch } from './watch';
import { IPushErrorHandlerRegistry } from './pushError';
import { ApiRepository } from './api/api1';
import { IRemoteSourcePublisherRegistry } from './remotePublisher';
import { ActionButtonCommand } from './actionButton';
import { CommitActionButton } from './actionButton';
import { IPostCommitCommandsProviderRegistry, CommitCommandsCenter } from './postCommitCommands';
import { Operation, OperationKind, OperationManager, OperationResult } from './operation';
import { GitBranchProtectionProvider, IBranchProtectionProviderRegistry } from './branchProtection';
import { GitHistoryProvider } from './historyProvider';
const timeout = (millis: number) => new Promise(c => setTimeout(c, millis));
@ -46,6 +47,43 @@ export const enum ResourceGroupType {
export class Resource implements SourceControlResourceState {
static getStatusLetter(type: Status): string {
switch (type) {
case Status.INDEX_MODIFIED:
case Status.MODIFIED:
return 'M';
case Status.INDEX_ADDED:
case Status.INTENT_TO_ADD:
return 'A';
case Status.INDEX_DELETED:
case Status.DELETED:
return 'D';
case Status.INDEX_RENAMED:
case Status.INTENT_TO_RENAME:
return 'R';
case Status.TYPE_CHANGED:
return 'T';
case Status.UNTRACKED:
return 'U';
case Status.IGNORED:
return 'I';
case Status.DELETED_BY_THEM:
return 'D';
case Status.DELETED_BY_US:
return 'D';
case Status.INDEX_COPIED:
return 'C';
case Status.BOTH_DELETED:
case Status.ADDED_BY_US:
case Status.ADDED_BY_THEM:
case Status.BOTH_ADDED:
case Status.BOTH_MODIFIED:
return '!'; // Using ! instead of ⚠, because the latter looks really bad on windows
default:
throw new Error('Unknown git status: ' + type);
}
}
static getStatusText(type: Status) {
switch (type) {
case Status.INDEX_MODIFIED: return l10n.t('Index Modified');
@ -71,6 +109,41 @@ export class Resource implements SourceControlResourceState {
}
}
static getStatusColor(type: Status): ThemeColor {
switch (type) {
case Status.INDEX_MODIFIED:
return new ThemeColor('gitDecoration.stageModifiedResourceForeground');
case Status.MODIFIED:
case Status.TYPE_CHANGED:
return new ThemeColor('gitDecoration.modifiedResourceForeground');
case Status.INDEX_DELETED:
return new ThemeColor('gitDecoration.stageDeletedResourceForeground');
case Status.DELETED:
return new ThemeColor('gitDecoration.deletedResourceForeground');
case Status.INDEX_ADDED:
case Status.INTENT_TO_ADD:
return new ThemeColor('gitDecoration.addedResourceForeground');
case Status.INDEX_COPIED:
case Status.INDEX_RENAMED:
case Status.INTENT_TO_RENAME:
return new ThemeColor('gitDecoration.renamedResourceForeground');
case Status.UNTRACKED:
return new ThemeColor('gitDecoration.untrackedResourceForeground');
case Status.IGNORED:
return new ThemeColor('gitDecoration.ignoredResourceForeground');
case Status.BOTH_DELETED:
case Status.ADDED_BY_US:
case Status.DELETED_BY_THEM:
case Status.ADDED_BY_THEM:
case Status.DELETED_BY_US:
case Status.BOTH_ADDED:
case Status.BOTH_MODIFIED:
return new ThemeColor('gitDecoration.conflictingResourceForeground');
default:
throw new Error('Unknown git status: ' + type);
}
}
@memoize
get resourceUri(): Uri {
if (this.renameResourceUri && (this._type === Status.MODIFIED || this._type === Status.DELETED || this._type === Status.INDEX_RENAMED || this._type === Status.INDEX_COPIED || this._type === Status.INTENT_TO_RENAME)) {
@ -188,75 +261,11 @@ export class Resource implements SourceControlResourceState {
}
get letter(): string {
switch (this.type) {
case Status.INDEX_MODIFIED:
case Status.MODIFIED:
return 'M';
case Status.INDEX_ADDED:
case Status.INTENT_TO_ADD:
return 'A';
case Status.INDEX_DELETED:
case Status.DELETED:
return 'D';
case Status.INDEX_RENAMED:
case Status.INTENT_TO_RENAME:
return 'R';
case Status.TYPE_CHANGED:
return 'T';
case Status.UNTRACKED:
return 'U';
case Status.IGNORED:
return 'I';
case Status.DELETED_BY_THEM:
return 'D';
case Status.DELETED_BY_US:
return 'D';
case Status.INDEX_COPIED:
return 'C';
case Status.BOTH_DELETED:
case Status.ADDED_BY_US:
case Status.ADDED_BY_THEM:
case Status.BOTH_ADDED:
case Status.BOTH_MODIFIED:
return '!'; // Using ! instead of ⚠, because the latter looks really bad on windows
default:
throw new Error('Unknown git status: ' + this.type);
}
return Resource.getStatusLetter(this.type);
}
get color(): ThemeColor {
switch (this.type) {
case Status.INDEX_MODIFIED:
return new ThemeColor('gitDecoration.stageModifiedResourceForeground');
case Status.MODIFIED:
case Status.TYPE_CHANGED:
return new ThemeColor('gitDecoration.modifiedResourceForeground');
case Status.INDEX_DELETED:
return new ThemeColor('gitDecoration.stageDeletedResourceForeground');
case Status.DELETED:
return new ThemeColor('gitDecoration.deletedResourceForeground');
case Status.INDEX_ADDED:
case Status.INTENT_TO_ADD:
return new ThemeColor('gitDecoration.addedResourceForeground');
case Status.INDEX_COPIED:
case Status.INDEX_RENAMED:
case Status.INTENT_TO_RENAME:
return new ThemeColor('gitDecoration.renamedResourceForeground');
case Status.UNTRACKED:
return new ThemeColor('gitDecoration.untrackedResourceForeground');
case Status.IGNORED:
return new ThemeColor('gitDecoration.ignoredResourceForeground');
case Status.BOTH_DELETED:
case Status.ADDED_BY_US:
case Status.DELETED_BY_THEM:
case Status.ADDED_BY_THEM:
case Status.DELETED_BY_US:
case Status.BOTH_ADDED:
case Status.BOTH_MODIFIED:
return new ThemeColor('gitDecoration.conflictingResourceForeground');
default:
throw new Error('Unknown git status: ' + this.type);
}
return Resource.getStatusColor(this.type);
}
get priority(): number {
@ -833,8 +842,13 @@ export class Repository implements Disposable {
const root = Uri.file(repository.root);
this._sourceControl = scm.createSourceControl('git', 'Git', root);
this._sourceControl.acceptInputCommand = { command: 'git.commit', title: l10n.t('Commit'), arguments: [this._sourceControl] };
this._sourceControl.quickDiffProvider = this;
const historyProvider = new GitHistoryProvider(this);
this._sourceControl.historyProvider = historyProvider;
this.disposables.push(historyProvider);
this._sourceControl.acceptInputCommand = { command: 'git.commit', title: l10n.t('Commit'), arguments: [this._sourceControl] };
this._sourceControl.inputBox.validateInput = this.validateInput.bind(this);
this.disposables.push(this._sourceControl);
@ -921,10 +935,10 @@ export class Repository implements Disposable {
this.commitCommandCenter = new CommitCommandsCenter(globalState, this, postCommitCommandsProviderRegistry);
this.disposables.push(this.commitCommandCenter);
const actionButton = new ActionButtonCommand(this, this.commitCommandCenter);
this.disposables.push(actionButton);
actionButton.onDidChange(() => this._sourceControl.actionButton = actionButton.button);
this._sourceControl.actionButton = actionButton.button;
const commitActionButton = new CommitActionButton(this, this.commitCommandCenter);
this.disposables.push(commitActionButton);
commitActionButton.onDidChange(() => this._sourceControl.actionButton = commitActionButton.button);
this._sourceControl.actionButton = commitActionButton.button;
const progressManager = new ProgressManager(this);
this.disposables.push(progressManager);
@ -1115,6 +1129,10 @@ export class Repository implements Disposable {
return this.run(Operation.Diff, () => this.repository.diffBetween(ref1, ref2, path));
}
diffBetweenShortStat(ref1: string, ref2: string): Promise<string> {
return this.run(Operation.Diff, () => this.repository.diffBetweenShortStat(ref1, ref2));
}
getMergeBase(ref1: string, ref2: string): Promise<string> {
return this.run(Operation.MergeBase, () => this.repository.getMergeBase(ref1, ref2));
}
@ -1421,6 +1439,10 @@ export class Repository implements Disposable {
await this.run(Operation.Move, () => this.repository.move(from, to));
}
async getDefaultBranch(): Promise<Branch> {
return await this.run(Operation.GetBranch, () => this.repository.getDefaultBranch());
}
async getBranch(name: string): Promise<Branch> {
return await this.run(Operation.GetBranch, () => this.repository.getBranch(name));
}
@ -1506,6 +1528,10 @@ export class Repository implements Disposable {
return await this.repository.getCommit(ref);
}
async getCommitCount(range: string): Promise<{ ahead: number; behind: number }> {
return await this.run(Operation.RevList, () => this.repository.getCommitCount(range));
}
async reset(treeish: string, hard?: boolean): Promise<void> {
await this.run(Operation.Reset, () => this.repository.reset(treeish, hard));
}

View file

@ -12,6 +12,7 @@
"../../src/vscode-dts/vscode.d.ts",
"../../src/vscode-dts/vscode.proposed.diffCommand.d.ts",
"../../src/vscode-dts/vscode.proposed.scmActionButton.d.ts",
"../../src/vscode-dts/vscode.proposed.scmHistoryProvider.d.ts",
"../../src/vscode-dts/vscode.proposed.scmSelectedProvider.d.ts",
"../../src/vscode-dts/vscode.proposed.scmValidation.d.ts",
"../../src/vscode-dts/vscode.proposed.tabInputTextMerge.d.ts",

View file

@ -4,6 +4,7 @@
"description": "Extension to add Grunt capabilities to VS Code.",
"displayName": "Grunt support for VS Code",
"version": "1.0.0",
"private": true,
"icon": "images/grunt.png",
"license": "MIT",
"engines": {

View file

@ -3,6 +3,7 @@
"displayName": "%displayName%",
"description": "%description%",
"version": "1.0.0",
"private": true,
"publisher": "vscode",
"license": "MIT",
"engines": {

View file

@ -4,6 +4,7 @@
"displayName": "%displayName%",
"description": "%description%",
"version": "1.0.1",
"private": true,
"license": "MIT",
"engines": {
"vscode": "0.10.x"

View file

@ -6,7 +6,7 @@
"git": {
"name": "jeff-hykin/better-shell-syntax",
"repositoryUrl": "https://github.com/jeff-hykin/better-shell-syntax",
"commitHash": "ce62ea59e8e522f8a07d8d8a2d1f992c6c600b91"
"commitHash": "a3de7b32f1537194a83ee848838402fbf4b67424"
}
},
"license": "MIT",

View file

@ -4,7 +4,7 @@
"If you want to provide a fix or improvement, please create a pull request against the original repository.",
"Once accepted there, we are happy to receive an update request."
],
"version": "https://github.com/jeff-hykin/better-shell-syntax/commit/ce62ea59e8e522f8a07d8d8a2d1f992c6c600b91",
"version": "https://github.com/jeff-hykin/better-shell-syntax/commit/a3de7b32f1537194a83ee848838402fbf4b67424",
"name": "Shell Script",
"scopeName": "source.shell",
"patterns": [
@ -268,7 +268,7 @@
"patterns": [
{
"begin": "(?=\\S)",
"end": ";;",
"end": ";;&?|;&",
"endCaptures": {
"0": {
"name": "punctuation.terminator.case-clause.shell"
@ -311,7 +311,7 @@
},
{
"begin": "(?<=\\))",
"end": "(?=;;)",
"end": "(?=;;&?|;&)",
"name": "meta.scope.case-clause-body.shell",
"patterns": [
{

View file

@ -206,7 +206,7 @@ class DiagnosticsTelemetryManager extends Disposable {
this._diagnosticCodesMap.clear();
/* __GDPR__
"typescript.diagnostics" : {
"owner": "@aiday-mar",
"owner": "aiday-mar",
"diagnosticCodes" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
"${include}": [
"${TypeScriptCommonProperties}"
@ -214,7 +214,7 @@ class DiagnosticsTelemetryManager extends Disposable {
}
*/
this._telemetryReporter.logTelemetry('typescript.diagnostics', {
diagnoticCodes: diagnosticCodes
diagnosticCodes: diagnosticCodes
});
}
}, 5 * 60 * 1000); // 5 minutes

View file

@ -1,7 +1,7 @@
{
"name": "code-oss-dev",
"version": "1.83.0",
"distro": "c13fa037c20b672a48c9e7990df10998974196f9",
"distro": "5a794f00ac87e37b6abea46ccf030aeb4b323aeb",
"author": {
"name": "Microsoft Corporation"
},
@ -70,7 +70,7 @@
"@parcel/watcher": "2.1.0",
"@vscode/iconv-lite-umd": "0.7.0",
"@vscode/policy-watcher": "^1.1.4",
"@vscode/proxy-agent": "^0.17.2",
"@vscode/proxy-agent": "^0.17.4",
"@vscode/ripgrep": "^1.15.5",
"@vscode/spdlog": "^0.13.11",
"@vscode/sqlite3": "5.1.6-vscode",
@ -210,7 +210,7 @@
"ts-loader": "^9.4.2",
"ts-node": "^10.9.1",
"tsec": "0.2.7",
"typescript": "^5.3.0-dev.20230911",
"typescript": "^5.3.0-dev.20230919",
"typescript-formatter": "7.1.0",
"underscore": "^1.12.1",
"util": "^0.12.4",

View file

@ -7,7 +7,7 @@
"@microsoft/1ds-post-js": "^3.2.13",
"@parcel/watcher": "2.1.0",
"@vscode/iconv-lite-umd": "0.7.0",
"@vscode/proxy-agent": "^0.17.2",
"@vscode/proxy-agent": "^0.17.4",
"@vscode/ripgrep": "^1.15.5",
"@vscode/spdlog": "^0.13.11",
"@vscode/vscode-languagedetection": "1.0.21",

View file

@ -58,10 +58,10 @@
resolved "https://registry.yarnpkg.com/@vscode/iconv-lite-umd/-/iconv-lite-umd-0.7.0.tgz#d2f1e0664ee6036408f9743fee264ea0699b0e48"
integrity sha512-bRRFxLfg5dtAyl5XyiVWz/ZBPahpOpPrNYnnHpOpUZvam4tKH35wdhP4Kj6PbM0+KdliOsPzbGWpkxcdpNB/sg==
"@vscode/proxy-agent@^0.17.2":
version "0.17.2"
resolved "https://registry.yarnpkg.com/@vscode/proxy-agent/-/proxy-agent-0.17.2.tgz#0e0dac24478e2d71a4fd1b2bb5f84dc61add79e2"
integrity sha512-aKRo1YfUCsgEjHvr2HXfM6dwHhieyO6G+WHly7jewyyTJ1nANWEocS3JRnRbC4KjlajKhSUEOx838cdnY/vRtA==
"@vscode/proxy-agent@^0.17.4":
version "0.17.4"
resolved "https://registry.yarnpkg.com/@vscode/proxy-agent/-/proxy-agent-0.17.4.tgz#e3ffb63357353a428436f15a69de3453a5061f0c"
integrity sha512-tX8eidofoJlZFRWzdiiW3wyu26hgIRk8HvM/RoP1wVSu3U/As36EgGIZYG6pPnqiythRqTcsddniVNA5M39g4w==
dependencies:
"@tootallnate/once" "^3.0.0"
agent-base "^7.0.1"

View file

@ -17,6 +17,29 @@ import { FileAccess, RemoteAuthorities, Schemas } from 'vs/base/common/network';
import * as platform from 'vs/base/common/platform';
import { URI } from 'vs/base/common/uri';
export const { registerWindow, getWindows, onDidCreateWindow } = (function () {
const windows: Window[] = [];
const onDidCreateWindow = new event.Emitter<{ window: Window; disposableStore: DisposableStore }>();
return {
onDidCreateWindow: onDidCreateWindow.event,
registerWindow(window: Window): IDisposable {
windows.push(window);
const disposableStore = new DisposableStore();
disposableStore.add(toDisposable(() => {
const index = windows.indexOf(window);
if (index !== -1) {
windows.splice(index, 1);
}
}));
onDidCreateWindow.fire({ window, disposableStore });
return disposableStore;
},
getWindows(): Window[] {
return windows;
}
};
})();
export function clearNode(node: HTMLElement): void {
while (node.firstChild) {
node.firstChild.remove();
@ -282,34 +305,37 @@ export function addDisposableThrottledListener<R, E extends Event = Event>(node:
}
export function getComputedStyle(el: HTMLElement): CSSStyleDeclaration {
return document.defaultView!.getComputedStyle(el, null);
return el.ownerDocument.defaultView!.getComputedStyle(el, null);
}
export function getClientArea(element: HTMLElement): Dimension {
const elDocument = element.ownerDocument;
const elWindow = elDocument.defaultView?.window;
// Try with DOM clientWidth / clientHeight
if (element !== document.body) {
if (element !== elDocument.body) {
return new Dimension(element.clientWidth, element.clientHeight);
}
// If visual view port exits and it's on mobile, it should be used instead of window innerWidth / innerHeight, or document.body.clientWidth / document.body.clientHeight
if (platform.isIOS && window.visualViewport) {
return new Dimension(window.visualViewport.width, window.visualViewport.height);
if (platform.isIOS && elWindow?.visualViewport) {
return new Dimension(elWindow.visualViewport.width, elWindow.visualViewport.height);
}
// Try innerWidth / innerHeight
if (window.innerWidth && window.innerHeight) {
return new Dimension(window.innerWidth, window.innerHeight);
if (elWindow?.innerWidth && elWindow.innerHeight) {
return new Dimension(elWindow.innerWidth, elWindow.innerHeight);
}
// Try with document.body.clientWidth / document.body.clientHeight
if (document.body && document.body.clientWidth && document.body.clientHeight) {
return new Dimension(document.body.clientWidth, document.body.clientHeight);
if (elDocument.body && elDocument.body.clientWidth && elDocument.body.clientHeight) {
return new Dimension(elDocument.body.clientWidth, elDocument.body.clientHeight);
}
// Try with document.documentElement.clientWidth / document.documentElement.clientHeight
if (document.documentElement && document.documentElement.clientWidth && document.documentElement.clientHeight) {
return new Dimension(document.documentElement.clientWidth, document.documentElement.clientHeight);
if (elDocument.documentElement && elDocument.documentElement.clientWidth && elDocument.documentElement.clientHeight) {
return new Dimension(elDocument.documentElement.clientWidth, elDocument.documentElement.clientHeight);
}
throw new Error('Unable to figure out browser width and height');
@ -431,8 +457,8 @@ export function getTopLeftOffset(element: HTMLElement): IDomPosition {
while (
(element = <HTMLElement>element.parentNode) !== null
&& element !== document.body
&& element !== document.documentElement
&& element !== element.ownerDocument.body
&& element !== element.ownerDocument.documentElement
) {
top -= element.scrollTop;
const c = isShadowRoot(element) ? null : getComputedStyle(element);
@ -498,8 +524,8 @@ export function position(element: HTMLElement, top: number, right?: number, bott
export function getDomNodePagePosition(domNode: HTMLElement): IDomNodePagePosition {
const bb = domNode.getBoundingClientRect();
return {
left: bb.left + window.scrollX,
top: bb.top + window.scrollY,
left: bb.left + (domNode.ownerDocument.defaultView?.scrollX ?? 0),
top: bb.top + (domNode.ownerDocument.defaultView?.scrollY ?? 0),
width: bb.width,
height: bb.height
};
@ -518,7 +544,7 @@ export function getDomNodeZoomLevel(domNode: HTMLElement): number {
}
testElement = testElement.parentElement;
} while (testElement !== null && testElement !== document.documentElement);
} while (testElement !== null && testElement !== testElement.ownerDocument.documentElement);
return zoom;
}
@ -602,7 +628,7 @@ export function setParentFlowTo(fromChildElement: HTMLElement, toParentElement:
function getParentFlowToElement(node: HTMLElement): HTMLElement | null {
const flowToParentId = node.dataset[parentFlowToDataKey];
if (typeof flowToParentId === 'string') {
return document.getElementById(flowToParentId);
return node.ownerDocument.getElementById(flowToParentId);
}
return null;
}
@ -671,7 +697,7 @@ export function isInShadowDOM(domNode: Node): boolean {
export function getShadowRoot(domNode: Node): ShadowRoot | null {
while (domNode.parentNode) {
if (domNode === document.body) {
if (domNode === domNode.ownerDocument?.body) {
// reached the body
return null;
}
@ -680,8 +706,12 @@ export function getShadowRoot(domNode: Node): ShadowRoot | null {
return isShadowRoot(domNode) ? domNode : null;
}
/**
* Returns the active element across all child windows.
* Use this instead of `document.activeElement` to handle multiple windows.
*/
export function getActiveElement(): Element | null {
let result = document.activeElement;
let result = getActiveDocument().activeElement;
while (result?.shadowRoot) {
result = result.shadowRoot.activeElement;
@ -690,6 +720,15 @@ export function getActiveElement(): Element | null {
return result;
}
/**
* Returns the active document across all child windows.
* Use this instead of `document` when reacting to dom events to handle multiple windows.
*/
export function getActiveDocument(): Document {
const documents = [document, ...getWindows().map(w => w.document)];
return documents.find(doc => doc.hasFocus()) ?? document;
}
export function createStyleSheet(container: HTMLElement = document.getElementsByTagName('head')[0], beforeAppend?: (style: HTMLStyleElement) => void): HTMLStyleElement {
const style = document.createElement('style');
style.type = 'text/css';
@ -875,15 +914,19 @@ class FocusTracker extends Disposable implements IFocusTracker {
private _refreshStateHandler: () => void;
private static hasFocusWithin(element: HTMLElement): boolean {
const shadowRoot = getShadowRoot(element);
const activeElement = (shadowRoot ? shadowRoot.activeElement : document.activeElement);
return isAncestor(activeElement, element);
private static hasFocusWithin(element: HTMLElement | Window): boolean {
if (isHTMLElement(element)) {
const shadowRoot = getShadowRoot(element);
const activeElement = (shadowRoot ? shadowRoot.activeElement : element.ownerDocument.activeElement);
return isAncestor(activeElement, element);
} else {
return isAncestor(window.document.activeElement, window.document);
}
}
constructor(element: HTMLElement | Window) {
super();
let hasFocus = FocusTracker.hasFocusWithin(<HTMLElement>element);
let hasFocus = FocusTracker.hasFocusWithin(element);
let loosingFocus = false;
const onFocus = () => {
@ -1092,7 +1135,7 @@ export function removeTabIndexAndUpdateFocus(node: HTMLElement): void {
// standard DOM behavior is to move focus to the <body> element. We
// typically never want that, rather put focus to the closest element
// in the hierarchy of the parent DOM nodes.
if (document.activeElement === node) {
if (node.ownerDocument.activeElement === node) {
const parentFocusable = findParentWithAttribute(node.parentElement, 'tabIndex');
parentFocusable?.focus();
}

View file

@ -69,8 +69,8 @@ export class StandardMouseEvent implements IMouseEvent {
this.posy = e.pageY;
} else {
// Probably hit by MSGestureEvent
this.posx = e.clientX + document.body.scrollLeft + document.documentElement!.scrollLeft;
this.posy = e.clientY + document.body.scrollTop + document.documentElement!.scrollTop;
this.posx = e.clientX + this.target.ownerDocument.body.scrollLeft + this.target.ownerDocument.documentElement.scrollLeft;
this.posy = e.clientY + this.target.ownerDocument.body.scrollTop + this.target.ownerDocument.documentElement.scrollTop;
}
// Find the position of the iframe this code is executing in relative to the iframe where the event was captured.

View file

@ -778,7 +778,7 @@ class LeafNode implements ISplitView<ILayoutContext>, IDisposable {
private _onDidViewChange: Event<number | undefined>;
readonly onDidChange: Event<number | undefined>;
private disposables = new DisposableStore();
private readonly disposables = new DisposableStore();
constructor(
readonly view: IView,

View file

@ -0,0 +1,38 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.icon-select-box > .icon-select-box-container {
height: 100%;
}
.icon-select-box .icon-select-icons-container {
height: 100%;
outline: 0 !important;
}
.icon-select-box .icon-select-icons-container > .icon-container {
display: inline-flex;
cursor: pointer;
font-size: 20px;
align-items: center;
justify-content: center;
border-radius: 5px;
}
.icon-select-box .icon-select-icons-container > .icon-container.focused {
outline: 1px dashed var(--vscode-toolbar-hoverOutline);
outline-offset: -1px;
background-color: var(--vscode-toolbar-hoverBackground);
}
.icon-select-box .icon-select-id-container .icon-select-id-label {
height: 24px;
padding: 10px;
opacity: .8;
}
.icon-select-box .icon-select-id-container .icon-select-id-label .highlight {
color: var(--vscode-list-highlightForeground);
}

View file

@ -0,0 +1,287 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./iconSelectBox';
import * as dom from 'vs/base/browser/dom';
import { alert } from 'vs/base/browser/ui/aria/aria';
import { IInputBoxStyles, InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { Emitter } from 'vs/base/common/event';
import { IDisposable, DisposableStore, Disposable, MutableDisposable } from 'vs/base/common/lifecycle';
import { ThemeIcon } from 'vs/base/common/themables';
import { localize } from 'vs/nls';
import { IMatch } from 'vs/base/common/filters';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel';
export interface IIconSelectBoxOptions {
readonly icons: ThemeIcon[];
readonly inputBoxStyles: IInputBoxStyles;
}
interface IRenderedIconItem {
readonly icon: ThemeIcon;
readonly element: HTMLElement;
readonly highlightMatches?: IMatch[];
}
export class IconSelectBox extends Disposable {
private static InstanceCount = 0;
readonly domId = `icon_select_box_id_${++IconSelectBox.InstanceCount}`;
readonly domNode: HTMLElement;
private _onDidSelect = this._register(new Emitter<ThemeIcon>());
readonly onDidSelect = this._onDidSelect.event;
private renderedIcons: IRenderedIconItem[] = [];
private focusedItemIndex: number = 0;
private numberOfElementsPerRow: number = 1;
protected inputBox: InputBox | undefined;
private scrollableElement: DomScrollableElement | undefined;
private iconIdElement: HighlightedLabel | undefined;
private readonly iconContainerWidth = 36;
private readonly iconContainerHeight = 32;
constructor(
private readonly options: IIconSelectBoxOptions,
) {
super();
this.domNode = dom.$('.icon-select-box');
this._register(this.create());
}
private create(): IDisposable {
const disposables = new DisposableStore();
const iconSelectBoxContainer = dom.append(this.domNode, dom.$('.icon-select-box-container'));
iconSelectBoxContainer.style.margin = '10px 15px';
const iconSelectInputContainer = dom.append(iconSelectBoxContainer, dom.$('.icon-select-input-container'));
iconSelectInputContainer.style.paddingBottom = '10px';
this.inputBox = disposables.add(new InputBox(iconSelectInputContainer, undefined, {
placeholder: localize('iconSelect.placeholder', "Search icons"),
inputBoxStyles: this.options.inputBoxStyles,
}));
const iconsContainer = dom.$('.icon-select-icons-container', { id: `${this.domId}_icons` });
iconsContainer.style.paddingRight = '10px';
iconsContainer.role = 'listbox';
iconsContainer.tabIndex = 0;
this.scrollableElement = disposables.add(new DomScrollableElement(iconsContainer, {
useShadows: false,
horizontal: ScrollbarVisibility.Hidden,
}));
dom.append(iconSelectBoxContainer, this.scrollableElement.getDomNode());
this.iconIdElement = new HighlightedLabel(dom.append(dom.append(iconSelectBoxContainer, dom.$('.icon-select-id-container')), dom.$('.icon-select-id-label')));
const iconsDisposables = disposables.add(new MutableDisposable());
iconsDisposables.value = this.renderIcons(this.options.icons, [], iconsContainer);
this.scrollableElement.scanDomNode();
disposables.add(this.inputBox.onDidChange(value => {
const icons = [], matches = [];
for (const icon of this.options.icons) {
const match = this.matchesContiguous(value, icon.id);
if (match) {
icons.push(icon);
matches.push(match);
}
}
iconsDisposables.value = this.renderIcons(icons, matches, iconsContainer);
this.scrollableElement?.scanDomNode();
}));
this.inputBox.inputElement.role = 'combobox';
this.inputBox.inputElement.ariaHasPopup = 'menu';
this.inputBox.inputElement.ariaAutoComplete = 'list';
this.inputBox.inputElement.ariaExpanded = 'true';
this.inputBox.inputElement.setAttribute('aria-controls', iconsContainer.id);
return disposables;
}
private renderIcons(icons: ThemeIcon[], matches: IMatch[][], container: HTMLElement): IDisposable {
const disposables = new DisposableStore();
dom.clearNode(container);
const focusedIcon = this.renderedIcons[this.focusedItemIndex]?.icon;
let focusedIconIndex = 0;
const renderedIcons: IRenderedIconItem[] = [];
if (icons.length) {
for (let index = 0; index < icons.length; index++) {
const icon = icons[index];
const iconContainer = dom.append(container, dom.$('.icon-container', { id: `${this.domId}_icons_${index}` }));
iconContainer.style.width = `${this.iconContainerWidth}px`;
iconContainer.style.height = `${this.iconContainerHeight}px`;
iconContainer.title = icon.id;
iconContainer.role = 'button';
iconContainer.setAttribute('aria-setsize', `${icons.length}`);
iconContainer.setAttribute('aria-posinset', `${index + 1}`);
dom.append(iconContainer, dom.$(ThemeIcon.asCSSSelector(icon)));
renderedIcons.push({ icon, element: iconContainer, highlightMatches: matches[index] });
disposables.add(dom.addDisposableListener(iconContainer, dom.EventType.CLICK, (e: MouseEvent) => {
e.stopPropagation();
this.setSelection(index);
}));
disposables.add(dom.addDisposableListener(iconContainer, dom.EventType.MOUSE_OVER, (e: MouseEvent) => {
this.focusIcon(index);
}));
if (icon === focusedIcon) {
focusedIconIndex = index;
}
}
} else {
const noResults = localize('iconSelect.noResults', "No results");
dom.append(container, dom.$('.icon-no-results', undefined, noResults));
alert(noResults);
}
this.renderedIcons.splice(0, this.renderedIcons.length, ...renderedIcons);
this.focusIcon(focusedIconIndex);
return disposables;
}
private focusIcon(index: number): void {
const existing = this.renderedIcons[this.focusedItemIndex];
if (existing) {
existing.element.classList.remove('focused');
}
this.focusedItemIndex = index;
const renderedItem = this.renderedIcons[index];
if (renderedItem) {
renderedItem.element.classList.add('focused');
}
if (this.inputBox) {
if (renderedItem) {
this.inputBox.inputElement.setAttribute('aria-activedescendant', renderedItem.element.id);
} else {
this.inputBox.inputElement.removeAttribute('aria-activedescendant');
}
}
if (this.iconIdElement) {
if (renderedItem) {
this.iconIdElement.set(renderedItem.icon.id, renderedItem.highlightMatches);
} else {
this.iconIdElement.set('');
}
}
this.reveal(index);
}
private reveal(index: number): void {
if (!this.scrollableElement) {
return;
}
if (index < 0 || index >= this.renderedIcons.length) {
return;
}
const element = this.renderedIcons[index].element;
if (!element) {
return;
}
const { height } = this.scrollableElement.getScrollDimensions();
const { scrollTop } = this.scrollableElement.getScrollPosition();
if (element.offsetTop + this.iconContainerHeight > scrollTop + height) {
this.scrollableElement.setScrollPosition({ scrollTop: element.offsetTop + this.iconContainerHeight - height });
} else if (element.offsetTop < scrollTop) {
this.scrollableElement.setScrollPosition({ scrollTop: element.offsetTop });
}
}
private matchesContiguous(word: string, wordToMatchAgainst: string): IMatch[] | null {
const matchIndex = wordToMatchAgainst.toLowerCase().indexOf(word.toLowerCase());
if (matchIndex !== -1) {
return [{ start: matchIndex, end: matchIndex + word.length }];
}
return null;
}
layout(dimension: dom.Dimension): void {
this.domNode.style.width = `${dimension.width}px`;
this.domNode.style.height = `${dimension.height}px`;
const iconsContainerWidth = dimension.width - 40;
this.numberOfElementsPerRow = Math.floor(iconsContainerWidth / this.iconContainerWidth);
if (this.numberOfElementsPerRow === 0) {
throw new Error('Insufficient width');
}
const extraSpace = iconsContainerWidth % this.iconContainerWidth;
const margin = Math.floor(extraSpace / this.numberOfElementsPerRow);
for (const { element } of this.renderedIcons) {
element.style.marginRight = `${margin}px`;
}
if (this.scrollableElement) {
this.scrollableElement.getDomNode().style.height = `${dimension.height - 80}px`;
this.scrollableElement.scanDomNode();
}
}
getFocus(): number[] {
return [this.focusedItemIndex];
}
setSelection(index: number): void {
if (index < 0 || index >= this.renderedIcons.length) {
throw new Error(`Invalid index ${index}`);
}
this.focusIcon(index);
this._onDidSelect.fire(this.renderedIcons[index].icon);
}
focus(): void {
this.inputBox?.focus();
this.focusIcon(0);
}
focusNext(): void {
this.focusIcon((this.focusedItemIndex + 1) % this.renderedIcons.length);
}
focusPrevious(): void {
this.focusIcon((this.focusedItemIndex - 1 + this.renderedIcons.length) % this.renderedIcons.length);
}
focusNextRow(): void {
let nextRowIndex = this.focusedItemIndex + this.numberOfElementsPerRow;
if (nextRowIndex >= this.renderedIcons.length) {
nextRowIndex = (nextRowIndex + 1) % this.numberOfElementsPerRow;
nextRowIndex = nextRowIndex >= this.renderedIcons.length ? 0 : nextRowIndex;
}
this.focusIcon(nextRowIndex);
}
focusPreviousRow(): void {
let previousRowIndex = this.focusedItemIndex - this.numberOfElementsPerRow;
if (previousRowIndex < 0) {
const numberOfRows = Math.floor(this.renderedIcons.length / this.numberOfElementsPerRow);
previousRowIndex = this.focusedItemIndex + (this.numberOfElementsPerRow * numberOfRows) - 1;
previousRowIndex = previousRowIndex < 0
? this.renderedIcons.length - 1
: previousRowIndex >= this.renderedIcons.length
? previousRowIndex - this.numberOfElementsPerRow
: previousRowIndex;
}
this.focusIcon(previousRowIndex);
}
getFocusedIcon(): ThemeIcon {
return this.renderedIcons[this.focusedItemIndex].icon;
}
}

View file

@ -173,7 +173,7 @@ interface IPointerEventFactory {
class MouseEventFactory implements IPointerEventFactory {
private disposables = new DisposableStore();
private readonly disposables = new DisposableStore();
@memoize
get onPointerMove(): Event<PointerEvent> {
@ -192,7 +192,7 @@ class MouseEventFactory implements IPointerEventFactory {
class GestureEventFactory implements IPointerEventFactory {
private disposables = new DisposableStore();
private readonly disposables = new DisposableStore();
@memoize
get onPointerMove(): Event<PointerEvent> {

View file

@ -1027,7 +1027,7 @@ class FindController<T, TFilterData> implements IDisposable {
private readonly _onDidChangeOpenState = new Emitter<boolean>();
readonly onDidChangeOpenState = this._onDidChangeOpenState.event;
private enabledDisposables = new DisposableStore();
private readonly enabledDisposables = new DisposableStore();
private readonly disposables = new DisposableStore();
constructor(
@ -1085,8 +1085,7 @@ class FindController<T, TFilterData> implements IDisposable {
this._history = this.widget.getHistory();
this.widget = undefined;
this.enabledDisposables.dispose();
this.enabledDisposables = new DisposableStore();
this.enabledDisposables.clear();
this.previousPattern = this.pattern;
this.onDidChangeValue('');

View file

@ -187,6 +187,12 @@ export function forEachAdjacent<T>(arr: T[], f: (item1: T | undefined, item2: T
}
}
export function forEachWithNeighbors<T>(arr: T[], f: (before: T | undefined, element: T, after: T | undefined) => void): void {
for (let i = 0; i < arr.length; i++) {
f(i === 0 ? undefined : arr[i - 1], arr[i], i + 1 === arr.length ? undefined : arr[i + 1]);
}
}
interface IMutableSplice<T> extends ISplice<T> {
readonly toInsert: T[];
deleteCount: number;

View file

@ -324,7 +324,7 @@ export const Codicon = {
note: register('note', 0xeb26),
octoface: register('octoface', 0xeb27),
openPreview: register('open-preview', 0xeb28),
package_: register('package', 0xeb29),
package: register('package', 0xeb29),
paintcan: register('paintcan', 0xeb2a),
pin: register('pin', 0xeb2b),
play: register('play', 0xeb2c),

View file

@ -120,14 +120,15 @@ export class HistoryNavigator2<T> {
private head: HistoryNode<T>;
private tail: HistoryNode<T>;
private cursor: HistoryNode<T>;
private size: number;
private _size: number;
get size(): number { return this._size; }
constructor(history: readonly T[], private capacity: number = 10) {
if (history.length < 1) {
throw new Error('not supported');
}
this.size = 1;
this._size = 1;
this.head = this.tail = this.cursor = {
value: history[0],
previous: undefined,
@ -150,7 +151,7 @@ export class HistoryNavigator2<T> {
this.tail.next = node;
this.tail = node;
this.cursor = this.tail;
this.size++;
this._size++;
if (this.valueSet.has(value)) {
this._deleteFromList(value);
@ -158,12 +159,12 @@ export class HistoryNavigator2<T> {
this.valueSet.add(value);
}
while (this.size > this.capacity) {
while (this._size > this.capacity) {
this.valueSet.delete(this.head.value);
this.head = this.head.next!;
this.head.previous = undefined;
this.size--;
this._size--;
}
}
@ -188,6 +189,24 @@ export class HistoryNavigator2<T> {
return oldValue;
}
prepend(value: T): void {
if (this._size === this.capacity || this.valueSet.has(value)) {
return;
}
const node: HistoryNode<T> = {
value,
previous: undefined,
next: this.head
};
this.head.previous = node;
this.head = node;
this._size++;
this.valueSet.add(value);
}
isAtEnd(): boolean {
return this.cursor === this.tail;
}
@ -243,7 +262,7 @@ export class HistoryNavigator2<T> {
temp.next!.previous = temp.previous;
}
this.size--;
this._size--;
}
temp = temp.next!;

View file

@ -30,6 +30,12 @@ export namespace Iterable {
return iterable || _empty;
}
export function* reverse<T>(array: Array<T>): Iterable<T> {
for (let i = array.length - 1; i >= 0; i--) {
yield array[i];
}
}
export function isEmpty<T>(iterable: Iterable<T> | undefined | null): boolean {
return !iterable || iterable[Symbol.iterator]().next().done === true;
}

View file

@ -41,6 +41,10 @@ class ResourceMapEntry<T> {
constructor(readonly uri: URI, readonly value: T) { }
}
function isEntries<T>(arg: ResourceMap<T> | ResourceMapKeyFn | readonly (readonly [URI, T])[] | undefined): arg is readonly (readonly [URI, T])[] {
return Array.isArray(arg);
}
export class ResourceMap<T> implements Map<URI, T> {
private static readonly defaultToKey = (resource: URI) => resource.toString();
@ -63,13 +67,27 @@ export class ResourceMap<T> implements Map<URI, T> {
*/
constructor(other?: ResourceMap<T>, toKey?: ResourceMapKeyFn);
constructor(mapOrKeyFn?: ResourceMap<T> | ResourceMapKeyFn, toKey?: ResourceMapKeyFn) {
if (mapOrKeyFn instanceof ResourceMap) {
this.map = new Map(mapOrKeyFn.map);
/**
*
* @param other Another resource which this maps is created from
* @param toKey Custom uri identity function, e.g use an existing `IExtUri#getComparison`-util
*/
constructor(entries?: readonly (readonly [URI, T])[], toKey?: ResourceMapKeyFn);
constructor(arg?: ResourceMap<T> | ResourceMapKeyFn | readonly (readonly [URI, T])[], toKey?: ResourceMapKeyFn) {
if (arg instanceof ResourceMap) {
this.map = new Map(arg.map);
this.toKey = toKey ?? ResourceMap.defaultToKey;
} else if (isEntries(arg)) {
this.map = new Map();
this.toKey = toKey ?? ResourceMap.defaultToKey;
for (const [resource, value] of arg) {
this.set(resource, value);
}
} else {
this.map = new Map();
this.toKey = mapOrKeyFn ?? ResourceMap.defaultToKey;
this.toKey = arg ?? ResourceMap.defaultToKey;
}
}

View file

@ -3,7 +3,6 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export const enum MarshalledId {
Uri = 1,
Regexp,
@ -19,6 +18,7 @@ export const enum MarshalledId {
TimelineActionContext,
NotebookCellActionContext,
NotebookActionContext,
TerminalContext,
TestItemContext,
Date,
TestMessageMenuArgs,

241
src/vs/base/common/tfIdf.ts Normal file
View file

@ -0,0 +1,241 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CancellationToken } from 'vs/base/common/cancellation';
type SparseEmbedding = Record</* word */ string, /* weight */number>;
type TermFrequencies = Map</* word */ string, /*occurrences*/ number>;
type DocumentOccurrences = Map</* word */ string, /*documentOccurrences*/ number>;
function countMapFrom<K>(values: Iterable<K>): Map<K, number> {
const map = new Map<K, number>();
for (const value of values) {
map.set(value, (map.get(value) ?? 0) + 1);
}
return map;
}
interface DocumentChunkEntry {
readonly text: string;
readonly tf: TermFrequencies;
}
export interface TfIdfDocument {
readonly key: string;
readonly textChunks: readonly string[];
}
export interface TfIdfScore {
readonly key: string;
/**
* An unbounded number.
*/
readonly score: number;
}
export interface NormalizedTfIdfScore {
readonly key: string;
/**
* A number between 0 and 1.
*/
readonly score: number;
}
/**
* Implementation of tf-idf (term frequency-inverse document frequency) for a set of
* documents where each document contains one or more chunks of text.
* Each document is identified by a key, and the score for each document is computed
* by taking the max score over all the chunks in the document.
*/
export class TfIdfCalculator {
calculateScores(query: string, token: CancellationToken): TfIdfScore[] {
const embedding = this.computeEmbedding(query);
const idfCache = new Map<string, number>();
const scores: TfIdfScore[] = [];
// For each document, generate one score
for (const [key, doc] of this.documents) {
if (token.isCancellationRequested) {
return [];
}
for (const chunk of doc.chunks) {
const score = this.computeSimilarityScore(chunk, embedding, idfCache);
if (score > 0) {
scores.push({ key, score });
}
}
}
return scores;
}
/**
* Count how many times each term (word) appears in a string.
*/
private static termFrequencies(input: string): TermFrequencies {
return countMapFrom(TfIdfCalculator.splitTerms(input));
}
/**
* Break a string into terms (words).
*/
private static *splitTerms(input: string): Iterable<string> {
const normalize = (word: string) => word.toLowerCase();
// Only match on words that are at least 3 characters long and start with a letter
for (const [word] of input.matchAll(/\b\p{Letter}[\p{Letter}\d]{2,}\b/gu)) {
yield normalize(word);
// eslint-disable-next-line local/code-no-look-behind-regex
const camelParts = word.split(/(?<=[a-z])(?=[A-Z])/g);
if (camelParts.length > 1) {
for (const part of camelParts) {
// Require at least 3 letters in the parts of a camel case word
if (part.length > 2 && /\p{Letter}{3,}/gu.test(part)) {
yield normalize(part);
}
}
}
}
}
/**
* Total number of chunks
*/
private chunkCount = 0;
private readonly chunkOccurrences: DocumentOccurrences = new Map</* word */ string, /*documentOccurrences*/ number>();
private readonly documents = new Map</* key */ string, {
readonly chunks: ReadonlyArray<DocumentChunkEntry>;
}>();
updateDocuments(documents: ReadonlyArray<TfIdfDocument>): this {
for (const { key } of documents) {
this.deleteDocument(key);
}
for (const doc of documents) {
const chunks: Array<{ text: string; tf: TermFrequencies }> = [];
for (const text of doc.textChunks) {
// TODO: See if we can compute the tf lazily
// The challenge is that we need to also update the `chunkOccurrences`
// and all of those updates need to get flushed before the real TF-IDF of
// anything is computed.
const tf = TfIdfCalculator.termFrequencies(text);
// Update occurrences list
for (const term of tf.keys()) {
this.chunkOccurrences.set(term, (this.chunkOccurrences.get(term) ?? 0) + 1);
}
chunks.push({ text, tf });
}
this.chunkCount += chunks.length;
this.documents.set(doc.key, { chunks });
}
return this;
}
deleteDocument(key: string) {
const doc = this.documents.get(key);
if (!doc) {
return;
}
this.documents.delete(key);
this.chunkCount -= doc.chunks.length;
// Update term occurrences for the document
for (const chunk of doc.chunks) {
for (const term of chunk.tf.keys()) {
const currentOccurrences = this.chunkOccurrences.get(term);
if (typeof currentOccurrences === 'number') {
const newOccurrences = currentOccurrences - 1;
if (newOccurrences <= 0) {
this.chunkOccurrences.delete(term);
} else {
this.chunkOccurrences.set(term, newOccurrences);
}
}
}
}
}
private computeSimilarityScore(chunk: DocumentChunkEntry, queryEmbedding: SparseEmbedding, idfCache: Map<string, number>): number {
// Compute the dot product between the chunk's embedding and the query embedding
// Note that the chunk embedding is computed lazily on a per-term basis.
// This lets us skip a large number of calculations because the majority
// of chunks do not share any terms with the query.
let sum = 0;
for (const [term, termTfidf] of Object.entries(queryEmbedding)) {
const chunkTf = chunk.tf.get(term);
if (!chunkTf) {
// Term does not appear in chunk so it has no contribution
continue;
}
let chunkIdf = idfCache.get(term);
if (typeof chunkIdf !== 'number') {
chunkIdf = this.computeIdf(term);
idfCache.set(term, chunkIdf);
}
const chunkTfidf = chunkTf * chunkIdf;
sum += chunkTfidf * termTfidf;
}
return sum;
}
private computeEmbedding(input: string): SparseEmbedding {
const tf = TfIdfCalculator.termFrequencies(input);
return this.computeTfidf(tf);
}
private computeIdf(term: string): number {
const chunkOccurrences = this.chunkOccurrences.get(term) ?? 0;
return chunkOccurrences > 0
? Math.log((this.chunkCount + 1) / chunkOccurrences)
: 0;
}
private computeTfidf(termFrequencies: TermFrequencies): SparseEmbedding {
const embedding = Object.create(null);
for (const [word, occurrences] of termFrequencies) {
const idf = this.computeIdf(word);
if (idf > 0) {
embedding[word] = occurrences * idf;
}
}
return embedding;
}
}
/**
* Normalize the scores to be between 0 and 1 and sort them decending.
* @param scores array of scores from {@link TfIdfCalculator.calculateScores}
* @returns normalized scores
*/
export function normalizeTfIdfScores(scores: TfIdfScore[]): NormalizedTfIdfScore[] {
// copy of scores
const result = scores.slice(0) as { score: number }[];
// sort descending
result.sort((a, b) => b.score - a.score);
// normalize
const max = result[0]?.score ?? 0;
if (max > 0) {
for (const score of result) {
score.score /= max;
}
}
return result as TfIdfScore[];
}

View file

@ -222,3 +222,8 @@ export type OmitOptional<T> = { [K in keyof T as T[K] extends Required<T>[K] ? K
export type Mutable<T> = {
-readonly [P in keyof T]: T[P]
};
/**
* A single object or an array of the objects.
*/
export type SingleOrMany<T> = T | T[];

View file

@ -798,7 +798,7 @@ export class IPCServer<TContext = string> implements IChannelServer<TContext>, I
private readonly _onDidRemoveConnection = new Emitter<Connection<TContext>>();
readonly onDidRemoveConnection: Event<Connection<TContext>> = this._onDidRemoveConnection.event;
private disposables = new DisposableStore();
private readonly disposables = new DisposableStore();
get connections(): Connection<TContext>[] {
const result: Connection<TContext>[] = [];

View file

@ -113,7 +113,7 @@ interface ITestService {
class TestService implements ITestService {
private disposables = new DisposableStore();
private readonly disposables = new DisposableStore();
private readonly _onPong = new Emitter<string>();
readonly onPong = this._onPong.event;

View file

@ -102,7 +102,7 @@ const NullAccessor = new NullAccessorClass();
suite('Fuzzy Scorer', () => {
test('score (fuzzy)', function () {
const target = 'HeLlo-World';
const target = 'HelLo-World';
const scores: FuzzyScore[] = [];
scores.push(_doScore(target, 'HelLo-World', true)); // direct case match
@ -134,7 +134,7 @@ suite('Fuzzy Scorer', () => {
});
test('score (non fuzzy)', function () {
const target = 'HeLlo-World';
const target = 'HelLo-World';
assert.ok(_doScore(target, 'HelLo-World', false)[0] > 0);
assert.strictEqual(_doScore(target, 'HelLo-World', false)[1].length, 'HelLo-World'.length);
@ -1161,10 +1161,10 @@ suite('Fuzzy Scorer', () => {
});
test('fuzzyScore2 (matching)', function () {
const target = 'HeLlo-World';
const target = 'HelLo-World';
for (const offset of [0, 3]) {
let [score, matches] = _doScore2(offset === 0 ? target : `123${target}`, 'HeLlo-World', offset);
let [score, matches] = _doScore2(offset === 0 ? target : `123${target}`, 'HelLo-World', offset);
assert.ok(score);
assert.strictEqual(matches.length, 1);
@ -1183,7 +1183,7 @@ suite('Fuzzy Scorer', () => {
});
test('fuzzyScore2 (multiple queries)', function () {
const target = 'HeLlo-World';
const target = 'HelLo-World';
const [firstSingleScore, firstSingleMatches] = _doScore2(target, 'HelLo');
const [secondSingleScore, secondSingleMatches] = _doScore2(target, 'World');

View file

@ -4,9 +4,12 @@
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { HistoryNavigator, HistoryNavigator2 } from 'vs/base/common/history';
import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils';
suite('History Navigator', () => {
ensureNoDisposablesAreLeakedInTestSuite();
test('create reduces the input to limit', () => {
const testObject = new HistoryNavigator(['1', '2', '3', '4'], 2);
@ -269,4 +272,21 @@ suite('History Navigator 2', () => {
assert.strictEqual(testObject.previous(), '1');
});
test('prepend', () => {
const testObject = new HistoryNavigator2(['1', '2', '3', '4']);
assert.strictEqual(testObject.current(), '4');
assert.ok(testObject.isAtEnd());
assert.deepStrictEqual(Array.from(testObject), ['1', '2', '3', '4']);
testObject.prepend('0');
assert.strictEqual(testObject.current(), '4');
assert.ok(testObject.isAtEnd());
assert.deepStrictEqual(Array.from(testObject), ['0', '1', '2', '3', '4']);
testObject.prepend('2');
assert.strictEqual(testObject.current(), '4');
assert.ok(testObject.isAtEnd());
assert.deepStrictEqual(Array.from(testObject), ['0', '1', '2', '3', '4']);
});
});

View file

@ -0,0 +1,194 @@
/*---------------------------------------------------------------------------------------------
* 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 { CancellationToken } from 'vs/base/common/cancellation';
import { TfIdfCalculator, TfIdfDocument, TfIdfScore } from 'vs/base/common/tfIdf';
/**
* Generates all permutations of an array.
*
* This is useful for testing to make sure order does not effect the result.
*/
function permutate<T>(arr: T[]): T[][] {
if (arr.length === 0) {
return [[]];
}
const result: T[][] = [];
for (let i = 0; i < arr.length; i++) {
const rest = [...arr.slice(0, i), ...arr.slice(i + 1)];
const permutationsRest = permutate(rest);
for (let j = 0; j < permutationsRest.length; j++) {
result.push([arr[i], ...permutationsRest[j]]);
}
}
return result;
}
function assertScoreOrdersEqual(actualScores: TfIdfScore[], expectedScoreKeys: string[]): void {
actualScores.sort((a, b) => (b.score - a.score) || a.key.localeCompare(b.key));
assert.strictEqual(actualScores.length, expectedScoreKeys.length);
for (let i = 0; i < expectedScoreKeys.length; i++) {
assert.strictEqual(actualScores[i].key, expectedScoreKeys[i]);
}
}
suite('TF-IDF Calculator', function () {
test('Should return no scores when no documents are given', () => {
const tfidf = new TfIdfCalculator();
const scores = tfidf.calculateScores('something', CancellationToken.None);
assertScoreOrdersEqual(scores, []);
});
test('Should return no scores for term not in document', () => {
const tfidf = new TfIdfCalculator().updateDocuments([
makeDocument('A', 'cat dog fish'),
]);
const scores = tfidf.calculateScores('elepant', CancellationToken.None);
assertScoreOrdersEqual(scores, []);
});
test('Should return scores for document with exact match', () => {
for (const docs of permutate([
makeDocument('A', 'cat dog cat'),
makeDocument('B', 'cat fish'),
])) {
const tfidf = new TfIdfCalculator().updateDocuments(docs);
const scores = tfidf.calculateScores('dog', CancellationToken.None);
assertScoreOrdersEqual(scores, ['A']);
}
});
test('Should return document with more matches first', () => {
for (const docs of permutate([
makeDocument('/A', 'cat dog cat'),
makeDocument('/B', 'cat fish'),
makeDocument('/C', 'frog'),
])) {
const tfidf = new TfIdfCalculator().updateDocuments(docs);
const scores = tfidf.calculateScores('cat', CancellationToken.None);
assertScoreOrdersEqual(scores, ['/A', '/B']);
}
});
test('Should return document with more matches first when term appears in all documents', () => {
for (const docs of permutate([
makeDocument('/A', 'cat dog cat cat'),
makeDocument('/B', 'cat fish'),
makeDocument('/C', 'frog cat cat'),
])) {
const tfidf = new TfIdfCalculator().updateDocuments(docs);
const scores = tfidf.calculateScores('cat', CancellationToken.None);
assertScoreOrdersEqual(scores, ['/A', '/C', '/B']);
}
});
test('Should weigh less common term higher', () => {
for (const docs of permutate([
makeDocument('/A', 'cat dog cat'),
makeDocument('/B', 'fish'),
makeDocument('/C', 'cat cat cat cat'),
makeDocument('/D', 'cat fish')
])) {
const tfidf = new TfIdfCalculator().updateDocuments(docs);
const scores = tfidf.calculateScores('cat the dog', CancellationToken.None);
assertScoreOrdersEqual(scores, ['/A', '/C', '/D']);
}
});
test('Should weigh chunks with less common terms higher', () => {
for (const docs of permutate([
makeDocument('/A', ['cat dog cat', 'fish']),
makeDocument('/B', ['cat cat cat cat dog', 'dog'])
])) {
const tfidf = new TfIdfCalculator().updateDocuments(docs);
const scores = tfidf.calculateScores('cat', CancellationToken.None);
assertScoreOrdersEqual(scores, ['/B', '/A']);
}
for (const docs of permutate([
makeDocument('/A', ['cat dog cat', 'fish']),
makeDocument('/B', ['cat cat cat cat dog', 'dog'])
])) {
const tfidf = new TfIdfCalculator().updateDocuments(docs);
const scores = tfidf.calculateScores('dog', CancellationToken.None);
assertScoreOrdersEqual(scores, ['/A', '/B', '/B']);
}
for (const docs of permutate([
makeDocument('/A', ['cat dog cat', 'fish']),
makeDocument('/B', ['cat cat cat cat dog', 'dog'])
])) {
const tfidf = new TfIdfCalculator().updateDocuments(docs);
const scores = tfidf.calculateScores('cat the dog', CancellationToken.None);
assertScoreOrdersEqual(scores, ['/B', '/A', '/B']);
}
for (const docs of permutate([
makeDocument('/A', ['cat dog cat', 'fish']),
makeDocument('/B', ['cat cat cat cat dog', 'dog'])
])) {
const tfidf = new TfIdfCalculator().updateDocuments(docs);
const scores = tfidf.calculateScores('lake fish', CancellationToken.None);
assertScoreOrdersEqual(scores, ['/A']);
}
});
test('Should ignore case and punctuation', () => {
for (const docs of permutate([
makeDocument('/A', 'Cat doG.cat'),
makeDocument('/B', 'cAt fiSH'),
makeDocument('/C', 'frOg'),
])) {
const tfidf = new TfIdfCalculator().updateDocuments(docs);
const scores = tfidf.calculateScores('. ,CaT! ', CancellationToken.None);
assertScoreOrdersEqual(scores, ['/A', '/B']);
}
});
test('Should match on camelCase words', () => {
for (const docs of permutate([
makeDocument('/A', 'catDog cat'),
makeDocument('/B', 'fishCatFish'),
makeDocument('/C', 'frogcat'),
])) {
const tfidf = new TfIdfCalculator().updateDocuments(docs);
const scores = tfidf.calculateScores('catDOG', CancellationToken.None);
assertScoreOrdersEqual(scores, ['/A', '/B']);
}
});
test('Should not match document after delete', () => {
const docA = makeDocument('/A', 'cat dog cat');
const docB = makeDocument('/B', 'cat fish');
const docC = makeDocument('/C', 'frog');
const tfidf = new TfIdfCalculator().updateDocuments([docA, docB, docC]);
let scores = tfidf.calculateScores('cat', CancellationToken.None);
assertScoreOrdersEqual(scores, ['/A', '/B']);
tfidf.deleteDocument(docA.key);
scores = tfidf.calculateScores('cat', CancellationToken.None);
assertScoreOrdersEqual(scores, ['/B']);
tfidf.deleteDocument(docC.key);
scores = tfidf.calculateScores('cat', CancellationToken.None);
assertScoreOrdersEqual(scores, ['/B']);
tfidf.deleteDocument(docB.key);
scores = tfidf.calculateScores('cat', CancellationToken.None);
assertScoreOrdersEqual(scores, []);
});
});
function makeDocument(key: string, content: string | string[]): TfIdfDocument {
return {
key,
textChunks: Array.isArray(content) ? content : [content],
};
}

View file

@ -45,7 +45,7 @@ import { IExtensionUrlTrustService } from 'vs/platform/extensionManagement/commo
import { ExtensionUrlTrustService } from 'vs/platform/extensionManagement/node/extensionUrlTrustService';
import { IExtensionHostStarter, ipcExtensionHostStarterChannelName } from 'vs/platform/extensions/common/extensionHostStarter';
import { ExtensionHostStarter } from 'vs/platform/extensions/electron-main/extensionHostStarter';
import { IExternalTerminalMainService } from 'vs/platform/externalTerminal/common/externalTerminal';
import { IExternalTerminalMainService } from 'vs/platform/externalTerminal/electron-main/externalTerminal';
import { LinuxExternalTerminalService, MacExternalTerminalService, WindowsExternalTerminalService } from 'vs/platform/externalTerminal/node/externalTerminalService';
import { LOCAL_FILE_SYSTEM_CHANNEL_NAME } from 'vs/platform/files/common/diskFileSystemProviderClient';
import { IFileService } from 'vs/platform/files/common/files';

View file

@ -1052,10 +1052,11 @@ export class IssueReporter extends Disposable {
}, extension.name);
};
const extensionsSelector = this.getElementById('extension-selector');
const extensionsSelector = this.getElementById<HTMLSelectElement>('extension-selector');
if (extensionsSelector) {
const { selectedExtension } = this.issueReporterModel.getData();
reset(extensionsSelector, $<HTMLOptionElement>('option'), ...extensionOptions.map(extension => makeOption(extension, selectedExtension)));
reset(extensionsSelector, this.makeOption('', localize('selectExtension', "Select extension"), true), ...extensionOptions.map(extension => makeOption(extension, selectedExtension)));
extensionsSelector.selectedIndex = 0;
this.addEventListener('extension-selector', 'change', (e: Event) => {
const selectedExtensionId = (<HTMLInputElement>e.target).value;

View file

@ -9,10 +9,12 @@ import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { IVoiceRecognitionService } from 'vs/platform/voiceRecognition/node/voiceRecognitionService';
import { ILogService } from 'vs/platform/log/common/log';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { LimitedQueue } from 'vs/base/common/async';
import { LimitedQueue, Queue } from 'vs/base/common/async';
export class VoiceTranscriptionManager extends Disposable {
private static USE_SLIDING_WINDOW = !!process.env.VSCODE_VOICE_USE_SLIDING_WINDOW;
constructor(
private readonly onDidWindowConnectRaw: Event<MessagePortMain>,
@IVoiceRecognitionService private readonly voiceRecognitionService: IVoiceRecognitionService,
@ -25,23 +27,25 @@ export class VoiceTranscriptionManager extends Disposable {
private registerListeners(): void {
this._register(this.onDidWindowConnectRaw(port => {
this._register(new VoiceTranscriber(port, this.voiceRecognitionService, this.logService));
this.logService.info(`[voice] transcriber: new connection (sliding window: ${VoiceTranscriptionManager.USE_SLIDING_WINDOW})`);
if (VoiceTranscriptionManager.USE_SLIDING_WINDOW) {
this._register(new SlidingWindowVoiceTranscriber(port, this.voiceRecognitionService, this.logService));
} else {
this._register(new FullWindowVoiceTranscriber(port, this.voiceRecognitionService, this.logService));
}
}));
}
}
class VoiceTranscriber extends Disposable {
abstract class VoiceTranscriber extends Disposable {
private static MAX_DATA_LENGTH = 30 /* seconds */ * 16000 /* sampling rate */ * 16 /* bith depth */ * 1 /* channels */ / 8;
private readonly transcriptionQueue = new LimitedQueue();
private data: Float32Array | undefined = undefined;
protected static MAX_DATA_LENGTH = 30 /* seconds */ * 16000 /* sampling rate */ * 16 /* bith depth */ * 1 /* channels */ / 8;
constructor(
private readonly port: MessagePortMain,
private readonly voiceRecognitionService: IVoiceRecognitionService,
private readonly logService: ILogService
protected readonly port: MessagePortMain,
protected readonly voiceRecognitionService: IVoiceRecognitionService,
protected readonly logService: ILogService
) {
super();
@ -49,39 +53,115 @@ class VoiceTranscriber extends Disposable {
}
private registerListeners(): void {
this.logService.info(`[voice] transcriber: new connection`);
const cts = new CancellationTokenSource();
this._register(toDisposable(() => cts.dispose(true)));
const requestHandler = (e: MessageEvent) => this.handleRequest(e, cts.token);
const requestHandler = (e: MessageEvent) => {
if (!(e.data instanceof Float32Array)) {
return;
}
this.handleRequest(e.data, cts.token);
};
this.port.on('message', requestHandler);
this._register(toDisposable(() => this.port.off('message', requestHandler)));
this.port.start();
this._register(toDisposable(() => this.port.close()));
let closed = false;
this.port.on('close', () => {
this.logService.info(`[voice] transcriber: closed connection`);
cts.dispose(true);
closed = true;
this.dispose();
});
this._register(toDisposable(() => {
if (!closed) {
this.port.close();
}
}));
}
private async handleRequest(e: MessageEvent, cancellation: CancellationToken): Promise<void> {
if (!(Array.isArray(e.data))) {
protected abstract handleRequest(data: Float32Array, cancellation: CancellationToken): Promise<void>;
protected joinFloat32Arrays(float32Arrays: Float32Array[]): Float32Array {
const result = new Float32Array(float32Arrays.reduce((prev, curr) => prev + curr.length, 0));
let offset = 0;
for (const float32Array of float32Arrays) {
result.set(float32Array, offset);
offset += float32Array.length;
}
return result;
}
}
class SlidingWindowVoiceTranscriber extends VoiceTranscriber {
private readonly transcriptionQueue = this._register(new Queue());
private transcribedResults: string[] = [];
private data: Float32Array = new Float32Array(0);
protected async handleRequest(data: Float32Array, cancellation: CancellationToken): Promise<void> {
if (data.length > 0) {
this.logService.info(`[voice] transcriber: voice detected, storing in buffer`);
this.data = this.data ? this.joinFloat32Arrays([this.data, data]) : data;
} else {
this.logService.info(`[voice] transcriber: silence detected, transcribing window...`);
const data = this.data.slice(0);
this.data = new Float32Array(0);
this.transcriptionQueue.queue(() => this.transcribe(data, cancellation));
}
}
private async transcribe(data: Float32Array, cancellation: CancellationToken): Promise<void> {
if (cancellation.isCancellationRequested) {
return;
}
const newData: Float32Array[] = [];
for (const channelData of e.data) {
if (channelData instanceof Float32Array) {
newData.push(channelData);
if (data.length > VoiceTranscriber.MAX_DATA_LENGTH) {
this.logService.warn(`[voice] transcriber: refusing to accept more than 30s of audio data`);
return;
}
if (data.length !== 0) {
const result = await this.voiceRecognitionService.transcribe(data, cancellation);
if (result) {
this.transcribedResults.push(result);
}
}
const dataCandidate = this.joinFloat32Arrays(this.data ? [this.data, ...newData] : newData);
if (cancellation.isCancellationRequested) {
return;
}
this.port.postMessage(this.transcribedResults.join(' '));
}
override dispose(): void {
super.dispose();
this.data = new Float32Array(0);
}
}
class FullWindowVoiceTranscriber extends VoiceTranscriber {
private readonly transcriptionQueue = new LimitedQueue();
private data: Float32Array | undefined = undefined;
private transcribedDataLength = 0;
private transcribedResult = '';
protected async handleRequest(data: Float32Array, cancellation: CancellationToken): Promise<void> {
const dataCandidate = this.data ? this.joinFloat32Arrays([this.data, data]) : data;
if (dataCandidate.length > VoiceTranscriber.MAX_DATA_LENGTH) {
this.logService.warn(`[voice] transcriber: refusing to accept more than 30s of audio data`);
return;
@ -102,7 +182,20 @@ class VoiceTranscriber extends Disposable {
return;
}
const result = await this.voiceRecognitionService.transcribe(data, cancellation);
let result: string;
if (data.length === this.transcribedDataLength) {
// Optimization: if the data is the same as the last time
// we transcribed, don't transcribe again, just return the
// same result as we had last time.
this.logService.info(`[voice] transcriber: silence detected, reusing previous transcription result`);
result = this.transcribedResult;
} else {
this.logService.info(`[voice] transcriber: voice detected, transcribing everything...`);
result = await this.voiceRecognitionService.transcribe(data, cancellation);
}
this.transcribedResult = result;
this.transcribedDataLength = data.length;
if (cancellation.isCancellationRequested) {
return;
@ -111,15 +204,9 @@ class VoiceTranscriber extends Disposable {
this.port.postMessage(result);
}
private joinFloat32Arrays(float32Arrays: Float32Array[]): Float32Array {
const result = new Float32Array(float32Arrays.reduce((prev, curr) => prev + curr.length, 0));
override dispose(): void {
super.dispose();
let offset = 0;
for (const float32Array of float32Arrays) {
result.set(float32Array, offset);
offset += float32Array.length;
}
return result;
this.data = undefined;
}
}

View file

@ -98,7 +98,7 @@ export class MouseHandler extends ViewEventHandler {
// remove this listener
if (!this._mouseLeaveMonitor) {
this._mouseLeaveMonitor = dom.addDisposableListener(document, 'mousemove', (e) => {
this._mouseLeaveMonitor = dom.addDisposableListener(this.viewHelper.viewDomNode.ownerDocument, 'mousemove', (e) => {
if (!this.viewHelper.viewDomNode.contains(e.target as Node | null)) {
// went outside the editor!
this._onMouseLeave(new EditorMouseEvent(e, false, this.viewHelper.viewDomNode));

View file

@ -336,7 +336,7 @@ export class HitTestContext {
}
private static _findAttribute(element: Element, attr: string, stopAt: Element): string | null {
while (element && element !== document.body) {
while (element && element !== element.ownerDocument.body) {
if (element.hasAttribute && element.hasAttribute(attr)) {
return element.getAttribute(attr);
}
@ -917,7 +917,7 @@ export class MouseTargetFactory {
range = (<any>shadowRoot).caretRangeFromPoint(coords.clientX, coords.clientY);
}
} else {
range = (<any>document).caretRangeFromPoint(coords.clientX, coords.clientY);
range = (<any>ctx.viewDomNode.ownerDocument).caretRangeFromPoint(coords.clientX, coords.clientY);
}
if (!range || !range.startContainer) {
@ -959,7 +959,7 @@ export class MouseTargetFactory {
* Most probably Gecko
*/
private static _doHitTestWithCaretPositionFromPoint(ctx: HitTestContext, coords: ClientCoordinates): HitTestResult {
const hitResult: { offsetNode: Node; offset: number } = (<any>document).caretPositionFromPoint(coords.clientX, coords.clientY);
const hitResult: { offsetNode: Node; offset: number } = (<any>ctx.viewDomNode.ownerDocument).caretPositionFromPoint(coords.clientX, coords.clientY);
if (hitResult.offsetNode.nodeType === hitResult.offsetNode.TEXT_NODE) {
// offsetNode is expected to be the token text
@ -1011,9 +1011,9 @@ export class MouseTargetFactory {
private static _doHitTest(ctx: HitTestContext, request: BareHitTestRequest): HitTestResult {
let result: HitTestResult = new UnknownHitTestResult();
if (typeof (<any>document).caretRangeFromPoint === 'function') {
if (typeof (<any>ctx.viewDomNode.ownerDocument).caretRangeFromPoint === 'function') {
result = this._doHitTestWithCaretRangeFromPoint(ctx, request);
} else if ((<any>document).caretPositionFromPoint) {
} else if ((<any>ctx.viewDomNode.ownerDocument).caretPositionFromPoint) {
result = this._doHitTestWithCaretPositionFromPoint(ctx, request.pos.toClientCoordinates());
}
if (result.type === HitTestResultType.Content) {

View file

@ -391,7 +391,7 @@ export class TextAreaHandler extends ViewPart {
const distanceToModelLineStart = startModelPosition.column - 1 - visibleBeforeCharCount;
const hiddenLineTextBefore = lineTextBeforeSelection.substring(0, lineTextBeforeSelection.length - visibleBeforeCharCount);
const { tabSize } = this._context.viewModel.model.getOptions();
const widthOfHiddenTextBefore = measureText(hiddenLineTextBefore, this._fontInfo, tabSize);
const widthOfHiddenTextBefore = measureText(this.textArea.domNode.ownerDocument, hiddenLineTextBefore, this._fontInfo, tabSize);
return { distanceToModelLineStart, widthOfHiddenTextBefore };
})();
@ -927,7 +927,7 @@ interface IRenderData {
strikethrough?: boolean;
}
function measureText(text: string, fontInfo: FontInfo, tabSize: number): number {
function measureText(document: Document, text: string, fontInfo: FontInfo, tabSize: number): number {
if (text.length === 0) {
return 0;
}

View file

@ -110,6 +110,8 @@ export interface ICompleteTextAreaWrapper extends ITextAreaWrapper {
readonly onBlur: Event<FocusEvent>;
readonly onSyntheticTap: Event<void>;
readonly ownerDocument: Document;
setIgnoreSelectionChangeTime(reason: string): void;
getIgnoreSelectionChangeTime(): number;
resetSelectionChangeTime(): void;
@ -494,7 +496,7 @@ export class TextAreaInput extends Disposable {
// `selectionchange` events often come multiple times for a single logical change
// so throttle multiple `selectionchange` events that burst in a short period of time.
let previousSelectionChangeEventTime = 0;
return dom.addDisposableListener(document, 'selectionchange', (e) => {
return dom.addDisposableListener(this._textArea.ownerDocument, 'selectionchange', (e) => {//todo
inputLatency.onSelectionChange();
if (!this._hasFocus) {
@ -701,6 +703,10 @@ export class TextAreaWrapper extends Disposable implements ICompleteTextAreaWrap
public readonly onFocus = this._register(new DomEmitter(this._actual, 'focus')).event;
public readonly onBlur = this._register(new DomEmitter(this._actual, 'blur')).event;
public get ownerDocument(): Document {
return this._actual.ownerDocument;
}
private _onSyntheticTap = this._register(new Emitter<void>());
public readonly onSyntheticTap: Event<void> = this._onSyntheticTap.event;
@ -725,7 +731,7 @@ export class TextAreaWrapper extends Disposable implements ICompleteTextAreaWrap
if (shadowRoot) {
return shadowRoot.activeElement === this._actual;
} else if (dom.isInDOM(this._actual)) {
return document.activeElement === this._actual;
return this._actual.ownerDocument.activeElement === this._actual;
} else {
return false;
}
@ -775,7 +781,7 @@ export class TextAreaWrapper extends Disposable implements ICompleteTextAreaWrap
if (shadowRoot) {
activeElement = shadowRoot.activeElement;
} else {
activeElement = document.activeElement;
activeElement = textArea.ownerDocument.activeElement;
}
const currentIsFocused = (activeElement === textArea);

View file

@ -29,6 +29,7 @@ import { KeybindingWeight, KeybindingsRegistry } from 'vs/platform/keybinding/co
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { IViewModel } from 'vs/editor/common/viewModel';
import { ISelection } from 'vs/editor/common/core/selection';
import { getActiveElement } from 'vs/base/browser/dom';
const CORE_WEIGHT = KeybindingWeight.EditorCore;
@ -315,9 +316,9 @@ abstract class EditorOrNativeTextInputCommand {
// 2. handle case when focus is in some other `input` / `textarea`.
target.addImplementation(1000, 'generic-dom-input-textarea', (accessor: ServicesAccessor, args: unknown) => {
// Only if focused on an element that allows for entering text
const activeElement = <HTMLElement>document.activeElement;
const activeElement = getActiveElement();
if (activeElement && ['input', 'textarea'].indexOf(activeElement.tagName.toLowerCase()) >= 0) {
this.runDOMCommand();
this.runDOMCommand(activeElement);
return true;
}
return false;
@ -343,7 +344,7 @@ abstract class EditorOrNativeTextInputCommand {
return true;
}
public abstract runDOMCommand(): void;
public abstract runDOMCommand(activeElement: Element): void;
public abstract runEditorCommand(accessor: ServicesAccessor | null, editor: ICodeEditor, args: unknown): void | Promise<void>;
}
@ -1875,13 +1876,13 @@ export namespace CoreNavigationCommands {
constructor() {
super(SelectAllCommand);
}
public runDOMCommand(): void {
public runDOMCommand(activeElement: Element): void {
if (isFirefox) {
(<HTMLInputElement>document.activeElement).focus();
(<HTMLInputElement>document.activeElement).select();
(<HTMLInputElement>activeElement).focus();
(<HTMLInputElement>activeElement).select();
}
document.execCommand('selectAll');
activeElement.ownerDocument.execCommand('selectAll');
}
public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: unknown): void {
const viewModel = editor._getViewModel();
@ -2090,8 +2091,8 @@ export namespace CoreEditingCommands {
constructor() {
super(UndoCommand);
}
public runDOMCommand(): void {
document.execCommand('undo');
public runDOMCommand(activeElement: Element): void {
activeElement.ownerDocument.execCommand('undo');
}
public runEditorCommand(accessor: ServicesAccessor | null, editor: ICodeEditor, args: unknown): void | Promise<void> {
if (!editor.hasModel() || editor.getOption(EditorOption.readOnly) === true) {
@ -2105,8 +2106,8 @@ export namespace CoreEditingCommands {
constructor() {
super(RedoCommand);
}
public runDOMCommand(): void {
document.execCommand('redo');
public runDOMCommand(activeElement: Element): void {
activeElement.ownerDocument.execCommand('redo');
}
public runEditorCommand(accessor: ServicesAccessor | null, editor: ICodeEditor, args: unknown): void | Promise<void> {
if (!editor.hasModel() || editor.getOption(EditorOption.readOnly) === true) {

View file

@ -241,7 +241,7 @@ export class GlobalEditorPointerMoveMonitor extends Disposable {
// Add a <<capture>> keydown event listener that will cancel the monitoring
// if something other than a modifier key is pressed
this._keydownListener = dom.addStandardDisposableListener(<any>document, 'keydown', (e) => {
this._keydownListener = dom.addStandardDisposableListener(<any>initialElement.ownerDocument, 'keydown', (e) => {
const chord = e.toKeyCodeChord();
if (chord.isModifierKey()) {
// Allow modifier keys

View file

@ -24,6 +24,7 @@ import { ThemeIcon } from 'vs/base/common/themables';
import { IDisposable } from 'vs/base/common/lifecycle';
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
import { ILogService } from 'vs/platform/log/common/log';
import { getActiveElement } from 'vs/base/browser/dom';
export type ServicesAccessor = InstantiationServicesAccessor;
export type EditorContributionCtor = IConstructorSignature<IEditorContribution, [ICodeEditor]>;
@ -219,7 +220,7 @@ export class MultiCommand extends Command {
logService.trace(`Executing Command '${this.id}' which has ${this._implementations.length} bound.`);
for (const impl of this._implementations) {
if (impl.when) {
const context = contextKeyService.getContext(document.activeElement);
const context = contextKeyService.getContext(getActiveElement());
const value = impl.when.evaluate(context);
if (!value) {
continue;

View file

@ -57,7 +57,7 @@ export class PartFingerprints {
const result: PartFingerprint[] = [];
let resultLen = 0;
while (child && child !== document.body) {
while (child && child !== child.ownerDocument.body) {
if (child === stopAt) {
break;
}

View file

@ -266,9 +266,11 @@ class Widget {
}
private _getMaxWidth(): number {
const elDocument = this.domNode.domNode.ownerDocument;
const elWindow = elDocument.defaultView;
return (
this.allowEditorOverflow
? window.innerWidth || document.documentElement!.offsetWidth || document.body.offsetWidth
? elWindow?.innerWidth || elDocument.documentElement.offsetWidth || elDocument.body.offsetWidth
: this._contentWidth
);
}
@ -326,7 +328,9 @@ class Widget {
const MIN_LIMIT = Math.max(LEFT_PADDING, domNodePosition.left - width);
const MAX_LIMIT = Math.min(domNodePosition.left + domNodePosition.width + width, windowSize.width - RIGHT_PADDING);
let absoluteLeft = domNodePosition.left + left - window.scrollX;
const elDocument = this._viewDomNode.domNode.ownerDocument;
const elWindow = elDocument.defaultView;
let absoluteLeft = domNodePosition.left + left - (elWindow?.scrollX ?? 0);
if (absoluteLeft + width > MAX_LIMIT) {
const delta = absoluteLeft - (MAX_LIMIT - width);
@ -348,10 +352,12 @@ class Widget {
const belowTop = anchor.top + anchor.height;
const domNodePosition = dom.getDomNodePagePosition(this._viewDomNode.domNode);
const absoluteAboveTop = domNodePosition.top + aboveTop - window.scrollY;
const absoluteBelowTop = domNodePosition.top + belowTop - window.scrollY;
const elDocument = this._viewDomNode.domNode.ownerDocument;
const elWindow = elDocument.defaultView;
const absoluteAboveTop = domNodePosition.top + aboveTop - (elWindow?.scrollY ?? 0);
const absoluteBelowTop = domNodePosition.top + belowTop - (elWindow?.scrollY ?? 0);
const windowSize = dom.getClientArea(document.body);
const windowSize = dom.getClientArea(elDocument.body);
const [left, absoluteAboveLeft] = this._layoutHorizontalSegmentInPage(windowSize, domNodePosition, anchor.left - ctx.scrollLeft + this._contentLeft, width);
// Leave some clearance to the top/bottom

View file

@ -3,6 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { getActiveElement } from 'vs/base/browser/dom';
import { Codicon } from 'vs/base/common/codicons';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser';
@ -290,10 +291,11 @@ export function findFocusedDiffEditor(accessor: ServicesAccessor): IDiffEditor |
}
}
if (document.activeElement) {
const activeElement = getActiveElement();
if (activeElement) {
for (const d of diffEditors) {
const container = d.getContainerDomNode();
if (isElementOrParentOf(container, document.activeElement)) {
if (isElementOrParentOf(container, activeElement)) {
return d;
}
}

View file

@ -103,6 +103,12 @@ export class OffsetRange {
return undefined;
}
public intersectsOrTouches(other: OffsetRange): boolean {
const start = Math.max(this.start, other.start);
const end = Math.min(this.endExclusive, other.endExclusive);
return start <= end;
}
public slice<T>(arr: T[]): T[] {
return arr.slice(this.start, this.endExclusive);
}

View file

@ -16,11 +16,11 @@ export interface IDiffAlgorithm {
export class DiffAlgorithmResult {
static trivial(seq1: ISequence, seq2: ISequence): DiffAlgorithmResult {
return new DiffAlgorithmResult([new SequenceDiff(new OffsetRange(0, seq1.length), new OffsetRange(0, seq2.length))], false);
return new DiffAlgorithmResult([new SequenceDiff(OffsetRange.ofLength(seq1.length), OffsetRange.ofLength(seq2.length))], false);
}
static trivialTimedOut(seq1: ISequence, seq2: ISequence): DiffAlgorithmResult {
return new DiffAlgorithmResult([new SequenceDiff(new OffsetRange(0, seq1.length), new OffsetRange(0, seq2.length))], true);
return new DiffAlgorithmResult([new SequenceDiff(OffsetRange.ofLength(seq1.length), OffsetRange.ofLength(seq2.length))], true);
}
constructor(
@ -36,21 +36,22 @@ export class DiffAlgorithmResult {
export class SequenceDiff {
public static invert(sequenceDiffs: SequenceDiff[], doc1Length: number): SequenceDiff[] {
const result: SequenceDiff[] = [];
forEachAdjacent(sequenceDiffs, (a, b) => {
const seq1Start = a ? a.seq1Range.endExclusive : 0;
const seq2Start = a ? a.seq2Range.endExclusive : 0;
const seq1EndEx = b ? b.seq1Range.start : doc1Length;
const seq2EndEx = b ? b.seq2Range.start : (a ? a.seq2Range.endExclusive - a.seq1Range.endExclusive : 0) + doc1Length;
result.push(new SequenceDiff(
new OffsetRange(seq1Start, seq1EndEx),
new OffsetRange(seq2Start, seq2EndEx),
result.push(SequenceDiff.fromOffsetPairs(
a ? a.getEndExclusives() : OffsetPair.zero,
b ? b.getStarts() : new OffsetPair(doc1Length, (a ? a.seq2Range.endExclusive - a.seq1Range.endExclusive : 0) + doc1Length)
));
});
return result;
}
public static fromOffsetPairs(start: OffsetPair, endExclusive: OffsetPair): SequenceDiff {
return new SequenceDiff(
new OffsetRange(start.offset1, endExclusive.offset1),
new OffsetRange(start.offset2, endExclusive.offset2),
);
}
constructor(
public readonly seq1Range: OffsetRange,
public readonly seq2Range: OffsetRange,
@ -74,6 +75,56 @@ export class SequenceDiff {
}
return new SequenceDiff(this.seq1Range.delta(offset), this.seq2Range.delta(offset));
}
public deltaStart(offset: number): SequenceDiff {
if (offset === 0) {
return this;
}
return new SequenceDiff(this.seq1Range.deltaStart(offset), this.seq2Range.deltaStart(offset));
}
public deltaEnd(offset: number): SequenceDiff {
if (offset === 0) {
return this;
}
return new SequenceDiff(this.seq1Range.deltaEnd(offset), this.seq2Range.deltaEnd(offset));
}
public intersectsOrTouches(other: SequenceDiff): boolean {
return this.seq1Range.intersectsOrTouches(other.seq1Range) || this.seq2Range.intersectsOrTouches(other.seq2Range);
}
public intersect(other: SequenceDiff): SequenceDiff | undefined {
const i1 = this.seq1Range.intersect(other.seq1Range);
const i2 = this.seq2Range.intersect(other.seq2Range);
if (!i1 || !i2) {
return undefined;
}
return new SequenceDiff(i1, i2);
}
public getStarts(): OffsetPair {
return new OffsetPair(this.seq1Range.start, this.seq2Range.start);
}
public getEndExclusives(): OffsetPair {
return new OffsetPair(this.seq1Range.endExclusive, this.seq2Range.endExclusive);
}
}
export class OffsetPair {
public static readonly zero = new OffsetPair(0, 0);
public static readonly max = new OffsetPair(Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER);
constructor(
public readonly offset1: number,
public readonly offset2: number,
) {
}
public toString(): string {
return `${this.offset1} <-> ${this.offset2}`;
}
}
export interface ISequence {

View file

@ -196,6 +196,9 @@ function computeUnchangedMoves(
for (extendToTop = 0; extendToTop < linesAbove; extendToTop++) {
const origLine = move.original.startLineNumber - extendToTop - 1;
const modLine = move.modified.startLineNumber - extendToTop - 1;
if (origLine > originalLines.length || modLine > modifiedLines.length) {
break;
}
if (modifiedSet.contains(modLine) || originalSet.contains(origLine)) {
break;
}
@ -213,6 +216,9 @@ function computeUnchangedMoves(
for (extendToBottom = 0; extendToBottom < linesBelow; extendToBottom++) {
const origLine = move.original.endLineNumberExclusive + extendToBottom;
const modLine = move.modified.endLineNumberExclusive + extendToBottom;
if (origLine > originalLines.length || modLine > modifiedLines.length) {
break;
}
if (modifiedSet.contains(modLine) || originalSet.contains(origLine)) {
break;
}

View file

@ -3,8 +3,9 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { forEachWithNeighbors } from 'vs/base/common/arrays';
import { OffsetRange } from 'vs/editor/common/core/offsetRange';
import { ISequence, SequenceDiff } from 'vs/editor/common/diff/defaultLinesDiffComputer/algorithms/diffAlgorithm';
import { ISequence, OffsetPair, SequenceDiff } from 'vs/editor/common/diff/defaultLinesDiffComputer/algorithms/diffAlgorithm';
import { LineSequence } from 'vs/editor/common/diff/defaultLinesDiffComputer/lineSequence';
import { LinesSliceCharSequence } from 'vs/editor/common/diff/defaultLinesDiffComputer/linesSliceCharSequence';
@ -425,28 +426,33 @@ export function removeVeryShortMatchingTextBetweenLongDiffs(sequence1: LinesSlic
diffs = result;
} while (counter++ < 10 && shouldRepeat);
// Remove short suffixes/prefixes
for (let i = 0; i < diffs.length; i++) {
const cur = diffs[i];
const newDiffs: SequenceDiff[] = [];
let range1 = cur.seq1Range;
let range2 = cur.seq2Range;
// Remove short suffixes/prefixes
forEachWithNeighbors(diffs, (prev, cur, next) => {
let newDiff = cur;
function shouldMarkAsChanged(text: string): boolean {
return text.length > 0 && text.trim().length <= 3 && cur.seq1Range.length + cur.seq2Range.length > 100;
}
const fullRange1 = sequence1.extendToFullLines(cur.seq1Range);
const prefix = sequence1.getText(new OffsetRange(fullRange1.start, cur.seq1Range.start));
if (prefix.length > 0 && prefix.trim().length <= 3 && cur.seq1Range.length + cur.seq2Range.length > 100) {
range1 = cur.seq1Range.deltaStart(-prefix.length);
range2 = cur.seq2Range.deltaStart(-prefix.length);
if (shouldMarkAsChanged(prefix)) {
newDiff = newDiff.deltaStart(-prefix.length);
}
const suffix = sequence1.getText(new OffsetRange(cur.seq1Range.endExclusive, fullRange1.endExclusive));
if (suffix.length > 0 && (suffix.trim().length <= 3 && cur.seq1Range.length + cur.seq2Range.length > 150)) {
range1 = range1.deltaEnd(suffix.length);
range2 = range2.deltaEnd(suffix.length);
if (shouldMarkAsChanged(suffix)) {
newDiff = newDiff.deltaEnd(suffix.length);
}
diffs[i] = new SequenceDiff(range1, range2);
}
const availableSpace = SequenceDiff.fromOffsetPairs(
prev ? prev.getEndExclusives() : OffsetPair.zero,
next ? next.getStarts() : OffsetPair.max,
);
const result = newDiff.intersect(availableSpace)!;
newDiffs.push(result);
});
return diffs;
return newDiffs;
}

View file

@ -96,7 +96,7 @@ export class LinesSliceCharSequence implements ISequence {
let score = 0;
if (prevCategory !== nextCategory) {
score += 10;
if (nextCategory === CharBoundaryCategory.WordUpper) {
if (prevCategory === CharBoundaryCategory.WordLower && nextCategory === CharBoundaryCategory.WordUpper) {
score += 1;
}
}

View file

@ -840,6 +840,13 @@ export interface ITextModel {
*/
isTooLargeForTokenization(): boolean;
/**
* The file is so large, that operations on it might be too large for heap
* and can lead to OOM crashes so they should be disabled.
* @internal
*/
isTooLargeForHeapOperation(): boolean;
/**
* Search the model.
* @param searchString The string used to search. If it is a regular expression, set `isRegex` to true.

View file

@ -177,6 +177,7 @@ export class TextModel extends Disposable implements model.ITextModel, IDecorati
static _MODEL_SYNC_LIMIT = 50 * 1024 * 1024; // 50 MB, // used in tests
private static readonly LARGE_FILE_SIZE_THRESHOLD = 20 * 1024 * 1024; // 20 MB;
private static readonly LARGE_FILE_LINE_COUNT_THRESHOLD = 300 * 1000; // 300K lines
private static readonly LARGE_FILE_HEAP_OPERATION_THRESHOLD = 256 * 1024 * 1024; // 256M characters, usually ~> 512MB memory usage
public static DEFAULT_CREATION_OPTIONS: model.ITextModelCreationOptions = {
isForSimpleWidget: false,
@ -257,6 +258,7 @@ export class TextModel extends Disposable implements model.ITextModel, IDecorati
private _initialUndoRedoSnapshot: ResourceEditStackSnapshot | null;
private readonly _isTooLargeForSyncing: boolean;
private readonly _isTooLargeForTokenization: boolean;
private readonly _isTooLargeForHeapOperation: boolean;
//#region Editing
private readonly _commandManager: EditStack;
@ -345,8 +347,11 @@ export class TextModel extends Disposable implements model.ITextModel, IDecorati
(bufferTextLength > TextModel.LARGE_FILE_SIZE_THRESHOLD)
|| (bufferLineCount > TextModel.LARGE_FILE_LINE_COUNT_THRESHOLD)
);
this._isTooLargeForHeapOperation = bufferTextLength > TextModel.LARGE_FILE_HEAP_OPERATION_THRESHOLD;
} else {
this._isTooLargeForTokenization = false;
this._isTooLargeForHeapOperation = false;
}
this._isTooLargeForSyncing = (bufferTextLength > TextModel._MODEL_SYNC_LIMIT);
@ -587,6 +592,10 @@ export class TextModel extends Disposable implements model.ITextModel, IDecorati
return this._isTooLargeForTokenization;
}
public isTooLargeForHeapOperation(): boolean {
return this._isTooLargeForHeapOperation;
}
public isDisposed(): boolean {
return this._isDisposed;
}
@ -743,6 +752,10 @@ export class TextModel extends Disposable implements model.ITextModel, IDecorati
public getValue(eol?: model.EndOfLinePreference, preserveBOM: boolean = false): string {
this._assertNotDisposed();
if (this.isTooLargeForHeapOperation()) {
throw new BugIndicatingError('Operation would exceed heap memory limits');
}
const fullModelRange = this.getFullModelRange();
const fullModelValue = this.getValueInRange(fullModelRange, eol);
@ -809,6 +822,10 @@ export class TextModel extends Disposable implements model.ITextModel, IDecorati
public getLinesContent(): string[] {
this._assertNotDisposed();
if (this.isTooLargeForHeapOperation()) {
throw new BugIndicatingError('Operation would exceed heap memory limits');
}
return this._buffer.getLinesContent();
}

View file

@ -574,7 +574,7 @@ export class EditorSimpleWorker implements IRequestHandler, IDisposable {
return result;
}
public async computeHumanReadableDiff(modelUrl: string, edits: TextEdit[], options: ILinesDiffComputerOptions): Promise<TextEdit[]> {
public computeHumanReadableDiff(modelUrl: string, edits: TextEdit[], options: ILinesDiffComputerOptions): TextEdit[] {
const model = this._getModel(modelUrl);
if (!model) {
return edits;

View file

@ -572,28 +572,28 @@ export enum KeyCode {
* Either the angle bracket key or the backslash key on the RT 102-key keyboard.
*/
IntlBackslash = 97,
Numpad0 = 98,
Numpad1 = 99,
Numpad2 = 100,
Numpad3 = 101,
Numpad4 = 102,
Numpad5 = 103,
Numpad6 = 104,
Numpad7 = 105,
Numpad8 = 106,
Numpad9 = 107,
NumpadMultiply = 108,
NumpadAdd = 109,
NUMPAD_SEPARATOR = 110,
NumpadSubtract = 111,
NumpadDecimal = 112,
NumpadDivide = 113,
Numpad0 = 98,// VK_NUMPAD0, 0x60, Numeric keypad 0 key
Numpad1 = 99,// VK_NUMPAD1, 0x61, Numeric keypad 1 key
Numpad2 = 100,// VK_NUMPAD2, 0x62, Numeric keypad 2 key
Numpad3 = 101,// VK_NUMPAD3, 0x63, Numeric keypad 3 key
Numpad4 = 102,// VK_NUMPAD4, 0x64, Numeric keypad 4 key
Numpad5 = 103,// VK_NUMPAD5, 0x65, Numeric keypad 5 key
Numpad6 = 104,// VK_NUMPAD6, 0x66, Numeric keypad 6 key
Numpad7 = 105,// VK_NUMPAD7, 0x67, Numeric keypad 7 key
Numpad8 = 106,// VK_NUMPAD8, 0x68, Numeric keypad 8 key
Numpad9 = 107,// VK_NUMPAD9, 0x69, Numeric keypad 9 key
NumpadMultiply = 108,// VK_MULTIPLY, 0x6A, Multiply key
NumpadAdd = 109,// VK_ADD, 0x6B, Add key
NUMPAD_SEPARATOR = 110,// VK_SEPARATOR, 0x6C, Separator key
NumpadSubtract = 111,// VK_SUBTRACT, 0x6D, Subtract key
NumpadDecimal = 112,// VK_DECIMAL, 0x6E, Decimal key
NumpadDivide = 113,// VK_DIVIDE, 0x6F,
/**
* Cover all key codes when IME is processing input.
*/
KEY_IN_COMPOSITION = 114,
ABNT_C1 = 115,
ABNT_C2 = 116,
ABNT_C1 = 115,// Brazilian (ABNT) Keyboard
ABNT_C2 = 116,// Brazilian (ABNT) Keyboard
AudioVolumeMute = 117,
AudioVolumeUp = 118,
AudioVolumeDown = 119,

View file

@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as browser from 'vs/base/browser/browser';
import { getActiveDocument } from 'vs/base/browser/dom';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import * as platform from 'vs/base/common/platform';
import { CopyOptions, InMemoryClipboardMetadataManager } from 'vs/editor/browser/controller/textAreaInput';
@ -179,7 +180,7 @@ class ExecCommandCopyWithSyntaxHighlightingAction extends EditorAction {
CopyOptions.forceCopyWithSyntaxHighlighting = true;
editor.focus();
document.execCommand('copy');
editor.getContainerDomNode().ownerDocument.execCommand('copy');
CopyOptions.forceCopyWithSyntaxHighlighting = false;
}
}
@ -200,7 +201,7 @@ function registerExecCommandImpl(target: MultiCommand | undefined, browserComman
if (selection && selection.isEmpty() && !emptySelectionClipboard) {
return true;
}
document.execCommand(browserCommand);
focusedEditor.getContainerDomNode().ownerDocument.execCommand(browserCommand);
return true;
}
return false;
@ -208,7 +209,7 @@ function registerExecCommandImpl(target: MultiCommand | undefined, browserComman
// 2. (default) handle case when focus is somewhere else.
target.addImplementation(0, 'generic-dom', (accessor: ServicesAccessor, args: any) => {
document.execCommand(browserCommand);
getActiveDocument().execCommand(browserCommand);
return true;
});
}
@ -225,7 +226,7 @@ if (PasteAction) {
// Only if editor text focus (i.e. not if editor has widget focus).
const focusedEditor = codeEditorService.getFocusedCodeEditor();
if (focusedEditor && focusedEditor.hasTextFocus()) {
const result = document.execCommand('paste');
const result = focusedEditor.getContainerDomNode().ownerDocument.execCommand('paste');
// Use the clipboard service if document.execCommand('paste') was not successful
if (!result && platform.isWeb) {
return (async () => {
@ -256,7 +257,7 @@ if (PasteAction) {
// 2. Paste: (default) handle case when focus is somewhere else.
PasteAction.addImplementation(0, 'generic-dom', (accessor: ServicesAccessor, args: any) => {
document.execCommand('paste');
getActiveDocument().execCommand('paste');
return true;
});
}

View file

@ -3,6 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import { coalesce, equals, isNonEmptyArray } from 'vs/base/common/arrays';
import { CancellationToken } from 'vs/base/common/cancellation';
import { illegalArgument, isCancellationError, onUnexpectedExternalError } from 'vs/base/common/errors';
@ -18,7 +19,6 @@ import { ITextModel } from 'vs/editor/common/model';
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
import { IModelService } from 'vs/editor/common/services/model';
import { TextModelCancellationTokenSource } from 'vs/editor/contrib/editorState/browser/editorState';
import * as nls from 'vs/nls';
import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { INotificationService } from 'vs/platform/notification/common/notification';
@ -89,6 +89,10 @@ export async function getCodeActions(
token: CancellationToken,
): Promise<CodeActionSet> {
const filter = trigger.filter || {};
const notebookFilter: CodeActionFilter = {
...filter,
excludes: [...(filter.excludes || []), CodeActionKind.Notebook],
};
const codeActionContext: languages.CodeActionContext = {
only: filter.include?.value,
@ -96,7 +100,9 @@ export async function getCodeActions(
};
const cts = new TextModelCancellationTokenSource(model, token);
const providers = getCodeActionProviders(registry, model, filter);
// if the trigger is auto (autosave, lightbulb, etc), we should exclude notebook codeActions
const excludeNotebookCodeActions = (trigger.type === languages.CodeActionTriggerType.Auto);
const providers = getCodeActionProviders(registry, model, (excludeNotebookCodeActions) ? notebookFilter : filter);
const disposables = new DisposableStore();
const promises = providers.map(async provider => {

View file

@ -30,6 +30,7 @@ import { IMarkerService } from 'vs/platform/markers/common/markers';
import { IEditorProgressService } from 'vs/platform/progress/common/progress';
import { CodeActionAutoApply, CodeActionFilter, CodeActionItem, CodeActionSet, CodeActionTrigger, CodeActionTriggerSource } from '../common/types';
import { CodeActionModel, CodeActionsState } from './codeActionModel';
import { CancellationToken } from 'vs/base/common/cancellation';
interface IActionShowOptions {
@ -251,6 +252,13 @@ export class CodeActionController extends Disposable implements IEditorContribut
},
onHide: () => {
this._editor?.focus();
},
onFocus: async (action: CodeActionItem, token: CancellationToken) => {
await action.resolve(token);
if (token.isCancellationRequested) {
return;
}
return { canPreview: !!action.action.edit?.edits.length };
}
};

View file

@ -44,7 +44,8 @@ export function toMenuItems(
item: action,
group: uncategorizedCodeActionGroup,
disabled: !!action.action.disabled,
label: action.action.disabled || action.action.title
label: action.action.disabled || action.action.title,
canPreview: !!action.action.edit?.edits.length,
};
});
}

View file

@ -20,6 +20,7 @@ export class CodeActionKind {
public static readonly RefactorInline = CodeActionKind.Refactor.append('inline');
public static readonly RefactorMove = CodeActionKind.Refactor.append('move');
public static readonly RefactorRewrite = CodeActionKind.Refactor.append('rewrite');
public static readonly Notebook = new CodeActionKind('notebook');
public static readonly Source = new CodeActionKind('source');
public static readonly SourceOrganizeImports = CodeActionKind.Source.append('organizeImports');
public static readonly SourceFixAll = CodeActionKind.Source.append('fixAll');

View file

@ -39,7 +39,7 @@ export class CodeLensContribution implements IEditorContribution {
private readonly _resolveCodeLensesScheduler: RunOnceScheduler;
private _getCodeLensModelPromise: CancelablePromise<CodeLensModel> | undefined;
private _oldCodeLensModels = new DisposableStore();
private readonly _oldCodeLensModels = new DisposableStore();
private _currentCodeLensModel: CodeLensModel | undefined;
private _resolveCodeLensesPromise: CancelablePromise<any> | undefined;

View file

@ -264,7 +264,7 @@ class SaturationBox extends Disposable {
this.monitor.startMonitoring(e.target, e.pointerId, e.buttons, event => this.onDidChangePosition(event.pageX - origin.left, event.pageY - origin.top), () => null);
const pointerUpListener = dom.addDisposableListener(document, dom.EventType.POINTER_UP, () => {
const pointerUpListener = dom.addDisposableListener(e.target.ownerDocument, dom.EventType.POINTER_UP, () => {
this._onColorFlushed.fire();
pointerUpListener.dispose();
if (this.monitor) {
@ -387,7 +387,7 @@ abstract class Strip extends Disposable {
monitor.startMonitoring(e.target, e.pointerId, e.buttons, event => this.onDidChangeTop(event.pageY - origin.top), () => null);
const pointerUpListener = dom.addDisposableListener(document, dom.EventType.POINTER_UP, () => {
const pointerUpListener = dom.addDisposableListener(e.target.ownerDocument, dom.EventType.POINTER_UP, () => {
this._onColorFlushed.fire();
pointerUpListener.dispose();
monitor.stopMonitoring(true);

View file

@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { addDisposableListener } from 'vs/base/browser/dom';
import { addDisposableListener, getActiveDocument } from 'vs/base/browser/dom';
import { coalesce } from 'vs/base/common/arrays';
import { CancelablePromise, createCancelablePromise, raceCancellation } from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
@ -99,7 +99,7 @@ export class CopyPasteController extends Disposable implements IEditorContributi
this._editor.focus();
try {
this._pasteAsActionContext = { preferredId };
document.execCommand('paste');
getActiveDocument().execCommand('paste');
} finally {
this._pasteAsActionContext = undefined;
}

View file

@ -97,6 +97,7 @@ export class CommonFindController extends Disposable implements IEditorContribut
protected readonly _storageService: IStorageService;
private readonly _clipboardService: IClipboardService;
protected readonly _contextKeyService: IContextKeyService;
protected readonly _notificationService: INotificationService;
get editor() {
return this._editor;
@ -110,7 +111,8 @@ export class CommonFindController extends Disposable implements IEditorContribut
editor: ICodeEditor,
@IContextKeyService contextKeyService: IContextKeyService,
@IStorageService storageService: IStorageService,
@IClipboardService clipboardService: IClipboardService
@IClipboardService clipboardService: IClipboardService,
@INotificationService notificationService: INotificationService
) {
super();
this._editor = editor;
@ -118,6 +120,7 @@ export class CommonFindController extends Disposable implements IEditorContribut
this._contextKeyService = contextKeyService;
this._storageService = storageService;
this._clipboardService = clipboardService;
this._notificationService = notificationService;
this._updateHistoryDelayer = new Delayer<void>(500);
this._state = this._register(new FindReplaceState());
@ -390,6 +393,10 @@ export class CommonFindController extends Disposable implements IEditorContribut
public replaceAll(): boolean {
if (this._model) {
if (this._editor.getModel()?.isTooLargeForHeapOperation()) {
this._notificationService.warn(nls.localize('too.large.for.replaceall', "The file is too large to perform a replace all operation."));
return false;
}
this._model.replaceAll();
return true;
}
@ -437,11 +444,11 @@ export class FindController extends CommonFindController implements IFindControl
@IContextKeyService _contextKeyService: IContextKeyService,
@IKeybindingService private readonly _keybindingService: IKeybindingService,
@IThemeService private readonly _themeService: IThemeService,
@INotificationService private readonly _notificationService: INotificationService,
@INotificationService notificationService: INotificationService,
@IStorageService _storageService: IStorageService,
@IClipboardService clipboardService: IClipboardService,
) {
super(editor, _contextKeyService, _storageService, clipboardService);
super(editor, _contextKeyService, _storageService, clipboardService, notificationService);
this._widget = null;
this._findOptionsWidget = null;
}

View file

@ -20,6 +20,7 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IStorageService, InMemoryStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
class TestFindController extends CommonFindController {
@ -33,9 +34,10 @@ class TestFindController extends CommonFindController {
editor: ICodeEditor,
@IContextKeyService contextKeyService: IContextKeyService,
@IStorageService storageService: IStorageService,
@IClipboardService clipboardService: IClipboardService
@IClipboardService clipboardService: IClipboardService,
@INotificationService notificationService: INotificationService
) {
super(editor, contextKeyService, storageService, clipboardService);
super(editor, contextKeyService, storageService, clipboardService, notificationService);
this._findInputFocused = CONTEXT_FIND_INPUT_FOCUSED.bindTo(contextKeyService);
this._updateHistoryDelayer = new Delayer<void>(50);
this.hasFocus = false;

View file

@ -675,7 +675,7 @@ export class ContentHoverWidget extends ResizableContentWidget {
);
if (overflowing || this._hover.containerDomNode.clientWidth < initialWidth) {
const bodyBoxWidth = dom.getClientArea(document.body).width;
const bodyBoxWidth = dom.getClientArea(this._hover.containerDomNode.ownerDocument.body).width;
const horizontalPadding = 14;
return bodyBoxWidth - horizontalPadding;
} else {

View file

@ -78,7 +78,7 @@ export abstract class ResizableContentWidget extends Disposable implements ICont
return;
}
const editorBox = dom.getDomNodePagePosition(editorDomNode);
const bodyBox = dom.getClientArea(document.body);
const bodyBox = dom.getClientArea(editorDomNode.ownerDocument.body);
const mouseBottom = editorBox.top + mouseBox.top + mouseBox.height;
return bodyBox.height - mouseBottom - BOTTOM_HEIGHT;
}

View file

@ -708,7 +708,7 @@ export class DeleteAllLeftAction extends AbstractDeleteAllToBoundaryAction {
if (selection.isEmpty()) {
if (selection.startColumn === 1) {
const deleteFromLine = Math.max(1, selection.startLineNumber - 1);
const deleteFromColumn = selection.startLineNumber === 1 ? 1 : model.getLineContent(deleteFromLine).length + 1;
const deleteFromColumn = selection.startLineNumber === 1 ? 1 : model.getLineLength(deleteFromLine) + 1;
return new Range(deleteFromLine, deleteFromColumn, selection.startLineNumber, 1);
} else {
return new Range(selection.startLineNumber, 1, selection.startLineNumber, selection.startColumn);
@ -864,7 +864,7 @@ export class JoinLinesAction extends EditorAction {
let endLineNumber: number,
endColumn: number;
const selectionEndPositionOffset = model.getLineContent(selection.endLineNumber).length - selection.endColumn;
const selectionEndPositionOffset = model.getLineLength(selection.endLineNumber) - selection.endColumn;
if (selection.isEmpty() || selection.startLineNumber === selection.endLineNumber) {
const position = selection.getStartPosition();

View file

@ -82,7 +82,7 @@ export class MessageController implements IEditorContribution {
return; // override when mouse over message
}
if (this._messageWidget.value && dom.isAncestor(document.activeElement, this._messageWidget.value.getDomNode())) {
if (this._messageWidget.value && dom.isAncestor(dom.getActiveElement(), this._messageWidget.value.getDomNode())) {
return; // override when focus is inside the message
}

View file

@ -192,7 +192,7 @@ export class RenameInputField implements IContentWidget {
};
disposeOnDone.add(token.onCancellationRequested(() => this.cancelInput(true)));
disposeOnDone.add(this._editor.onDidBlurEditorWidget(() => this.cancelInput(!document.hasFocus())));
disposeOnDone.add(this._editor.onDidBlurEditorWidget(() => this.cancelInput(!this._domNode?.ownerDocument.hasFocus())));
this._show();

View file

@ -62,7 +62,7 @@ export class SnippetController2 implements IEditorContribution {
private readonly _hasPrevTabstop: IContextKey<boolean>;
private _session?: SnippetSession;
private _snippetListener = new DisposableStore();
private readonly _snippetListener = new DisposableStore();
private _modelVersionId: number = -1;
private _currentChoice?: Choice;

View file

@ -787,7 +787,7 @@ export class SuggestWidget implements IDisposable {
return;
}
const bodyBox = dom.getClientArea(document.body);
const bodyBox = dom.getClientArea(this.element.domNode.ownerDocument.body);
const info = this.getLayoutInfo();
if (!size) {

View file

@ -372,7 +372,7 @@ export class SuggestDetailsOverlay implements IOverlayWidget {
}
_placeAtAnchor(anchorBox: dom.IDomNodePagePosition, size: dom.Dimension, preferAlignAtTop: boolean) {
const bodyBox = dom.getClientArea(document.body);
const bodyBox = dom.getClientArea(this.getDomNode().ownerDocument.body);
const info = this.widget.getLayoutInfo();

View file

@ -91,6 +91,8 @@ suite('TextAreaInput', () => {
private _state: IRecordedTextareaState;
private _currDispatchingEvent: IRecordedEvent | null;
public ownerDocument = document;
constructor() {
super();
this._state = {

View file

@ -100,6 +100,7 @@ export class TestCodeEditor extends CodeEditorWidget implements ICodeEditor {
class TestEditorDomElement {
parentElement: IContextKeyServiceTarget | null = null;
ownerDocument = document;
setAttribute(attr: string, value: string): void { }
removeAttribute(attr: string): void { }
hasAttribute(attr: string): boolean { return false; }

View file

@ -257,7 +257,7 @@ suite('EditorSimpleWorker', () => {
);
});
test.skip('[Bug] Getting Message "Overlapping ranges are not allowed" and nothing happens with Inline-Chat ', async function () {
test('[Bug] Getting Message "Overlapping ranges are not allowed" and nothing happens with Inline-Chat ', async function () {
await testEdits(("const API = require('../src/api');\n\ndescribe('API', () => {\n let api;\n let database;\n\n beforeAll(() => {\n database = {\n getAllBooks: jest.fn(),\n getBooksByAuthor: jest.fn(),\n getBooksByTitle: jest.fn(),\n };\n api = new API(database);\n });\n\n describe('GET /books', () => {\n it('should return all books', async () => {\n const mockBooks = [{ title: 'Book 1' }, { title: 'Book 2' }];\n database.getAllBooks.mockResolvedValue(mockBooks);\n\n const req = {};\n const res = {\n json: jest.fn(),\n };\n\n await api.register({\n get: (path, handler) => {\n if (path === '/books') {\n handler(req, res);\n }\n },\n });\n\n expect(database.getAllBooks).toHaveBeenCalled();\n expect(res.json).toHaveBeenCalledWith(mockBooks);\n });\n });\n\n describe('GET /books/author/:author', () => {\n it('should return books by author', async () => {\n const mockAuthor = 'John Doe';\n const mockBooks = [{ title: 'Book 1', author: mockAuthor }, { title: 'Book 2', author: mockAuthor }];\n database.getBooksByAuthor.mockResolvedValue(mockBooks);\n\n const req = {\n params: {\n author: mockAuthor,\n },\n };\n const res = {\n json: jest.fn(),\n };\n\n await api.register({\n get: (path, handler) => {\n if (path === `/books/author/${mockAuthor}`) {\n handler(req, res);\n }\n },\n });\n\n expect(database.getBooksByAuthor).toHaveBeenCalledWith(mockAuthor);\n expect(res.json).toHaveBeenCalledWith(mockBooks);\n });\n });\n\n describe('GET /books/title/:title', () => {\n it('should return books by title', async () => {\n const mockTitle = 'Book 1';\n const mockBooks = [{ title: mockTitle, author: 'John Doe' }];\n database.getBooksByTitle.mockResolvedValue(mockBooks);\n\n const req = {\n params: {\n title: mockTitle,\n },\n };\n const res = {\n json: jest.fn(),\n };\n\n await api.register({\n get: (path, handler) => {\n if (path === `/books/title/${mockTitle}`) {\n handler(req, res);\n }\n },\n });\n\n expect(database.getBooksByTitle).toHaveBeenCalledWith(mockTitle);\n expect(res.json).toHaveBeenCalledWith(mockBooks);\n });\n });\n});\n").split('\n'),
[{
range: { startLineNumber: 1, startColumn: 1, endLineNumber: 96, endColumn: 1 },
@ -337,7 +337,7 @@ function applyEdits(text: string, edits: { range: IRange; text: string }[]): str
class PositionOffsetTransformer {
private readonly lineStartOffsetByLineIdx: number[];
constructor(text: string) {
constructor(private readonly text: string) {
this.lineStartOffsetByLineIdx = [];
this.lineStartOffsetByLineIdx.push(0);
for (let i = 0; i < text.length; i++) {
@ -349,7 +349,7 @@ class PositionOffsetTransformer {
}
getOffset(position: Position): number {
const nextLineOffset = this.lineStartOffsetByLineIdx[position.lineNumber];
return Math.min(this.lineStartOffsetByLineIdx[position.lineNumber - 1] + position.column - 1, nextLineOffset - 1);
const maxLineOffset = position.lineNumber >= this.lineStartOffsetByLineIdx.length ? this.text.length : (this.lineStartOffsetByLineIdx[position.lineNumber] - 1);
return Math.min(this.lineStartOffsetByLineIdx[position.lineNumber - 1] + position.column - 1, maxLineOffset);
}
}

View file

@ -11,6 +11,7 @@ import { FileAccess } from 'vs/base/common/network';
import { DetailedLineRangeMapping } from 'vs/editor/common/diff/rangeMapping';
import { LegacyLinesDiffComputer } from 'vs/editor/common/diff/legacyLinesDiffComputer';
import { DefaultLinesDiffComputer } from 'vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer';
import { Range } from 'vs/editor/common/core/range';
suite('diffing fixtures', () => {
setup(() => {
@ -48,12 +49,18 @@ suite('diffing fixtures', () => {
originalRange: c.original.toString(),
modifiedRange: c.modified.toString(),
innerChanges: c.innerChanges?.map<IDiff>(c => ({
originalRange: c.originalRange.toString(),
modifiedRange: c.modifiedRange.toString(),
originalRange: formatRange(c.originalRange, firstContentLines),
modifiedRange: formatRange(c.modifiedRange, secondContentLines),
})) || null
}));
}
function formatRange(range: Range, lines: string[]): string {
const toLastChar = range.endColumn === lines[range.endLineNumber - 1].length + 1;
return '[' + range.startLineNumber + ',' + range.startColumn + ' -> ' + range.endLineNumber + ',' + range.endColumn + (toLastChar ? ' EOL' : '') + ']';
}
const actualDiffingResult: DiffingResult = {
original: { content: firstContent, fileName: `./${firstFileName}` },
modified: { content: secondContent, fileName: `./${secondFileName}` },

View file

@ -13,8 +13,8 @@
"modifiedRange": "[29,31)",
"innerChanges": [
{
"originalRange": "[29,1 -> 33,41]",
"modifiedRange": "[29,1 -> 30,54]"
"originalRange": "[29,1 -> 33,41 EOL]",
"modifiedRange": "[29,1 -> 30,54 EOL]"
}
]
},
@ -59,8 +59,8 @@
"modifiedRange": "[36,42 -> 36,43]"
},
{
"originalRange": "[41,29 -> 45,24]",
"modifiedRange": "[36,46 -> 36,66]"
"originalRange": "[41,29 -> 45,24 EOL]",
"modifiedRange": "[36,46 -> 36,66 EOL]"
}
]
},

View file

@ -14,7 +14,7 @@
"innerChanges": [
{
"originalRange": "[1,1 -> 29,64]",
"modifiedRange": "[1,1 -> 1,1]"
"modifiedRange": "[1,1 -> 1,1 EOL]"
}
]
}

View file

@ -33,8 +33,8 @@
"modifiedRange": "[156,163)",
"innerChanges": [
{
"originalRange": "[159,1 -> 159,1]",
"modifiedRange": "[156,1 -> 163,1]"
"originalRange": "[159,1 -> 159,1 EOL]",
"modifiedRange": "[156,1 -> 163,1 EOL]"
}
]
},

View file

@ -28,8 +28,8 @@
"modifiedRange": "[226,234)",
"innerChanges": [
{
"originalRange": "[222,17 -> 222,19]",
"modifiedRange": "[226,17 -> 226,17]"
"originalRange": "[222,17 -> 222,19 EOL]",
"modifiedRange": "[226,17 -> 226,17 EOL]"
},
{
"originalRange": "[223,4 -> 223,28]",
@ -40,8 +40,8 @@
"modifiedRange": "[227,41 -> 227,48]"
},
{
"originalRange": "[223,54 -> 223,65]",
"modifiedRange": "[227,53 -> 227,62]"
"originalRange": "[223,54 -> 223,65 EOL]",
"modifiedRange": "[227,53 -> 227,62 EOL]"
},
{
"originalRange": "[224,4 -> 224,21]",
@ -60,8 +60,8 @@
"modifiedRange": "[228,123 -> 228,152]"
},
{
"originalRange": "[226,22 -> 226,25]",
"modifiedRange": "[228,166 -> 228,169]"
"originalRange": "[226,22 -> 226,25 EOL]",
"modifiedRange": "[228,166 -> 228,169 EOL]"
},
{
"originalRange": "[227,5 -> 227,30]",
@ -72,16 +72,16 @@
"modifiedRange": "[229,28 -> 229,32]"
},
{
"originalRange": "[227,45 -> 227,93]",
"modifiedRange": "[229,35 -> 229,67]"
"originalRange": "[227,45 -> 227,93 EOL]",
"modifiedRange": "[229,35 -> 229,67 EOL]"
},
{
"originalRange": "[228,5 -> 228,51]",
"modifiedRange": "[230,5 -> 230,30]"
"originalRange": "[228,5 -> 228,51 EOL]",
"modifiedRange": "[230,5 -> 230,30 EOL]"
},
{
"originalRange": "[229,6 -> 230,6]",
"modifiedRange": "[231,6 -> 231,40]"
"originalRange": "[229,6 -> 230,6 EOL]",
"modifiedRange": "[231,6 -> 231,40 EOL]"
},
{
"originalRange": "[231,4 -> 232,42]",
@ -92,8 +92,8 @@
"modifiedRange": "[232,25 -> 233,32]"
},
{
"originalRange": "[232,72 -> 233,4]",
"modifiedRange": "[233,35 -> 233,60]"
"originalRange": "[232,72 -> 233,4 EOL]",
"modifiedRange": "[233,35 -> 233,60 EOL]"
}
]
}

View file

@ -17,32 +17,32 @@
"modifiedRange": "[1,1 -> 1,1]"
},
{
"originalRange": "[2,15 -> 2,15]",
"modifiedRange": "[1,15 -> 1,16]"
"originalRange": "[2,15 -> 2,15 EOL]",
"modifiedRange": "[1,15 -> 1,16 EOL]"
},
{
"originalRange": "[3,15 -> 3,15]",
"modifiedRange": "[2,15 -> 2,16]"
"originalRange": "[3,15 -> 3,15 EOL]",
"modifiedRange": "[2,15 -> 2,16 EOL]"
},
{
"originalRange": "[4,15 -> 4,15]",
"modifiedRange": "[3,15 -> 3,16]"
"originalRange": "[4,15 -> 4,15 EOL]",
"modifiedRange": "[3,15 -> 3,16 EOL]"
},
{
"originalRange": "[5,15 -> 5,15]",
"modifiedRange": "[4,15 -> 5,1]"
"originalRange": "[5,15 -> 5,15 EOL]",
"modifiedRange": "[4,15 -> 5,1 EOL]"
},
{
"originalRange": "[6,15 -> 6,15]",
"modifiedRange": "[6,15 -> 6,16]"
"originalRange": "[6,15 -> 6,15 EOL]",
"modifiedRange": "[6,15 -> 6,16 EOL]"
},
{
"originalRange": "[7,15 -> 7,15]",
"modifiedRange": "[7,15 -> 7,16]"
"originalRange": "[7,15 -> 7,15 EOL]",
"modifiedRange": "[7,15 -> 7,16 EOL]"
},
{
"originalRange": "[8,15 -> 8,15]",
"modifiedRange": "[8,15 -> 9,1]"
"originalRange": "[8,15 -> 8,15 EOL]",
"modifiedRange": "[8,15 -> 9,1 EOL]"
}
]
}

View file

@ -0,0 +1,3 @@
import { RuntimeMode } from './runtimeMode';
import { PromiseQueue } from './telemetry';
import { TestNotificationSender, TestUrlOpener } from './testHelpers';

View file

@ -0,0 +1,3 @@
import { RuntimeMode } from './runtimeMode';
import { PromiseQueue, TestPromiseQueue } from './telemetry';
import { TestNotificationSender, TestUrlOpener } from './testHelpers';

View file

@ -0,0 +1,22 @@
{
"original": {
"content": "import { RuntimeMode } from './runtimeMode';\nimport { PromiseQueue } from './telemetry';\nimport { TestNotificationSender, TestUrlOpener } from './testHelpers';\n",
"fileName": "./1.tst"
},
"modified": {
"content": "import { RuntimeMode } from './runtimeMode';\nimport { PromiseQueue, TestPromiseQueue } from './telemetry';\nimport { TestNotificationSender, TestUrlOpener } from './testHelpers';\n",
"fileName": "./2.tst"
},
"diffs": [
{
"originalRange": "[2,3)",
"modifiedRange": "[2,3)",
"innerChanges": [
{
"originalRange": "[2,22 -> 2,22]",
"modifiedRange": "[2,22 -> 2,40]"
}
]
}
]
}

View file

@ -0,0 +1,22 @@
{
"original": {
"content": "import { RuntimeMode } from './runtimeMode';\nimport { PromiseQueue } from './telemetry';\nimport { TestNotificationSender, TestUrlOpener } from './testHelpers';\n",
"fileName": "./1.tst"
},
"modified": {
"content": "import { RuntimeMode } from './runtimeMode';\nimport { PromiseQueue, TestPromiseQueue } from './telemetry';\nimport { TestNotificationSender, TestUrlOpener } from './testHelpers';\n",
"fileName": "./2.tst"
},
"diffs": [
{
"originalRange": "[2,3)",
"modifiedRange": "[2,3)",
"innerChanges": [
{
"originalRange": "[2,22 -> 2,22]",
"modifiedRange": "[2,22 -> 2,40]"
}
]
}
]
}

View file

@ -13,16 +13,16 @@
"modifiedRange": "[1,4)",
"innerChanges": [
{
"originalRange": "[1,15 -> 1,16]",
"modifiedRange": "[1,15 -> 1,15]"
"originalRange": "[1,15 -> 1,16 EOL]",
"modifiedRange": "[1,15 -> 1,15 EOL]"
},
{
"originalRange": "[2,15 -> 3,16]",
"modifiedRange": "[2,15 -> 2,15]"
"originalRange": "[2,15 -> 3,16 EOL]",
"modifiedRange": "[2,15 -> 2,15 EOL]"
},
{
"originalRange": "[4,15 -> 4,16]",
"modifiedRange": "[3,15 -> 3,15]"
"originalRange": "[4,15 -> 4,16 EOL]",
"modifiedRange": "[3,15 -> 3,15 EOL]"
}
]
}

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