mirror of
https://github.com/Microsoft/vscode
synced 2024-08-27 04:49:35 +00:00
Merge branch 'main' into aamunger/scrollOutputWithKeys
This commit is contained in:
commit
9ede543860
|
@ -51,6 +51,7 @@ const setLauncherEnvironmentVars = () => {
|
|||
['VSCODE_CLI_TUNNEL_SERVICE_MUTEX', product.win32TunnelServiceMutex],
|
||||
['VSCODE_CLI_TUNNEL_CLI_MUTEX', product.win32TunnelMutex],
|
||||
['VSCODE_CLI_COMMIT', commit],
|
||||
['VSCODE_CLI_DEFAULT_PARENT_DATA_DIR', product.dataFolderName],
|
||||
[
|
||||
'VSCODE_CLI_WIN32_APP_IDS',
|
||||
!isOSS && JSON.stringify(makeQualityMap(json => Object.entries(json)
|
||||
|
@ -88,4 +89,4 @@ const setLauncherEnvironmentVars = () => {
|
|||
if (require.main === module) {
|
||||
setLauncherEnvironmentVars();
|
||||
}
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJlcGFyZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInByZXBhcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Z0dBR2dHOztBQUVoRyxxREFBa0Q7QUFDbEQseUJBQXlCO0FBQ3pCLDZCQUE2QjtBQUM3QixxREFBcUQ7QUFFckQsTUFBTSxJQUFJLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyx1QkFBdUIsSUFBSSxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDeEcsTUFBTSxRQUFRLEdBQUcsQ0FBQyxJQUFZLEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLFlBQVksQ0FBQyxJQUFJLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQztBQUU3RSxJQUFJLGVBQXVCLENBQUM7QUFDNUIsTUFBTSxLQUFLLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxjQUFjLEtBQUssS0FBSyxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxjQUFjLENBQUM7QUFDbEYsSUFBSSxLQUFLLEVBQUU7SUFDVixlQUFlLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsY0FBYyxDQUFDLENBQUM7Q0FDbEQ7S0FBTTtJQUNOLGVBQWUsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxPQUFPLEVBQUUsT0FBTyxDQUFDLEdBQUcsQ0FBQyxjQUFlLEVBQUUsY0FBYyxDQUFDLENBQUM7Q0FDeEY7QUFFRCxPQUFPLENBQUMsS0FBSyxDQUFDLDJCQUEyQixFQUFFLGVBQWUsQ0FBQyxDQUFDO0FBQzVELE1BQU0sT0FBTyxHQUFHLFFBQVEsQ0FBQyxlQUFlLENBQUMsQ0FBQztBQUMxQyxNQUFNLHVCQUF1QixHQUFHLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxPQUFPLENBQUMsQ0FBQztLQUMxRixHQUFHLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQyxDQUFDLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxRQUFRLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsT0FBTyxFQUFFLE9BQU8sRUFBRSxjQUFjLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0FBQ25HLE1BQU0sTUFBTSxHQUFHLElBQUEsdUJBQVUsRUFBQyxJQUFJLENBQUMsQ0FBQztBQUVoQyxNQUFNLGNBQWMsR0FBRyxDQUFJLENBQTJDLEVBQXFCLEVBQUU7SUFDNUYsTUFBTSxNQUFNLEdBQXNCLEVBQUUsQ0FBQztJQUNyQyxLQUFLLE1BQU0sRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLElBQUksdUJBQXVCLEVBQUU7UUFDeEQsTUFBTSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsT0FBTyxDQUFDLENBQUM7S0FDbkM7SUFDRCxPQUFPLE1BQU0sQ0FBQztBQUNmLENBQUMsQ0FBQztBQUVGOztHQUVHO0FBQ0gsTUFBTSwwQkFBMEIsR0FBRyxHQUFHLEVBQUU7SUFDdkMsTUFBTSxJQUFJLEdBQUcsSUFBSSxHQUFHLENBQUM7UUFDcEIsQ0FBQyxnQ0FBZ0MsRUFBRSxPQUFPLENBQUMsYUFBYSxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUN0RSxDQUFDLGtDQUFrQyxFQUFFLE9BQU8sQ0FBQyxtQkFBbUIsQ0FBQztRQUNqRSxDQUFDLG1CQUFtQixFQUFFLE9BQU8sQ0FBQyxRQUFRLEVBQUUsTUFBTSxDQUFDO1FBQy9DLENBQUMsd0JBQXdCLEVBQUUsT0FBTyxDQUFDLFFBQVEsRUFBRSxXQUFXLENBQUM7UUFDekQsQ0FBQyxvQkFBb0IsRUFBRSxXQUFXLENBQUMsT0FBTyxDQUFDO1FBQzNDLENBQUMsNEJBQTRCLEVBQUUsT0FBTyxDQUFDLFNBQVMsQ0FBQztRQUNqRCxDQUFDLG9CQUFvQixFQUFFLE9BQU8sQ0FBQyxPQUFPLENBQUM7UUFDdkMsQ0FBQyx1QkFBdUIsRUFBRSxPQUFPLENBQUMsU0FBUyxDQUFDO1FBQzVDLENBQUMsc0JBQXNCLEVBQUUsT0FBTyxDQUFDLFFBQVEsQ0FBQztRQUMxQyxDQUFDLHFDQUFxQyxFQUFFLE9BQU8sQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLGFBQWEsRUFBRSxFQUFFLENBQUMsQ0FBQztRQUNwRixDQUFDLDhCQUE4QixFQUFFLE9BQU8sQ0FBQyxnQkFBZ0IsQ0FBQztRQUMxRCxDQUFDLDZCQUE2QixFQUFFLE9BQU8sQ0FBQyxlQUFlLENBQUM7UUFDeEQsQ0FBQywyQkFBMkIsRUFBRSxPQUFPLENBQUMsdUJBQXVCLEVBQUUsWUFBWSxDQUFDO1FBQzVFLENBQUMsaUNBQWlDLEVBQUUsT0FBTyxDQUFDLHVCQUF1QixDQUFDO1FBQ3BFLENBQUMsNkJBQTZCLEVBQUUsT0FBTyxDQUFDLGdCQUFnQixDQUFDO1FBQ3pELENBQUMsbUJBQW1CLEVBQUUsTUFBTSxDQUFDO1FBQzdCO1lBQ0MsMEJBQTBCO1lBQzFCLENBQUMsS0FBSyxJQUFJLElBQUksQ0FBQyxTQUFTLENBQ3ZCLGNBQWMsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDO2lCQUN6QyxNQUFNLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxnQkFBZ0IsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7aUJBQzdDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxLQUFLLENBQUMsRUFBRSxFQUFFLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLE9BQU8sQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUN6RDtTQUNEO1FBQ0Q7WUFDQywwQkFBMEI7WUFDMUIsQ0FBQyxLQUFLLElBQUksSUFBSSxDQUFDLFNBQVMsQ0FBQyxjQUFjLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7U0FDL0Q7UUFDRDtZQUNDLGlDQUFpQztZQUNqQyxDQUFDLEtBQUssSUFBSSxJQUFJLENBQUMsU0FBUyxDQUFDLGNBQWMsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxlQUFlLENBQUMsQ0FBQztTQUN0RTtRQUNEO1lBQ0MsNEJBQTRCO1lBQzVCLENBQUMsS0FBSyxJQUFJLElBQUksQ0FBQyxTQUFTLENBQUMsY0FBYyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLHFCQUFxQixDQUFDLENBQUM7U0FDNUU7UUFDRDtZQUNDLGtDQUFrQztZQUNsQyxDQUFDLEtBQUssSUFBSSxJQUFJLENBQUMsU0FBUyxDQUFDLGNBQWMsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsQ0FBQztTQUNsRTtLQUNELENBQUMsQ0FBQztJQUVILElBQUksT0FBTyxDQUFDLEdBQUcsQ0FBQyx5QkFBeUIsS0FBSyxNQUFNLEVBQUU7UUFDckQsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7S0FDOUQ7U0FBTTtRQUNOLEtBQUssTUFBTSxDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsSUFBSSxJQUFJLEVBQUU7WUFDaEMsSUFBSSxLQUFLLEVBQUU7Z0JBQ1YsT0FBTyxDQUFDLEdBQUcsQ0FBQyxtQ0FBbUMsR0FBRyxJQUFJLEtBQUssRUFBRSxDQUFDLENBQUM7YUFDL0Q7U0FDRDtLQUNEO0FBRUYsQ0FBQyxDQUFDO0FBRUYsSUFBSSxPQUFPLENBQUMsSUFBSSxLQUFLLE1BQU0sRUFBRTtJQUM1QiwwQkFBMEIsRUFBRSxDQUFDO0NBQzdCIn0=
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJlcGFyZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInByZXBhcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Z0dBR2dHOztBQUVoRyxxREFBa0Q7QUFDbEQseUJBQXlCO0FBQ3pCLDZCQUE2QjtBQUM3QixxREFBcUQ7QUFFckQsTUFBTSxJQUFJLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyx1QkFBdUIsSUFBSSxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDeEcsTUFBTSxRQUFRLEdBQUcsQ0FBQyxJQUFZLEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLFlBQVksQ0FBQyxJQUFJLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQztBQUU3RSxJQUFJLGVBQXVCLENBQUM7QUFDNUIsTUFBTSxLQUFLLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxjQUFjLEtBQUssS0FBSyxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxjQUFjLENBQUM7QUFDbEYsSUFBSSxLQUFLLEVBQUU7SUFDVixlQUFlLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsY0FBYyxDQUFDLENBQUM7Q0FDbEQ7S0FBTTtJQUNOLGVBQWUsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxPQUFPLEVBQUUsT0FBTyxDQUFDLEdBQUcsQ0FBQyxjQUFlLEVBQUUsY0FBYyxDQUFDLENBQUM7Q0FDeEY7QUFFRCxPQUFPLENBQUMsS0FBSyxDQUFDLDJCQUEyQixFQUFFLGVBQWUsQ0FBQyxDQUFDO0FBQzVELE1BQU0sT0FBTyxHQUFHLFFBQVEsQ0FBQyxlQUFlLENBQUMsQ0FBQztBQUMxQyxNQUFNLHVCQUF1QixHQUFHLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxPQUFPLENBQUMsQ0FBQztLQUMxRixHQUFHLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQyxDQUFDLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxRQUFRLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsT0FBTyxFQUFFLE9BQU8sRUFBRSxjQUFjLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0FBQ25HLE1BQU0sTUFBTSxHQUFHLElBQUEsdUJBQVUsRUFBQyxJQUFJLENBQUMsQ0FBQztBQUVoQyxNQUFNLGNBQWMsR0FBRyxDQUFJLENBQTJDLEVBQXFCLEVBQUU7SUFDNUYsTUFBTSxNQUFNLEdBQXNCLEVBQUUsQ0FBQztJQUNyQyxLQUFLLE1BQU0sRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLElBQUksdUJBQXVCLEVBQUU7UUFDeEQsTUFBTSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsT0FBTyxDQUFDLENBQUM7S0FDbkM7SUFDRCxPQUFPLE1BQU0sQ0FBQztBQUNmLENBQUMsQ0FBQztBQUVGOztHQUVHO0FBQ0gsTUFBTSwwQkFBMEIsR0FBRyxHQUFHLEVBQUU7SUFDdkMsTUFBTSxJQUFJLEdBQUcsSUFBSSxHQUFHLENBQUM7UUFDcEIsQ0FBQyxnQ0FBZ0MsRUFBRSxPQUFPLENBQUMsYUFBYSxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUN0RSxDQUFDLGtDQUFrQyxFQUFFLE9BQU8sQ0FBQyxtQkFBbUIsQ0FBQztRQUNqRSxDQUFDLG1CQUFtQixFQUFFLE9BQU8sQ0FBQyxRQUFRLEVBQUUsTUFBTSxDQUFDO1FBQy9DLENBQUMsd0JBQXdCLEVBQUUsT0FBTyxDQUFDLFFBQVEsRUFBRSxXQUFXLENBQUM7UUFDekQsQ0FBQyxvQkFBb0IsRUFBRSxXQUFXLENBQUMsT0FBTyxDQUFDO1FBQzNDLENBQUMsNEJBQTRCLEVBQUUsT0FBTyxDQUFDLFNBQVMsQ0FBQztRQUNqRCxDQUFDLG9CQUFvQixFQUFFLE9BQU8sQ0FBQyxPQUFPLENBQUM7UUFDdkMsQ0FBQyx1QkFBdUIsRUFBRSxPQUFPLENBQUMsU0FBUyxDQUFDO1FBQzVDLENBQUMsc0JBQXNCLEVBQUUsT0FBTyxDQUFDLFFBQVEsQ0FBQztRQUMxQyxDQUFDLHFDQUFxQyxFQUFFLE9BQU8sQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLGFBQWEsRUFBRSxFQUFFLENBQUMsQ0FBQztRQUNwRixDQUFDLDhCQUE4QixFQUFFLE9BQU8sQ0FBQyxnQkFBZ0IsQ0FBQztRQUMxRCxDQUFDLDZCQUE2QixFQUFFLE9BQU8sQ0FBQyxlQUFlLENBQUM7UUFDeEQsQ0FBQywyQkFBMkIsRUFBRSxPQUFPLENBQUMsdUJBQXVCLEVBQUUsWUFBWSxDQUFDO1FBQzVFLENBQUMsaUNBQWlDLEVBQUUsT0FBTyxDQUFDLHVCQUF1QixDQUFDO1FBQ3BFLENBQUMsNkJBQTZCLEVBQUUsT0FBTyxDQUFDLGdCQUFnQixDQUFDO1FBQ3pELENBQUMsbUJBQW1CLEVBQUUsTUFBTSxDQUFDO1FBQzdCLENBQUMsb0NBQW9DLEVBQUUsT0FBTyxDQUFDLGNBQWMsQ0FBQztRQUM5RDtZQUNDLDBCQUEwQjtZQUMxQixDQUFDLEtBQUssSUFBSSxJQUFJLENBQUMsU0FBUyxDQUN2QixjQUFjLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQztpQkFDekMsTUFBTSxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsRUFBRSxFQUFFLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDO2lCQUM3QyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsS0FBSyxDQUFDLEVBQUUsRUFBRSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQyxPQUFPLENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FDekQ7U0FDRDtRQUNEO1lBQ0MsMEJBQTBCO1lBQzFCLENBQUMsS0FBSyxJQUFJLElBQUksQ0FBQyxTQUFTLENBQUMsY0FBYyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1NBQy9EO1FBQ0Q7WUFDQyxpQ0FBaUM7WUFDakMsQ0FBQyxLQUFLLElBQUksSUFBSSxDQUFDLFNBQVMsQ0FBQyxjQUFjLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsZUFBZSxDQUFDLENBQUM7U0FDdEU7UUFDRDtZQUNDLDRCQUE0QjtZQUM1QixDQUFDLEtBQUssSUFBSSxJQUFJLENBQUMsU0FBUyxDQUFDLGNBQWMsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxxQkFBcUIsQ0FBQyxDQUFDO1NBQzVFO1FBQ0Q7WUFDQyxrQ0FBa0M7WUFDbEMsQ0FBQyxLQUFLLElBQUksSUFBSSxDQUFDLFNBQVMsQ0FBQyxjQUFjLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLENBQUM7U0FDbEU7S0FDRCxDQUFDLENBQUM7SUFFSCxJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMseUJBQXlCLEtBQUssTUFBTSxFQUFFO1FBQ3JELE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0tBQzlEO1NBQU07UUFDTixLQUFLLE1BQU0sQ0FBQyxHQUFHLEVBQUUsS0FBSyxDQUFDLElBQUksSUFBSSxFQUFFO1lBQ2hDLElBQUksS0FBSyxFQUFFO2dCQUNWLE9BQU8sQ0FBQyxHQUFHLENBQUMsbUNBQW1DLEdBQUcsSUFBSSxLQUFLLEVBQUUsQ0FBQyxDQUFDO2FBQy9EO1NBQ0Q7S0FDRDtBQUVGLENBQUMsQ0FBQztBQUVGLElBQUksT0FBTyxDQUFDLElBQUksS0FBSyxNQUFNLEVBQUU7SUFDNUIsMEJBQTBCLEVBQUUsQ0FBQztDQUM3QiJ9
|
|
@ -54,6 +54,7 @@ const setLauncherEnvironmentVars = () => {
|
|||
['VSCODE_CLI_TUNNEL_SERVICE_MUTEX', product.win32TunnelServiceMutex],
|
||||
['VSCODE_CLI_TUNNEL_CLI_MUTEX', product.win32TunnelMutex],
|
||||
['VSCODE_CLI_COMMIT', commit],
|
||||
['VSCODE_CLI_DEFAULT_PARENT_DATA_DIR', product.dataFolderName],
|
||||
[
|
||||
'VSCODE_CLI_WIN32_APP_IDS',
|
||||
!isOSS && JSON.stringify(
|
||||
|
|
|
@ -36,7 +36,7 @@ async fn main() -> Result<(), std::convert::Infallible> {
|
|||
});
|
||||
|
||||
let core = parsed.core();
|
||||
let context_paths = LauncherPaths::new(&core.global_options.cli_data_dir).unwrap();
|
||||
let context_paths = LauncherPaths::migrate(core.global_options.cli_data_dir.clone()).unwrap();
|
||||
let context_args = core.clone();
|
||||
|
||||
// gets a command context without installing the global logger
|
||||
|
|
|
@ -75,6 +75,12 @@ pub const TUNNEL_ACTIVITY_NAME: &str = concatcp!(PRODUCT_NAME_LONG, " Tunnel");
|
|||
|
||||
const NONINTERACTIVE_VAR: &str = "VSCODE_CLI_NONINTERACTIVE";
|
||||
|
||||
/// Default data CLI data directory.
|
||||
pub const DEFAULT_DATA_PARENT_DIR: &str = match option_env!("VSCODE_CLI_DEFAULT_PARENT_DATA_DIR") {
|
||||
Some(n) => n,
|
||||
None => ".vscode-oss",
|
||||
};
|
||||
|
||||
pub fn get_default_user_agent() -> String {
|
||||
format!(
|
||||
"vscode-server-launcher/{}",
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
extern crate dirs;
|
||||
|
||||
use std::{
|
||||
fs::{create_dir, read_to_string, remove_dir_all, write},
|
||||
fs::{create_dir_all, read_to_string, remove_dir_all, write},
|
||||
path::{Path, PathBuf},
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
@ -14,7 +14,7 @@ use std::{
|
|||
use serde::{de::DeserializeOwned, Serialize};
|
||||
|
||||
use crate::{
|
||||
constants::VSCODE_CLI_QUALITY,
|
||||
constants::{DEFAULT_DATA_PARENT_DIR, VSCODE_CLI_QUALITY},
|
||||
download_cache::DownloadCache,
|
||||
util::errors::{wrap, AnyError, NoHomeForLauncherError, WrappedError},
|
||||
};
|
||||
|
@ -107,8 +107,38 @@ where
|
|||
}
|
||||
|
||||
impl LauncherPaths {
|
||||
pub fn new(root: &Option<String>) -> Result<LauncherPaths, AnyError> {
|
||||
let root = root.as_deref().unwrap_or("~/.vscode-cli");
|
||||
/// todo@conno4312: temporary migration from the old CLI data directory
|
||||
pub fn migrate(root: Option<String>) -> Result<LauncherPaths, AnyError> {
|
||||
if root.is_some() {
|
||||
return Self::new(root);
|
||||
}
|
||||
|
||||
let home_dir = match dirs::home_dir() {
|
||||
None => return Self::new(root),
|
||||
Some(d) => d,
|
||||
};
|
||||
|
||||
let old_dir = home_dir.join(".vscode-cli");
|
||||
let mut new_dir = home_dir;
|
||||
new_dir.push(DEFAULT_DATA_PARENT_DIR);
|
||||
new_dir.push("cli");
|
||||
if !old_dir.exists() || new_dir.exists() {
|
||||
return Self::new_for_path(new_dir);
|
||||
}
|
||||
|
||||
if let Err(e) = std::fs::rename(&old_dir, &new_dir) {
|
||||
// no logger exists at this point in the lifecycle, so just log to stderr
|
||||
eprintln!(
|
||||
"Failed to migrate old CLI data directory, will create a new one ({})",
|
||||
e
|
||||
);
|
||||
}
|
||||
|
||||
Self::new_for_path(new_dir)
|
||||
}
|
||||
|
||||
pub fn new(root: Option<String>) -> Result<LauncherPaths, AnyError> {
|
||||
let root = root.unwrap_or_else(|| format!("~/{}/cli", DEFAULT_DATA_PARENT_DIR));
|
||||
let mut replaced = root.to_owned();
|
||||
for token in HOME_DIR_ALTS {
|
||||
if root.contains(token) {
|
||||
|
@ -120,14 +150,16 @@ impl LauncherPaths {
|
|||
}
|
||||
}
|
||||
|
||||
if !Path::new(&replaced).exists() {
|
||||
create_dir(&replaced)
|
||||
.map_err(|e| wrap(e, format!("error creating directory {}", &replaced)))?;
|
||||
Self::new_for_path(PathBuf::from(replaced))
|
||||
}
|
||||
|
||||
fn new_for_path(root: PathBuf) -> Result<LauncherPaths, AnyError> {
|
||||
if !root.exists() {
|
||||
create_dir_all(&root)
|
||||
.map_err(|e| wrap(e, format!("error creating directory {}", root.display())))?;
|
||||
}
|
||||
|
||||
Ok(LauncherPaths::new_without_replacements(PathBuf::from(
|
||||
replaced,
|
||||
)))
|
||||
Ok(LauncherPaths::new_without_replacements(root))
|
||||
}
|
||||
|
||||
pub fn new_without_replacements(root: PathBuf) -> LauncherPaths {
|
||||
|
|
|
@ -482,11 +482,10 @@
|
|||
"@types/node": "16.x"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emmetio/abbreviation": "^2.2.0",
|
||||
"@emmetio/css-parser": "ramya-rao-a/css-parser#vscode",
|
||||
"@emmetio/html-matcher": "^0.3.3",
|
||||
"@emmetio/math-expression": "^1.0.4",
|
||||
"@vscode/emmet-helper": "^2.3.0",
|
||||
"@emmetio/math-expression": "^1.0.5",
|
||||
"@vscode/emmet-helper": "^2.8.8",
|
||||
"image-size": "~1.0.0",
|
||||
"vscode-languageserver-textdocument": "^1.0.1"
|
||||
},
|
||||
|
|
|
@ -2,26 +2,19 @@
|
|||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@emmetio/abbreviation@^2.2.0":
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@emmetio/abbreviation/-/abbreviation-2.2.2.tgz#746762fd9e7a8c2ea604f580c62e3cfe250e6989"
|
||||
integrity sha512-TtE/dBnkTCct8+LntkqVrwqQao6EnPAs1YN3cUgxOxTaBlesBCY37ROUAVZrRlG64GNnVShdl/b70RfAI3w5lw==
|
||||
"@emmetio/abbreviation@^2.3.2":
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@emmetio/abbreviation/-/abbreviation-2.3.2.tgz#375bf6bc6ae6405f62dd0ddab2559b46502d01f4"
|
||||
integrity sha512-8vqkn4rtjm5Zv34RPgsq3/ij88ri+IcfC2MxPELytrQvfpaLyppscE0YSwDVuIUR6KL5GCBUfr5Mo7SHSbswpA==
|
||||
dependencies:
|
||||
"@emmetio/scanner" "^1.0.0"
|
||||
"@emmetio/scanner" "^1.0.3"
|
||||
|
||||
"@emmetio/abbreviation@^2.2.3":
|
||||
version "2.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@emmetio/abbreviation/-/abbreviation-2.2.3.tgz#2b3c0383c1a4652f677d5b56fb3f1616fe16ef10"
|
||||
integrity sha512-87pltuCPt99aL+y9xS6GPZ+Wmmyhll2WXH73gG/xpGcQ84DRnptBsI2r0BeIQ0EB/SQTOe2ANPqFqj3Rj5FOGA==
|
||||
"@emmetio/css-abbreviation@^2.1.7":
|
||||
version "2.1.7"
|
||||
resolved "https://registry.yarnpkg.com/@emmetio/css-abbreviation/-/css-abbreviation-2.1.7.tgz#9791269586d780cf4b40078ea79886d1888a188a"
|
||||
integrity sha512-nrOt3/QROjYYK1cMjoO5fCfHIf0hFpcZeQQt7Ew6ixZ0ElEEs77ijnY57HC6ti91W/mn+c1T7ET8sClBMRHHBg==
|
||||
dependencies:
|
||||
"@emmetio/scanner" "^1.0.0"
|
||||
|
||||
"@emmetio/css-abbreviation@^2.1.4":
|
||||
version "2.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@emmetio/css-abbreviation/-/css-abbreviation-2.1.4.tgz#90362e8a1122ce3b76f6c3157907d30182f53f54"
|
||||
integrity sha512-qk9L60Y+uRtM5CPbB0y+QNl/1XKE09mSO+AhhSauIfr2YOx/ta3NJw2d8RtCFxgzHeRqFRr8jgyzThbu+MZ4Uw==
|
||||
dependencies:
|
||||
"@emmetio/scanner" "^1.0.0"
|
||||
"@emmetio/scanner" "^1.0.3"
|
||||
|
||||
"@emmetio/css-parser@ramya-rao-a/css-parser#vscode":
|
||||
version "0.4.0"
|
||||
|
@ -38,17 +31,22 @@
|
|||
"@emmetio/stream-reader" "^2.0.0"
|
||||
"@emmetio/stream-reader-utils" "^0.1.0"
|
||||
|
||||
"@emmetio/math-expression@^1.0.4":
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@emmetio/math-expression/-/math-expression-1.0.4.tgz#cb657ed944f82b3728f863bf5ece1b1ff3ae7497"
|
||||
integrity sha512-1m7y8/VeXCAfgFoPGTerbqCIadApcIINujd3TaM/LRLPPKiod8aT1PPmh542spnsUSsSnZJjbuF7xiO4WFA42g==
|
||||
"@emmetio/math-expression@^1.0.5":
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@emmetio/math-expression/-/math-expression-1.0.5.tgz#d0cc52ed453a107bc9b19c5d71d1390d3aecbe48"
|
||||
integrity sha512-qf5SXD/ViS04rXSeDg9CRGM10xLC9dVaKIbMHrrwxYr5LNB/C0rOfokhGSBwnVQKcidLmdRJeNWH1V1tppZ84Q==
|
||||
dependencies:
|
||||
"@emmetio/scanner" "^1.0.0"
|
||||
"@emmetio/scanner" "^1.0.4"
|
||||
|
||||
"@emmetio/scanner@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@emmetio/scanner/-/scanner-1.0.0.tgz#065b2af6233fe7474d44823e3deb89724af42b5f"
|
||||
integrity sha512-8HqW8EVqjnCmWXVpqAOZf+EGESdkR27odcMMMGefgKXtar00SoYNSryGv//TELI4T3QFsECo78p+0lmalk/CFA==
|
||||
"@emmetio/scanner@^1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@emmetio/scanner/-/scanner-1.0.3.tgz#755e581517e2302d31a387e4064bf73035ebfc46"
|
||||
integrity sha512-/EFyTijquAwKMGSBd50RnjxsfDXmZAFp71PGu7sM6LEnEJXMV+FKL7Rvr6YLu4czQmPVRsfyhcbQz+WZnM4AZw==
|
||||
|
||||
"@emmetio/scanner@^1.0.4":
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@emmetio/scanner/-/scanner-1.0.4.tgz#e9cdc67194fd91f8b7eb141014be4f2d086c15f1"
|
||||
integrity sha512-IqRuJtQff7YHHBk4G8YZ45uB9BaAGcwQeVzgj/zj8/UdOhtQpEIupUhSk8dys6spFIWVZVeK20CzGEnqR5SbqA==
|
||||
|
||||
"@emmetio/stream-reader-utils@^0.1.0":
|
||||
version "0.1.0"
|
||||
|
@ -65,24 +63,24 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae"
|
||||
integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==
|
||||
|
||||
"@vscode/emmet-helper@^2.3.0":
|
||||
version "2.8.6"
|
||||
resolved "https://registry.yarnpkg.com/@vscode/emmet-helper/-/emmet-helper-2.8.6.tgz#ee2fa52321d6af8a40310fd9d37b8590a4dabb18"
|
||||
integrity sha512-IIB8jbiKy37zN8bAIHx59YmnIelY78CGHtThnibD/d3tQOKRY83bYVi9blwmZVUZh6l9nfkYH3tvReaiNxY9EQ==
|
||||
"@vscode/emmet-helper@^2.8.8":
|
||||
version "2.8.8"
|
||||
resolved "https://registry.yarnpkg.com/@vscode/emmet-helper/-/emmet-helper-2.8.8.tgz#df64989d2812e031cd6393ce896a2fe33ae976bd"
|
||||
integrity sha512-QuD4CmNeXSFxuP8VZwI6qL+8vmmd7JcSdwsEIdsrzb4YumWs/+4rXRX9MM+NsFfUO69g6ezngCD7XRd6jY9TQw==
|
||||
dependencies:
|
||||
emmet "^2.3.0"
|
||||
emmet "^2.4.3"
|
||||
jsonc-parser "^2.3.0"
|
||||
vscode-languageserver-textdocument "^1.0.1"
|
||||
vscode-languageserver-types "^3.15.1"
|
||||
vscode-uri "^2.1.2"
|
||||
|
||||
emmet@^2.3.0:
|
||||
version "2.3.6"
|
||||
resolved "https://registry.yarnpkg.com/emmet/-/emmet-2.3.6.tgz#1d93c1ac03164da9ddf74864c1f341ed6ff6c336"
|
||||
integrity sha512-pLS4PBPDdxuUAmw7Me7+TcHbykTsBKN/S9XJbUOMFQrNv9MoshzyMFK/R57JBm94/6HSL4vHnDeEmxlC82NQ4A==
|
||||
emmet@^2.4.3:
|
||||
version "2.4.3"
|
||||
resolved "https://registry.yarnpkg.com/emmet/-/emmet-2.4.3.tgz#c99f19e572a270da27f456dd7f65dfda83dc0ec1"
|
||||
integrity sha512-Bq6zozVDVrLbBmKdosI9Q2DvrFh/ehwnNjgDRsvGVjPOEAhMKie9HwQnPuUi3NOZ2itVGyRwsLAdufnG9DVFwg==
|
||||
dependencies:
|
||||
"@emmetio/abbreviation" "^2.2.3"
|
||||
"@emmetio/css-abbreviation" "^2.1.4"
|
||||
"@emmetio/abbreviation" "^2.3.2"
|
||||
"@emmetio/css-abbreviation" "^2.1.7"
|
||||
|
||||
image-size@~1.0.0:
|
||||
version "1.0.0"
|
||||
|
@ -114,9 +112,9 @@ vscode-languageserver-textdocument@^1.0.1:
|
|||
integrity sha512-ynEGytvgTb6HVSUwPJIAZgiHQmPCx8bZ8w5um5Lz+q5DjP0Zj8wTFhQpyg8xaMvefDytw2+HH5yzqS+FhsR28A==
|
||||
|
||||
vscode-languageserver-types@^3.15.1:
|
||||
version "3.17.2"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.2.tgz#b2c2e7de405ad3d73a883e91989b850170ffc4f2"
|
||||
integrity sha512-zHhCWatviizPIq9B7Vh9uvrH6x3sK8itC84HkamnBWoDFJtzBf7SWlpLCZUit72b3os45h6RWQNC9xHRDF8dRA==
|
||||
version "3.17.3"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.3.tgz#72d05e47b73be93acb84d6e311b5786390f13f64"
|
||||
integrity sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA==
|
||||
|
||||
vscode-uri@^2.1.2:
|
||||
version "2.1.2"
|
||||
|
|
|
@ -1681,17 +1681,17 @@
|
|||
{
|
||||
"command": "git.stageSelectedRanges",
|
||||
"group": "2_git@1",
|
||||
"when": "isInDiffRightEditor && !isInEmbeddedDiffEditor && config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/"
|
||||
"when": "isInDiffRightEditor && !isEmbeddedDiffEditor && config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/"
|
||||
},
|
||||
{
|
||||
"command": "git.unstageSelectedRanges",
|
||||
"group": "2_git@2",
|
||||
"when": "isInDiffRightEditor && !isInEmbeddedDiffEditor && config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/"
|
||||
"when": "isInDiffRightEditor && !isEmbeddedDiffEditor && config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/"
|
||||
},
|
||||
{
|
||||
"command": "git.revertSelectedRanges",
|
||||
"group": "2_git@3",
|
||||
"when": "isInDiffRightEditor && !isInEmbeddedDiffEditor && config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/"
|
||||
"when": "isInDiffRightEditor && !isEmbeddedDiffEditor && config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/"
|
||||
}
|
||||
],
|
||||
"editor/content": [
|
||||
|
@ -2073,6 +2073,12 @@
|
|||
"markdownDescription": "%config.autofetchPeriod%",
|
||||
"default": 180
|
||||
},
|
||||
"git.defaultBranchName": {
|
||||
"type": "string",
|
||||
"description": "%config.defaultBranchName%",
|
||||
"default": "main",
|
||||
"scope": "resource"
|
||||
},
|
||||
"git.branchPrefix": {
|
||||
"type": "string",
|
||||
"description": "%config.branchPrefix%",
|
||||
|
|
|
@ -131,6 +131,7 @@
|
|||
"config.checkoutType.local": "Local branches",
|
||||
"config.checkoutType.tags": "Tags",
|
||||
"config.checkoutType.remote": "Remote branches",
|
||||
"config.defaultBranchName": "The name of the default branch (ex: main, trunk, development) when initializing a new git repository. When set to empty, the default branch name configured in git will be used.",
|
||||
"config.branchPrefix": "Prefix used when creating a new branch.",
|
||||
"config.branchProtection": "List of protected branches. By default, a prompt is shown before changes are committed to a protected branch. The prompt can be controlled using the `#git.branchProtectionPrompt#` setting.",
|
||||
"config.branchProtectionPrompt": "Controls whether a prompt is being shown before changes are committed to a protected branch.",
|
||||
|
|
|
@ -307,6 +307,10 @@ function getCheckoutProcessor(repository: Repository, type: string): CheckoutPro
|
|||
return undefined;
|
||||
}
|
||||
|
||||
function sanitizeBranchName(name: string, whitespaceChar: string): string {
|
||||
return name.trim().replace(/^-+/, '').replace(/^\.|\/\.|\.\.|~|\^|:|\/$|\.lock$|\.lock\/|\\|\*|\s|^\s*$|\.$|\[|\]$/g, whitespaceChar);
|
||||
}
|
||||
|
||||
function sanitizeRemoteName(name: string) {
|
||||
name = name.trim();
|
||||
return name && name.replace(/^\.|\/\.|\.\.|~|\^|:|\/$|\.lock$|\.lock\/|\\|\*|\s|^\s*$|\.$|\[|\]$/g, '-');
|
||||
|
@ -772,7 +776,11 @@ export class CommandCenter {
|
|||
}
|
||||
}
|
||||
|
||||
await this.git.init(repositoryPath);
|
||||
const config = workspace.getConfiguration('git');
|
||||
const defaultBranchName = config.get<string>('defaultBranchName', 'main');
|
||||
const branchWhitespaceChar = config.get<string>('branchWhitespaceChar', '-');
|
||||
|
||||
await this.git.init(repositoryPath, { defaultBranch: sanitizeBranchName(defaultBranchName, branchWhitespaceChar) });
|
||||
|
||||
let message = l10n.t('Would you like to open the initialized repository?');
|
||||
const open = l10n.t('Open');
|
||||
|
@ -2179,9 +2187,6 @@ export class CommandCenter {
|
|||
const branchPrefix = config.get<string>('branchPrefix')!;
|
||||
const branchWhitespaceChar = config.get<string>('branchWhitespaceChar')!;
|
||||
const branchValidationRegex = config.get<string>('branchValidationRegex')!;
|
||||
const sanitize = (name: string) => name ?
|
||||
name.trim().replace(/^-+/, '').replace(/^\.|\/\.|\.\.|~|\^|:|\/$|\.lock$|\.lock\/|\\|\*|\s|^\s*$|\.$|\[|\]$/g, branchWhitespaceChar)
|
||||
: name;
|
||||
|
||||
let rawBranchName = defaultName;
|
||||
|
||||
|
@ -2206,7 +2211,7 @@ export class CommandCenter {
|
|||
ignoreFocusOut: true,
|
||||
validateInput: (name: string) => {
|
||||
const validateName = new RegExp(branchValidationRegex);
|
||||
const sanitizedName = sanitize(name);
|
||||
const sanitizedName = sanitizeBranchName(name, branchWhitespaceChar);
|
||||
if (validateName.test(sanitizedName)) {
|
||||
// If the sanitized name that we will use is different than what is
|
||||
// in the input box, show an info message to the user informing them
|
||||
|
@ -2224,7 +2229,7 @@ export class CommandCenter {
|
|||
});
|
||||
}
|
||||
|
||||
return sanitize(rawBranchName || '');
|
||||
return sanitizeBranchName(rawBranchName || '', branchWhitespaceChar);
|
||||
}
|
||||
|
||||
private async _branch(repository: Repository, defaultName?: string, from = false): Promise<void> {
|
||||
|
|
|
@ -401,9 +401,14 @@ export class Git {
|
|||
return new Repository(this, repository, dotGit, logger);
|
||||
}
|
||||
|
||||
async init(repository: string): Promise<void> {
|
||||
await this.exec(repository, ['init']);
|
||||
return;
|
||||
async init(repository: string, options: { defaultBranch?: string } = {}): Promise<void> {
|
||||
const args = ['init'];
|
||||
|
||||
if (options.defaultBranch && options.defaultBranch !== '') {
|
||||
args.push('-b', options.defaultBranch);
|
||||
}
|
||||
|
||||
await this.exec(repository, args);
|
||||
}
|
||||
|
||||
async clone(url: string, options: ICloneOptions, cancellationToken?: CancellationToken): Promise<string> {
|
||||
|
|
|
@ -45,8 +45,9 @@ function getImageMimeType(uri: vscode.Uri): string | undefined {
|
|||
return imageExtToMime.get(extname(uri.fsPath).toLowerCase());
|
||||
}
|
||||
|
||||
const id = 'insertAttachment';
|
||||
class CopyPasteEditProvider implements vscode.DocumentPasteEditProvider {
|
||||
class DropOrPasteEditProvider implements vscode.DocumentPasteEditProvider, vscode.DocumentDropEditProvider {
|
||||
|
||||
private readonly id = 'insertAttachment';
|
||||
|
||||
async provideDocumentPasteEdits(
|
||||
document: vscode.TextDocument,
|
||||
|
@ -59,18 +60,16 @@ class CopyPasteEditProvider implements vscode.DocumentPasteEditProvider {
|
|||
return;
|
||||
}
|
||||
|
||||
const insert = await createInsertImageAttachmentEdit(document, dataTransfer, token);
|
||||
const insert = await this.createInsertImageAttachmentEdit(document, dataTransfer, token);
|
||||
if (!insert) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pasteEdit = new vscode.DocumentPasteEdit(insert.insertText, id, vscode.l10n.t('Insert Image as Attachment'));
|
||||
const pasteEdit = new vscode.DocumentPasteEdit(insert.insertText, this.id, vscode.l10n.t('Insert Image as Attachment'));
|
||||
pasteEdit.priority = this.getPriority(dataTransfer);
|
||||
pasteEdit.additionalEdit = insert.additionalEdit;
|
||||
return pasteEdit;
|
||||
}
|
||||
}
|
||||
|
||||
class DropEditProvider implements vscode.DocumentDropEditProvider {
|
||||
|
||||
async provideDocumentDropEdits(
|
||||
document: vscode.TextDocument,
|
||||
|
@ -78,58 +77,69 @@ class DropEditProvider implements vscode.DocumentDropEditProvider {
|
|||
dataTransfer: vscode.DataTransfer,
|
||||
token: vscode.CancellationToken,
|
||||
): Promise<vscode.DocumentDropEdit | undefined> {
|
||||
const insert = await createInsertImageAttachmentEdit(document, dataTransfer, token);
|
||||
const insert = await this.createInsertImageAttachmentEdit(document, dataTransfer, token);
|
||||
if (!insert) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dropEdit = new vscode.DocumentDropEdit(insert.insertText);
|
||||
dropEdit.id = id;
|
||||
dropEdit.id = this.id;
|
||||
dropEdit.priority = this.getPriority(dataTransfer);
|
||||
dropEdit.additionalEdit = insert.additionalEdit;
|
||||
dropEdit.label = vscode.l10n.t('Insert Image as Attachment');
|
||||
return dropEdit;
|
||||
}
|
||||
}
|
||||
|
||||
async function createInsertImageAttachmentEdit(
|
||||
document: vscode.TextDocument,
|
||||
dataTransfer: vscode.DataTransfer,
|
||||
token: vscode.CancellationToken,
|
||||
): Promise<{ insertText: vscode.SnippetString; additionalEdit: vscode.WorkspaceEdit } | undefined> {
|
||||
const imageData = await getDroppedImageData(dataTransfer, token);
|
||||
if (!imageData.length || token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentCell = getCellFromCellDocument(document);
|
||||
if (!currentCell) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// create updated metadata for cell (prep for WorkspaceEdit)
|
||||
const newAttachment = buildAttachment(currentCell, imageData);
|
||||
if (!newAttachment) {
|
||||
return;
|
||||
}
|
||||
|
||||
// build edits
|
||||
const additionalEdit = new vscode.WorkspaceEdit();
|
||||
const nbEdit = vscode.NotebookEdit.updateCellMetadata(currentCell.index, newAttachment.metadata);
|
||||
const notebookUri = currentCell.notebook.uri;
|
||||
additionalEdit.set(notebookUri, [nbEdit]);
|
||||
|
||||
// create a snippet for paste
|
||||
const insertText = new vscode.SnippetString();
|
||||
newAttachment.filenames.forEach((filename, i) => {
|
||||
insertText.appendText('![');
|
||||
insertText.appendPlaceholder(`${filename}`);
|
||||
insertText.appendText(`](${/\s/.test(filename) ? `<attachment:${filename}>` : `attachment:${filename}`})`);
|
||||
if (i !== newAttachment.filenames.length - 1) {
|
||||
insertText.appendText(' ');
|
||||
private getPriority(dataTransfer: vscode.DataTransfer): number {
|
||||
if (dataTransfer.get('text/plain')) {
|
||||
// Deprioritize in favor of normal text content
|
||||
return -5;
|
||||
}
|
||||
});
|
||||
|
||||
return { insertText, additionalEdit };
|
||||
// Otherwise boost priority so attachments are preferred
|
||||
return 5;
|
||||
}
|
||||
|
||||
private async createInsertImageAttachmentEdit(
|
||||
document: vscode.TextDocument,
|
||||
dataTransfer: vscode.DataTransfer,
|
||||
token: vscode.CancellationToken,
|
||||
): Promise<{ insertText: vscode.SnippetString; additionalEdit: vscode.WorkspaceEdit } | undefined> {
|
||||
const imageData = await getDroppedImageData(dataTransfer, token);
|
||||
if (!imageData.length || token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentCell = getCellFromCellDocument(document);
|
||||
if (!currentCell) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// create updated metadata for cell (prep for WorkspaceEdit)
|
||||
const newAttachment = buildAttachment(currentCell, imageData);
|
||||
if (!newAttachment) {
|
||||
return;
|
||||
}
|
||||
|
||||
// build edits
|
||||
const additionalEdit = new vscode.WorkspaceEdit();
|
||||
const nbEdit = vscode.NotebookEdit.updateCellMetadata(currentCell.index, newAttachment.metadata);
|
||||
const notebookUri = currentCell.notebook.uri;
|
||||
additionalEdit.set(notebookUri, [nbEdit]);
|
||||
|
||||
// create a snippet for paste
|
||||
const insertText = new vscode.SnippetString();
|
||||
newAttachment.filenames.forEach((filename, i) => {
|
||||
insertText.appendText('![');
|
||||
insertText.appendPlaceholder(`${filename}`);
|
||||
insertText.appendText(`](${/\s/.test(filename) ? `<attachment:${filename}>` : `attachment:${filename}`})`);
|
||||
if (i !== newAttachment.filenames.length - 1) {
|
||||
insertText.appendText(' ');
|
||||
}
|
||||
});
|
||||
|
||||
return { insertText, additionalEdit };
|
||||
}
|
||||
}
|
||||
|
||||
async function getDroppedImageData(
|
||||
|
@ -296,14 +306,15 @@ function buildAttachment(
|
|||
}
|
||||
|
||||
export function notebookImagePasteSetup(): vscode.Disposable {
|
||||
const provider = new DropOrPasteEditProvider();
|
||||
return vscode.Disposable.from(
|
||||
vscode.languages.registerDocumentPasteEditProvider(JUPYTER_NOTEBOOK_MARKDOWN_SELECTOR, new CopyPasteEditProvider(), {
|
||||
vscode.languages.registerDocumentPasteEditProvider(JUPYTER_NOTEBOOK_MARKDOWN_SELECTOR, provider, {
|
||||
pasteMimeTypes: [
|
||||
MimeType.png,
|
||||
MimeType.uriList,
|
||||
],
|
||||
}),
|
||||
vscode.languages.registerDocumentDropEditProvider(JUPYTER_NOTEBOOK_MARKDOWN_SELECTOR, new DropEditProvider(), {
|
||||
vscode.languages.registerDocumentDropEditProvider(JUPYTER_NOTEBOOK_MARKDOWN_SELECTOR, provider, {
|
||||
dropMimeTypes: [
|
||||
...Object.values(imageExtToMime),
|
||||
MimeType.uriList,
|
||||
|
|
|
@ -32,13 +32,19 @@ class PasteEditProvider implements vscode.DocumentPasteEditProvider {
|
|||
return;
|
||||
}
|
||||
|
||||
const edit = await this._makeCreateImagePasteEdit(document, dataTransfer, token);
|
||||
if (edit) {
|
||||
return edit;
|
||||
const createEdit = await this._makeCreateImagePasteEdit(document, dataTransfer, token);
|
||||
if (createEdit) {
|
||||
return createEdit;
|
||||
}
|
||||
|
||||
const snippet = await tryGetUriListSnippet(document, dataTransfer, token);
|
||||
return snippet ? new vscode.DocumentPasteEdit(snippet.snippet, this._id, snippet.label) : undefined;
|
||||
if (!snippet) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uriEdit = new vscode.DocumentPasteEdit(snippet.snippet, this._id, snippet.label);
|
||||
uriEdit.priority = this._getPriority(dataTransfer);
|
||||
return uriEdit;
|
||||
}
|
||||
|
||||
private async _makeCreateImagePasteEdit(document: vscode.TextDocument, dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken): Promise<vscode.DocumentPasteEdit | undefined> {
|
||||
|
@ -89,10 +95,19 @@ class PasteEditProvider implements vscode.DocumentPasteEditProvider {
|
|||
return;
|
||||
}
|
||||
|
||||
const pasteEdit = new vscode.DocumentPasteEdit(snippet.snippet, '', snippet.label);
|
||||
const pasteEdit = new vscode.DocumentPasteEdit(snippet.snippet, this._id, snippet.label);
|
||||
pasteEdit.additionalEdit = workspaceEdit;
|
||||
pasteEdit.priority = this._getPriority(dataTransfer);
|
||||
return pasteEdit;
|
||||
}
|
||||
|
||||
private _getPriority(dataTransfer: vscode.DataTransfer): number {
|
||||
if (dataTransfer.get('text/plain')) {
|
||||
// Deprioritize in favor of normal text content
|
||||
return -10;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
export function registerPasteSupport(selector: vscode.DocumentSelector,) {
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
const CONTROL_CODES = '\\u0000-\\u0020\\u007f-\\u009f';
|
||||
const WEB_LINK_REGEX = new RegExp('(?:[a-zA-Z][a-zA-Z0-9+.-]{2,}:\\/\\/|data:|www\\.)[^\\s' + CONTROL_CODES + '"]{2,}[^\\s' + CONTROL_CODES + '"\')}\\],:;.!?]', 'ug');
|
||||
|
||||
const WIN_ABSOLUTE_PATH = /(?:[a-zA-Z]:(?:(?:\\|\/)[\w\.-]*)+)/;
|
||||
const WIN_RELATIVE_PATH = /(?:(?:\~|\.)(?:(?:\\|\/)[\w\.-]*)+)/;
|
||||
const WIN_ABSOLUTE_PATH = /(?<=^|\s)(?:[a-zA-Z]:(?:(?:\\|\/)[\w\.-]*)+)/;
|
||||
const WIN_RELATIVE_PATH = /(?<=^|\s)(?:(?:\~|\.)(?:(?:\\|\/)[\w\.-]*)+)/;
|
||||
const WIN_PATH = new RegExp(`(${WIN_ABSOLUTE_PATH.source}|${WIN_RELATIVE_PATH.source})`);
|
||||
const POSIX_PATH = /((?:\~|\.)?(?:\/[\w\.-]*)+)/;
|
||||
const POSIX_PATH = /(?<=^|\s)((?:\~|\.)?(?:\/[\w\.-]*)+)/;
|
||||
const LINE_COLUMN = /(?:\:([\d]+))?(?:\:([\d]+))?/;
|
||||
const isWindows = (typeof navigator !== 'undefined') ? navigator.userAgent && navigator.userAgent.indexOf('Windows') >= 0 : false;
|
||||
const PATH_LINK_REGEX = new RegExp(`${isWindows ? WIN_PATH.source : POSIX_PATH.source}${LINE_COLUMN.source}`, 'g');
|
||||
|
|
|
@ -216,7 +216,7 @@ class MoveToFileRefactorCommand implements Command {
|
|||
...destinationItems
|
||||
], {
|
||||
title: vscode.l10n.t("Move to File"),
|
||||
placeHolder: vscode.l10n.t("Enter file path"),
|
||||
placeHolder: vscode.l10n.t("Select move destination"),
|
||||
});
|
||||
if (!picked) {
|
||||
return;
|
||||
|
|
|
@ -8,14 +8,14 @@ import { Iterable } from 'vs/base/common/iterator';
|
|||
import { URI } from 'vs/base/common/uri';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
|
||||
interface IDataTransferFile {
|
||||
export interface IDataTransferFile {
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly uri?: URI;
|
||||
data(): Promise<Uint8Array>;
|
||||
}
|
||||
|
||||
export interface IDataTransferItem {
|
||||
readonly id: string;
|
||||
asString(): Thenable<string>;
|
||||
asFile(): IDataTransferFile | undefined;
|
||||
value: any;
|
||||
|
@ -23,7 +23,6 @@ export interface IDataTransferItem {
|
|||
|
||||
export function createStringDataTransferItem(stringOrPromise: string | Promise<string>): IDataTransferItem {
|
||||
return {
|
||||
id: generateUuid(),
|
||||
asString: async () => stringOrPromise,
|
||||
asFile: () => undefined,
|
||||
value: typeof stringOrPromise === 'string' ? stringOrPromise : undefined,
|
||||
|
@ -31,36 +30,26 @@ export function createStringDataTransferItem(stringOrPromise: string | Promise<s
|
|||
}
|
||||
|
||||
export function createFileDataTransferItem(fileName: string, uri: URI | undefined, data: () => Promise<Uint8Array>): IDataTransferItem {
|
||||
const file = { id: generateUuid(), name: fileName, uri, data };
|
||||
return {
|
||||
id: generateUuid(),
|
||||
asString: async () => '',
|
||||
asFile: () => ({ name: fileName, uri, data }),
|
||||
asFile: () => file,
|
||||
value: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
export class VSDataTransfer {
|
||||
|
||||
private readonly _entries = new Map<string, IDataTransferItem[]>();
|
||||
|
||||
export interface IReadonlyVSDataTransfer extends Iterable<readonly [string, IDataTransferItem]> {
|
||||
/**
|
||||
* Get the total number of entries in this data transfer.
|
||||
*/
|
||||
public get size(): number {
|
||||
let size = 0;
|
||||
this.forEach(() => size++);
|
||||
return size;
|
||||
}
|
||||
get size(): number;
|
||||
|
||||
/**
|
||||
* Check if this data transfer contains data for `mimeType`.
|
||||
*
|
||||
* This uses exact matching and does not support wildcards.
|
||||
*/
|
||||
public has(mimeType: string): boolean {
|
||||
return this._entries.has(this.toKey(mimeType));
|
||||
}
|
||||
|
||||
has(mimeType: string): boolean;
|
||||
/**
|
||||
* Check if this data transfer contains data matching `pattern`.
|
||||
*
|
||||
|
@ -68,20 +57,41 @@ export class VSDataTransfer {
|
|||
*
|
||||
* Use the special `files` mime type to match any file in the data transfer.
|
||||
*/
|
||||
matches(pattern: string): boolean;
|
||||
|
||||
/**
|
||||
* Retrieve the first entry for `mimeType`.
|
||||
*
|
||||
* Note that if you want to find all entries for a given mime type, use {@link IReadonlyVSDataTransfer.entries} instead.
|
||||
*/
|
||||
get(mimeType: string): IDataTransferItem | undefined;
|
||||
}
|
||||
|
||||
export class VSDataTransfer implements IReadonlyVSDataTransfer {
|
||||
|
||||
private readonly _entries = new Map<string, IDataTransferItem[]>();
|
||||
|
||||
public get size(): number {
|
||||
let size = 0;
|
||||
for (const _ of this._entries) {
|
||||
size++;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
public has(mimeType: string): boolean {
|
||||
return this._entries.has(this.toKey(mimeType));
|
||||
}
|
||||
|
||||
public matches(pattern: string): boolean {
|
||||
const mimes = [...this._entries.keys()];
|
||||
if (Iterable.some(this.values(), item => item.asFile())) {
|
||||
if (Iterable.some(this, ([_, item]) => item.asFile())) {
|
||||
mimes.push('files');
|
||||
}
|
||||
|
||||
return matchesMimeType_normalized(normalizeMimeType(pattern), mimes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the first entry for `mimeType`.
|
||||
*
|
||||
* Note that if want to find all entries for a given mime type, use {@link VSDataTransfer.entries} instead.
|
||||
*/
|
||||
public get(mimeType: string): IDataTransferItem | undefined {
|
||||
return this._entries.get(this.toKey(mimeType))?.[0];
|
||||
}
|
||||
|
@ -121,34 +131,14 @@ export class VSDataTransfer {
|
|||
*
|
||||
* There may be multiple entries for each mime type.
|
||||
*/
|
||||
public *entries(): Iterable<[string, IDataTransferItem]> {
|
||||
for (const [mine, items] of this._entries.entries()) {
|
||||
public *[Symbol.iterator](): IterableIterator<readonly [string, IDataTransferItem]> {
|
||||
for (const [mine, items] of this._entries) {
|
||||
for (const item of items) {
|
||||
yield [mine, item];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over all items in this data transfer.
|
||||
*
|
||||
* There may be multiple entries for each mime type.
|
||||
*/
|
||||
public values(): Iterable<IDataTransferItem> {
|
||||
return Array.from(this._entries.values()).flat();
|
||||
}
|
||||
|
||||
/**
|
||||
* Call `f` for each item and mime in the data transfer.
|
||||
*
|
||||
* There may be multiple entries for each mime type.
|
||||
*/
|
||||
public forEach(f: (value: IDataTransferItem, mime: string) => void) {
|
||||
for (const [mime, item] of this.entries()) {
|
||||
f(item, mime);
|
||||
}
|
||||
}
|
||||
|
||||
private toKey(mimeType: string): string {
|
||||
return normalizeMimeType(mimeType);
|
||||
}
|
||||
|
|
|
@ -290,6 +290,6 @@ export class BugIndicatingError extends Error {
|
|||
// Because we know for sure only buggy code throws this,
|
||||
// we definitely want to break here and fix the bug.
|
||||
// eslint-disable-next-line no-debugger
|
||||
debugger;
|
||||
// debugger;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -488,12 +488,6 @@ export interface IDiffEditorConstructionOptions extends IDiffEditorOptions {
|
|||
* Aria label for modified editor.
|
||||
*/
|
||||
modifiedAriaLabel?: string;
|
||||
|
||||
/**
|
||||
* Is the diff editor inside another editor
|
||||
* Defaults to false
|
||||
*/
|
||||
isInEmbeddedEditor?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -48,7 +48,7 @@ import { IEditorWhitespace, InlineDecoration, InlineDecorationType, IViewModel,
|
|||
import { OverviewRulerZone } from 'vs/editor/common/viewModel/overviewZoneManager';
|
||||
import * as nls from 'vs/nls';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
|
@ -59,6 +59,7 @@ import { registerIcon } from 'vs/platform/theme/common/iconRegistry';
|
|||
import { getThemeTypeSelector, IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { ThemeIcon } from 'vs/base/common/themables';
|
||||
import { MarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
|
||||
export interface IDiffCodeEditorWidgetOptions {
|
||||
originalEditor?: ICodeEditorWidgetOptions;
|
||||
|
@ -239,6 +240,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
|
|||
|
||||
private readonly _reviewPane: DiffReview;
|
||||
|
||||
private isEmbeddedDiffEditorKey: IContextKey<boolean>;
|
||||
|
||||
constructor(
|
||||
domElement: HTMLElement,
|
||||
options: Readonly<editorBrowser.IDiffEditorConstructionOptions>,
|
||||
|
@ -289,12 +292,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
|
|||
accessibilityVerbose: false
|
||||
});
|
||||
|
||||
if (typeof options.isInEmbeddedEditor !== 'undefined') {
|
||||
this._contextKeyService.createKey('isInEmbeddedDiffEditor', options.isInEmbeddedEditor);
|
||||
} else {
|
||||
this._contextKeyService.createKey('isInEmbeddedDiffEditor', false);
|
||||
}
|
||||
|
||||
this.isEmbeddedDiffEditorKey = EditorContextKeys.isEmbeddedDiffEditor.bindTo(this._contextKeyService);
|
||||
this.isEmbeddedDiffEditorKey.set(typeof options.isInEmbeddedEditor !== 'undefined' ? options.isInEmbeddedEditor : false);
|
||||
this._updateDecorationsRunner = this._register(new RunOnceScheduler(() => this._updateDecorations(), 0));
|
||||
|
||||
this._containerDomElement = document.createElement('div');
|
||||
|
@ -777,6 +776,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
|
|||
const changed = changedDiffEditorOptions(this._options, newOptions);
|
||||
this._options = newOptions;
|
||||
|
||||
this.isEmbeddedDiffEditorKey.set(typeof _newOptions.isInEmbeddedEditor !== 'undefined' ? _newOptions.isInEmbeddedEditor : false);
|
||||
|
||||
const beginUpdateDecorations = (changed.ignoreTrimWhitespace || changed.renderIndicators || changed.renderMarginRevertIcon);
|
||||
const beginUpdateDecorationsSoon = (this._isVisible && (changed.maxComputationTime || changed.maxFileSize));
|
||||
this._documentDiffProvider.setOptions(newOptions);
|
||||
|
|
|
@ -800,6 +800,11 @@ export interface IDiffEditorBaseOptions {
|
|||
* Configuration options for the diff editor.
|
||||
*/
|
||||
export interface IDiffEditorOptions extends IEditorOptions, IDiffEditorBaseOptions {
|
||||
/**
|
||||
* Is the diff editor inside another editor
|
||||
* Defaults to false
|
||||
*/
|
||||
isInEmbeddedEditor?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -26,6 +26,7 @@ export namespace EditorContextKeys {
|
|||
|
||||
export const readOnly = new RawContextKey<boolean>('editorReadonly', false, nls.localize('editorReadonly', "Whether the editor is read only"));
|
||||
export const inDiffEditor = new RawContextKey<boolean>('inDiffEditor', false, nls.localize('inDiffEditor', "Whether the context is a diff editor"));
|
||||
export const isEmbeddedDiffEditor = new RawContextKey<boolean>('isEmbeddedDiffEditor', false, nls.localize('isEmbeddedDiffEditor', "Whether the context is an embedded diff editor"));
|
||||
export const columnSelection = new RawContextKey<boolean>('editorColumnSelection', false, nls.localize('editorColumnSelection', "Whether `editor.columnSelection` is enabled"));
|
||||
export const writable = readOnly.toNegated();
|
||||
export const hasNonEmptySelection = new RawContextKey<boolean>('editorHasSelection', false, nls.localize('editorHasSelection', "Whether the editor has text selected"));
|
||||
|
|
|
@ -7,7 +7,7 @@ import { VSBuffer } from 'vs/base/common/buffer';
|
|||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { VSDataTransfer } from 'vs/base/common/dataTransfer';
|
||||
import { IReadonlyVSDataTransfer } from 'vs/base/common/dataTransfer';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IMarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
|
@ -786,6 +786,7 @@ export interface DocumentPasteEdit {
|
|||
readonly id: string;
|
||||
readonly label: string;
|
||||
readonly detail: string;
|
||||
readonly priority: number;
|
||||
insertText: string | { readonly snippet: string };
|
||||
additionalEdit?: WorkspaceEdit;
|
||||
}
|
||||
|
@ -800,9 +801,9 @@ export interface DocumentPasteEditProvider {
|
|||
readonly copyMimeTypes?: readonly string[];
|
||||
readonly pasteMimeTypes: readonly string[];
|
||||
|
||||
prepareDocumentPaste?(model: model.ITextModel, ranges: readonly IRange[], dataTransfer: VSDataTransfer, token: CancellationToken): Promise<undefined | VSDataTransfer>;
|
||||
prepareDocumentPaste?(model: model.ITextModel, ranges: readonly IRange[], dataTransfer: IReadonlyVSDataTransfer, token: CancellationToken): Promise<undefined | IReadonlyVSDataTransfer>;
|
||||
|
||||
provideDocumentPasteEdits(model: model.ITextModel, ranges: readonly IRange[], dataTransfer: VSDataTransfer, token: CancellationToken): Promise<DocumentPasteEdit | undefined>;
|
||||
provideDocumentPasteEdits(model: model.ITextModel, ranges: readonly IRange[], dataTransfer: IReadonlyVSDataTransfer, token: CancellationToken): Promise<DocumentPasteEdit | undefined>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1948,6 +1949,7 @@ export enum ExternalUriOpenerPriority {
|
|||
export interface DocumentOnDropEdit {
|
||||
readonly id: string;
|
||||
readonly label: string;
|
||||
readonly priority: number;
|
||||
insertText: string | { readonly snippet: string };
|
||||
additionalEdit?: WorkspaceEdit;
|
||||
}
|
||||
|
@ -1958,5 +1960,5 @@ export interface DocumentOnDropEdit {
|
|||
export interface DocumentOnDropEditProvider {
|
||||
readonly dropMimeTypes?: readonly string[];
|
||||
|
||||
provideDocumentOnDropEdits(model: model.ITextModel, position: IPosition, dataTransfer: VSDataTransfer, token: CancellationToken): ProviderResult<DocumentOnDropEdit>;
|
||||
provideDocumentOnDropEdits(model: model.ITextModel, position: IPosition, dataTransfer: IReadonlyVSDataTransfer, token: CancellationToken): ProviderResult<DocumentOnDropEdit>;
|
||||
}
|
||||
|
|
|
@ -165,14 +165,18 @@ export class CopyPasteController extends Disposable implements IEditorContributi
|
|||
});
|
||||
|
||||
const promise = createCancelablePromise(async token => {
|
||||
const results = await Promise.all(providers.map(provider => {
|
||||
const results = coalesce(await Promise.all(providers.map(provider => {
|
||||
return provider.prepareDocumentPaste!(model, ranges, dataTransfer, token);
|
||||
}));
|
||||
})));
|
||||
|
||||
// Values from higher priority providers should overwrite values from lower priority ones.
|
||||
// Reverse the array to so that the calls to `replace` below will do this
|
||||
results.reverse();
|
||||
|
||||
for (const result of results) {
|
||||
result?.forEach((value, key) => {
|
||||
dataTransfer.replace(key, value);
|
||||
});
|
||||
for (const [mime, value] of result) {
|
||||
dataTransfer.replace(mime, value);
|
||||
}
|
||||
}
|
||||
|
||||
return dataTransfer;
|
||||
|
@ -368,9 +372,9 @@ export class CopyPasteController extends Disposable implements IEditorContributi
|
|||
return;
|
||||
}
|
||||
|
||||
toMergeDataTransfer.forEach((value, key) => {
|
||||
for (const [key, value] of toMergeDataTransfer) {
|
||||
dataTransfer.replace(key, value);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!dataTransfer.has(Mimes.uriList)) {
|
||||
|
@ -391,6 +395,7 @@ export class CopyPasteController extends Disposable implements IEditorContributi
|
|||
providers.map(provider => provider.provideDocumentPasteEdits(model, selections, dataTransfer, token))
|
||||
).then(coalesce),
|
||||
token);
|
||||
result?.sort((a, b) => b.priority - a.priority);
|
||||
return result ?? [];
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import { coalesce } from 'vs/base/common/arrays';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { UriList, VSDataTransfer } from 'vs/base/common/dataTransfer';
|
||||
import { IReadonlyVSDataTransfer, UriList } from 'vs/base/common/dataTransfer';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Mimes } from 'vs/base/common/mime';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
|
@ -27,17 +27,17 @@ abstract class SimplePasteAndDropProvider implements DocumentOnDropEditProvider,
|
|||
abstract readonly dropMimeTypes: readonly string[] | undefined;
|
||||
abstract readonly pasteMimeTypes: readonly string[];
|
||||
|
||||
async provideDocumentPasteEdits(_model: ITextModel, _ranges: readonly IRange[], dataTransfer: VSDataTransfer, token: CancellationToken): Promise<DocumentPasteEdit | undefined> {
|
||||
async provideDocumentPasteEdits(_model: ITextModel, _ranges: readonly IRange[], dataTransfer: IReadonlyVSDataTransfer, token: CancellationToken): Promise<DocumentPasteEdit | undefined> {
|
||||
const edit = await this.getEdit(dataTransfer, token);
|
||||
return edit ? { id: this.id, insertText: edit.insertText, label: edit.label, detail: edit.detail } : undefined;
|
||||
return edit ? { id: this.id, insertText: edit.insertText, label: edit.label, detail: edit.detail, priority: edit.priority } : undefined;
|
||||
}
|
||||
|
||||
async provideDocumentOnDropEdits(_model: ITextModel, _position: IPosition, dataTransfer: VSDataTransfer, token: CancellationToken): Promise<DocumentOnDropEdit | undefined> {
|
||||
async provideDocumentOnDropEdits(_model: ITextModel, _position: IPosition, dataTransfer: IReadonlyVSDataTransfer, token: CancellationToken): Promise<DocumentOnDropEdit | undefined> {
|
||||
const edit = await this.getEdit(dataTransfer, token);
|
||||
return edit ? { id: this.id, insertText: edit.insertText, label: edit.label } : undefined;
|
||||
return edit ? { id: this.id, insertText: edit.insertText, label: edit.label, priority: edit.priority } : undefined;
|
||||
}
|
||||
|
||||
protected abstract getEdit(dataTransfer: VSDataTransfer, token: CancellationToken): Promise<DocumentPasteEdit | undefined>;
|
||||
protected abstract getEdit(dataTransfer: IReadonlyVSDataTransfer, token: CancellationToken): Promise<DocumentPasteEdit | undefined>;
|
||||
}
|
||||
|
||||
class DefaultTextProvider extends SimplePasteAndDropProvider {
|
||||
|
@ -46,7 +46,7 @@ class DefaultTextProvider extends SimplePasteAndDropProvider {
|
|||
readonly dropMimeTypes = [Mimes.text];
|
||||
readonly pasteMimeTypes = [Mimes.text];
|
||||
|
||||
protected async getEdit(dataTransfer: VSDataTransfer, _token: CancellationToken) {
|
||||
protected async getEdit(dataTransfer: IReadonlyVSDataTransfer, _token: CancellationToken) {
|
||||
const textEntry = dataTransfer.get(Mimes.text);
|
||||
if (!textEntry) {
|
||||
return;
|
||||
|
@ -61,6 +61,7 @@ class DefaultTextProvider extends SimplePasteAndDropProvider {
|
|||
const insertText = await textEntry.asString();
|
||||
return {
|
||||
id: this.id,
|
||||
priority: 0,
|
||||
label: localize('text.label', "Insert Plain Text"),
|
||||
detail: builtInLabel,
|
||||
insertText
|
||||
|
@ -74,7 +75,7 @@ class PathProvider extends SimplePasteAndDropProvider {
|
|||
readonly dropMimeTypes = [Mimes.uriList];
|
||||
readonly pasteMimeTypes = [Mimes.uriList];
|
||||
|
||||
protected async getEdit(dataTransfer: VSDataTransfer, token: CancellationToken) {
|
||||
protected async getEdit(dataTransfer: IReadonlyVSDataTransfer, token: CancellationToken) {
|
||||
const entries = await extractUriList(dataTransfer);
|
||||
if (!entries.length || token.isCancellationRequested) {
|
||||
return;
|
||||
|
@ -107,6 +108,7 @@ class PathProvider extends SimplePasteAndDropProvider {
|
|||
|
||||
return {
|
||||
id: this.id,
|
||||
priority: 0,
|
||||
insertText,
|
||||
label,
|
||||
detail: builtInLabel,
|
||||
|
@ -126,7 +128,7 @@ class RelativePathProvider extends SimplePasteAndDropProvider {
|
|||
super();
|
||||
}
|
||||
|
||||
protected async getEdit(dataTransfer: VSDataTransfer, token: CancellationToken) {
|
||||
protected async getEdit(dataTransfer: IReadonlyVSDataTransfer, token: CancellationToken) {
|
||||
const entries = await extractUriList(dataTransfer);
|
||||
if (!entries.length || token.isCancellationRequested) {
|
||||
return;
|
||||
|
@ -143,6 +145,7 @@ class RelativePathProvider extends SimplePasteAndDropProvider {
|
|||
|
||||
return {
|
||||
id: this.id,
|
||||
priority: 0,
|
||||
insertText: relativeUris.join(' '),
|
||||
label: entries.length > 1
|
||||
? localize('defaultDropProvider.uriList.relativePaths', "Insert Relative Paths")
|
||||
|
@ -152,7 +155,7 @@ class RelativePathProvider extends SimplePasteAndDropProvider {
|
|||
}
|
||||
}
|
||||
|
||||
async function extractUriList(dataTransfer: VSDataTransfer): Promise<{ readonly uri: URI; readonly originalText: string }[]> {
|
||||
async function extractUriList(dataTransfer: IReadonlyVSDataTransfer): Promise<{ readonly uri: URI; readonly originalText: string }[]> {
|
||||
const urlListEntry = dataTransfer.get(Mimes.uriList);
|
||||
if (!urlListEntry) {
|
||||
return [];
|
||||
|
|
|
@ -13,6 +13,8 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
|||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||
import { DocumentOnDropEditProvider } from 'vs/editor/common/languages';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
|
||||
import { DraggedTreeItemsIdentifier } from 'vs/editor/common/services/treeViewsDnd';
|
||||
import { ITreeViewsDnDService } from 'vs/editor/common/services/treeViewsDndService';
|
||||
|
@ -99,19 +101,15 @@ export class DropIntoEditorController extends Disposable implements IEditorContr
|
|||
return provider.dropMimeTypes.some(mime => ourDataTransfer.matches(mime));
|
||||
});
|
||||
|
||||
const possibleDropEdits = await raceCancellation(Promise.all(providers.map(provider => {
|
||||
return provider.provideDocumentOnDropEdits(model, position, ourDataTransfer, tokenSource.token);
|
||||
})), tokenSource.token);
|
||||
const edits = await this.getDropEdits(providers, model, position, ourDataTransfer, tokenSource);
|
||||
if (tokenSource.token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (possibleDropEdits) {
|
||||
const allEdits = coalesce(possibleDropEdits);
|
||||
// Pass in the parent token here as it tracks cancelling the entire drop operation.
|
||||
|
||||
if (edits.length) {
|
||||
const canShowWidget = editor.getOption(EditorOption.dropIntoEditor).showDropSelector === 'afterDrop';
|
||||
await this._postDropWidgetManager.applyEditAndShowIfNeeded([Range.fromPositions(position)], { activeEditIndex: 0, allEdits }, canShowWidget, token);
|
||||
// Pass in the parent token here as it tracks cancelling the entire drop operation
|
||||
await this._postDropWidgetManager.applyEditAndShowIfNeeded([Range.fromPositions(position)], { activeEditIndex: 0, allEdits: edits }, canShowWidget, token);
|
||||
}
|
||||
} finally {
|
||||
tokenSource.dispose();
|
||||
|
@ -125,6 +123,15 @@ export class DropIntoEditorController extends Disposable implements IEditorContr
|
|||
this._currentOperation = p;
|
||||
}
|
||||
|
||||
private async getDropEdits(providers: DocumentOnDropEditProvider[], model: ITextModel, position: IPosition, dataTransfer: VSDataTransfer, tokenSource: EditorStateCancellationTokenSource) {
|
||||
const results = await raceCancellation(Promise.all(providers.map(provider => {
|
||||
return provider.provideDocumentOnDropEdits(model, position, dataTransfer, tokenSource.token);
|
||||
})), tokenSource.token);
|
||||
const edits = coalesce(results ?? []);
|
||||
edits.sort((a, b) => b.priority - a.priority);
|
||||
return edits;
|
||||
}
|
||||
|
||||
private async extractDataTransferData(dragEvent: DragEvent): Promise<VSDataTransfer> {
|
||||
if (!dragEvent.dataTransfer) {
|
||||
return new VSDataTransfer();
|
||||
|
@ -138,7 +145,7 @@ export class DropIntoEditorController extends Disposable implements IEditorContr
|
|||
for (const id of data) {
|
||||
const treeDataTransfer = await this._treeViewsDragAndDropService.removeDragOperationTransfer(id.identifier);
|
||||
if (treeDataTransfer) {
|
||||
for (const [type, value] of treeDataTransfer.entries()) {
|
||||
for (const [type, value] of treeDataTransfer) {
|
||||
dataTransfer.replace(type, value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import { ILanguageConfigurationService } from 'vs/editor/common/languages/langua
|
|||
import { ILanguageFeatureDebounceService, LanguageFeatureDebounceService } from 'vs/editor/common/services/languageFeatureDebounce';
|
||||
import { TestLanguageConfigurationService } from 'vs/editor/test/common/modes/testLanguageConfigurationService';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler';
|
||||
|
||||
suite('Sticky Scroll Tests', () => {
|
||||
|
||||
|
@ -110,120 +111,124 @@ suite('Sticky Scroll Tests', () => {
|
|||
};
|
||||
}
|
||||
|
||||
test('Testing the function getCandidateStickyLinesIntersecting', async () => {
|
||||
const model = createTextModel(text);
|
||||
await withAsyncTestCodeEditor(model, {
|
||||
stickyScroll: {
|
||||
enabled: true,
|
||||
maxLineCount: 5,
|
||||
defaultModel: 'outlineModel'
|
||||
}, serviceCollection: serviceCollection
|
||||
}, async (editor, _viewModel, instantiationService) => {
|
||||
const languageService = instantiationService.get(ILanguageFeaturesService);
|
||||
const languageConfigurationService = instantiationService.get(ILanguageConfigurationService);
|
||||
languageService.documentSymbolProvider.register('*', documentSymbolProviderForTestModel());
|
||||
const provider: StickyLineCandidateProvider = new StickyLineCandidateProvider(editor, languageService, languageConfigurationService);
|
||||
await provider.update();
|
||||
assert.deepStrictEqual(provider.getCandidateStickyLinesIntersecting({ startLineNumber: 1, endLineNumber: 4 }), [new StickyLineCandidate(1, 2, 1)]);
|
||||
assert.deepStrictEqual(provider.getCandidateStickyLinesIntersecting({ startLineNumber: 8, endLineNumber: 10 }), [new StickyLineCandidate(7, 11, 1), new StickyLineCandidate(9, 11, 2), new StickyLineCandidate(10, 10, 3)]);
|
||||
assert.deepStrictEqual(provider.getCandidateStickyLinesIntersecting({ startLineNumber: 10, endLineNumber: 13 }), [new StickyLineCandidate(7, 11, 1), new StickyLineCandidate(9, 11, 2), new StickyLineCandidate(10, 10, 3)]);
|
||||
test('Testing the function getCandidateStickyLinesIntersecting', () => {
|
||||
return runWithFakedTimers({ useFakeTimers: true }, async () => {
|
||||
const model = createTextModel(text);
|
||||
await withAsyncTestCodeEditor(model, {
|
||||
stickyScroll: {
|
||||
enabled: true,
|
||||
maxLineCount: 5,
|
||||
defaultModel: 'outlineModel'
|
||||
}, serviceCollection: serviceCollection
|
||||
}, async (editor, _viewModel, instantiationService) => {
|
||||
const languageService = instantiationService.get(ILanguageFeaturesService);
|
||||
const languageConfigurationService = instantiationService.get(ILanguageConfigurationService);
|
||||
languageService.documentSymbolProvider.register('*', documentSymbolProviderForTestModel());
|
||||
const provider: StickyLineCandidateProvider = new StickyLineCandidateProvider(editor, languageService, languageConfigurationService);
|
||||
await provider.update();
|
||||
assert.deepStrictEqual(provider.getCandidateStickyLinesIntersecting({ startLineNumber: 1, endLineNumber: 4 }), [new StickyLineCandidate(1, 2, 1)]);
|
||||
assert.deepStrictEqual(provider.getCandidateStickyLinesIntersecting({ startLineNumber: 8, endLineNumber: 10 }), [new StickyLineCandidate(7, 11, 1), new StickyLineCandidate(9, 11, 2), new StickyLineCandidate(10, 10, 3)]);
|
||||
assert.deepStrictEqual(provider.getCandidateStickyLinesIntersecting({ startLineNumber: 10, endLineNumber: 13 }), [new StickyLineCandidate(7, 11, 1), new StickyLineCandidate(9, 11, 2), new StickyLineCandidate(10, 10, 3)]);
|
||||
|
||||
provider.dispose();
|
||||
model.dispose();
|
||||
provider.dispose();
|
||||
model.dispose();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('issue #157180: Render the correct line corresponding to the scope definition', async () => {
|
||||
test('issue #157180: Render the correct line corresponding to the scope definition', () => {
|
||||
return runWithFakedTimers({ useFakeTimers: true }, async () => {
|
||||
const model = createTextModel(text);
|
||||
await withAsyncTestCodeEditor(model, {
|
||||
stickyScroll: {
|
||||
enabled: true,
|
||||
maxLineCount: 5,
|
||||
defaultModel: 'outlineModel'
|
||||
}, serviceCollection
|
||||
}, async (editor, _viewModel, instantiationService) => {
|
||||
|
||||
const model = createTextModel(text);
|
||||
await withAsyncTestCodeEditor(model, {
|
||||
stickyScroll: {
|
||||
enabled: true,
|
||||
maxLineCount: 5,
|
||||
defaultModel: 'outlineModel'
|
||||
}, serviceCollection
|
||||
}, async (editor, _viewModel, instantiationService) => {
|
||||
const stickyScrollController: StickyScrollController = editor.registerAndInstantiateContribution(StickyScrollController.ID, StickyScrollController);
|
||||
const lineHeight: number = editor.getOption(EditorOption.lineHeight);
|
||||
const languageService: ILanguageFeaturesService = instantiationService.get(ILanguageFeaturesService);
|
||||
languageService.documentSymbolProvider.register('*', documentSymbolProviderForTestModel());
|
||||
await stickyScrollController.stickyScrollCandidateProvider.update();
|
||||
let state;
|
||||
|
||||
const stickyScrollController: StickyScrollController = editor.registerAndInstantiateContribution(StickyScrollController.ID, StickyScrollController);
|
||||
const lineHeight: number = editor.getOption(EditorOption.lineHeight);
|
||||
const languageService: ILanguageFeaturesService = instantiationService.get(ILanguageFeaturesService);
|
||||
languageService.documentSymbolProvider.register('*', documentSymbolProviderForTestModel());
|
||||
await stickyScrollController.stickyScrollCandidateProvider.update();
|
||||
let state;
|
||||
editor.setScrollTop(1);
|
||||
state = stickyScrollController.findScrollWidgetState();
|
||||
assert.deepStrictEqual(state.lineNumbers, [1]);
|
||||
|
||||
editor.setScrollTop(1);
|
||||
state = stickyScrollController.findScrollWidgetState();
|
||||
assert.deepStrictEqual(state.lineNumbers, [1]);
|
||||
editor.setScrollTop(lineHeight + 1);
|
||||
state = stickyScrollController.findScrollWidgetState();
|
||||
assert.deepStrictEqual(state.lineNumbers, [1]);
|
||||
|
||||
editor.setScrollTop(lineHeight + 1);
|
||||
state = stickyScrollController.findScrollWidgetState();
|
||||
assert.deepStrictEqual(state.lineNumbers, [1]);
|
||||
editor.setScrollTop(4 * lineHeight + 1);
|
||||
state = stickyScrollController.findScrollWidgetState();
|
||||
assert.deepStrictEqual(state.lineNumbers, []);
|
||||
|
||||
editor.setScrollTop(4 * lineHeight + 1);
|
||||
state = stickyScrollController.findScrollWidgetState();
|
||||
assert.deepStrictEqual(state.lineNumbers, []);
|
||||
editor.setScrollTop(8 * lineHeight + 1);
|
||||
state = stickyScrollController.findScrollWidgetState();
|
||||
assert.deepStrictEqual(state.lineNumbers, [7, 9]);
|
||||
|
||||
editor.setScrollTop(8 * lineHeight + 1);
|
||||
state = stickyScrollController.findScrollWidgetState();
|
||||
assert.deepStrictEqual(state.lineNumbers, [7, 9]);
|
||||
editor.setScrollTop(9 * lineHeight + 1);
|
||||
state = stickyScrollController.findScrollWidgetState();
|
||||
assert.deepStrictEqual(state.lineNumbers, [7, 9]);
|
||||
|
||||
editor.setScrollTop(9 * lineHeight + 1);
|
||||
state = stickyScrollController.findScrollWidgetState();
|
||||
assert.deepStrictEqual(state.lineNumbers, [7, 9]);
|
||||
editor.setScrollTop(10 * lineHeight + 1);
|
||||
state = stickyScrollController.findScrollWidgetState();
|
||||
assert.deepStrictEqual(state.lineNumbers, [7]);
|
||||
|
||||
editor.setScrollTop(10 * lineHeight + 1);
|
||||
state = stickyScrollController.findScrollWidgetState();
|
||||
assert.deepStrictEqual(state.lineNumbers, [7]);
|
||||
|
||||
stickyScrollController.dispose();
|
||||
stickyScrollController.stickyScrollCandidateProvider.dispose();
|
||||
model.dispose();
|
||||
stickyScrollController.dispose();
|
||||
stickyScrollController.stickyScrollCandidateProvider.dispose();
|
||||
model.dispose();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('issue #156268 : Do not reveal sticky lines when they are in a folded region ', async () => {
|
||||
test('issue #156268 : Do not reveal sticky lines when they are in a folded region ', () => {
|
||||
return runWithFakedTimers({ useFakeTimers: true }, async () => {
|
||||
const model = createTextModel(text);
|
||||
await withAsyncTestCodeEditor(model, {
|
||||
stickyScroll: {
|
||||
enabled: true,
|
||||
maxLineCount: 5,
|
||||
defaultModel: 'outlineModel'
|
||||
}, serviceCollection
|
||||
}, async (editor, viewModel, instantiationService) => {
|
||||
|
||||
const model = createTextModel(text);
|
||||
await withAsyncTestCodeEditor(model, {
|
||||
stickyScroll: {
|
||||
enabled: true,
|
||||
maxLineCount: 5,
|
||||
defaultModel: 'outlineModel'
|
||||
}, serviceCollection
|
||||
}, async (editor, viewModel, instantiationService) => {
|
||||
const stickyScrollController: StickyScrollController = editor.registerAndInstantiateContribution(StickyScrollController.ID, StickyScrollController);
|
||||
const lineHeight = editor.getOption(EditorOption.lineHeight);
|
||||
|
||||
const stickyScrollController: StickyScrollController = editor.registerAndInstantiateContribution(StickyScrollController.ID, StickyScrollController);
|
||||
const lineHeight = editor.getOption(EditorOption.lineHeight);
|
||||
const languageService = instantiationService.get(ILanguageFeaturesService);
|
||||
languageService.documentSymbolProvider.register('*', documentSymbolProviderForTestModel());
|
||||
await stickyScrollController.stickyScrollCandidateProvider.update();
|
||||
editor.setHiddenAreas([{ startLineNumber: 2, endLineNumber: 2, startColumn: 1, endColumn: 1 }, { startLineNumber: 10, endLineNumber: 11, startColumn: 1, endColumn: 1 }]);
|
||||
let state;
|
||||
|
||||
const languageService = instantiationService.get(ILanguageFeaturesService);
|
||||
languageService.documentSymbolProvider.register('*', documentSymbolProviderForTestModel());
|
||||
await stickyScrollController.stickyScrollCandidateProvider.update();
|
||||
editor.setHiddenAreas([{ startLineNumber: 2, endLineNumber: 2, startColumn: 1, endColumn: 1 }, { startLineNumber: 10, endLineNumber: 11, startColumn: 1, endColumn: 1 }]);
|
||||
let state;
|
||||
editor.setScrollTop(1);
|
||||
state = stickyScrollController.findScrollWidgetState();
|
||||
assert.deepStrictEqual(state.lineNumbers, [1]);
|
||||
|
||||
editor.setScrollTop(1);
|
||||
state = stickyScrollController.findScrollWidgetState();
|
||||
assert.deepStrictEqual(state.lineNumbers, [1]);
|
||||
editor.setScrollTop(lineHeight + 1);
|
||||
state = stickyScrollController.findScrollWidgetState();
|
||||
assert.deepStrictEqual(state.lineNumbers, []);
|
||||
|
||||
editor.setScrollTop(lineHeight + 1);
|
||||
state = stickyScrollController.findScrollWidgetState();
|
||||
assert.deepStrictEqual(state.lineNumbers, []);
|
||||
editor.setScrollTop(6 * lineHeight + 1);
|
||||
state = stickyScrollController.findScrollWidgetState();
|
||||
assert.deepStrictEqual(state.lineNumbers, [7, 9]);
|
||||
|
||||
editor.setScrollTop(6 * lineHeight + 1);
|
||||
state = stickyScrollController.findScrollWidgetState();
|
||||
assert.deepStrictEqual(state.lineNumbers, [7, 9]);
|
||||
editor.setScrollTop(7 * lineHeight + 1);
|
||||
state = stickyScrollController.findScrollWidgetState();
|
||||
assert.deepStrictEqual(state.lineNumbers, [7]);
|
||||
|
||||
editor.setScrollTop(7 * lineHeight + 1);
|
||||
state = stickyScrollController.findScrollWidgetState();
|
||||
assert.deepStrictEqual(state.lineNumbers, [7]);
|
||||
editor.setScrollTop(10 * lineHeight + 1);
|
||||
state = stickyScrollController.findScrollWidgetState();
|
||||
assert.deepStrictEqual(state.lineNumbers, []);
|
||||
|
||||
editor.setScrollTop(10 * lineHeight + 1);
|
||||
state = stickyScrollController.findScrollWidgetState();
|
||||
assert.deepStrictEqual(state.lineNumbers, []);
|
||||
|
||||
stickyScrollController.dispose();
|
||||
stickyScrollController.stickyScrollCandidateProvider.dispose();
|
||||
model.dispose();
|
||||
stickyScrollController.dispose();
|
||||
stickyScrollController.stickyScrollCandidateProvider.dispose();
|
||||
model.dispose();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -274,49 +279,50 @@ suite('Sticky Scroll Tests', () => {
|
|||
};
|
||||
}
|
||||
|
||||
test('issue #159271 : render the correct widget state when the child scope starts on the same line as the parent scope', async () => {
|
||||
test('issue #159271 : render the correct widget state when the child scope starts on the same line as the parent scope', () => {
|
||||
return runWithFakedTimers({ useFakeTimers: true }, async () => {
|
||||
const model = createTextModel(textWithScopesWithSameStartingLines);
|
||||
await withAsyncTestCodeEditor(model, {
|
||||
stickyScroll: {
|
||||
enabled: true,
|
||||
maxLineCount: 5,
|
||||
defaultModel: 'outlineModel'
|
||||
}, serviceCollection
|
||||
}, async (editor, _viewModel, instantiationService) => {
|
||||
|
||||
const model = createTextModel(textWithScopesWithSameStartingLines);
|
||||
await withAsyncTestCodeEditor(model, {
|
||||
stickyScroll: {
|
||||
enabled: true,
|
||||
maxLineCount: 5,
|
||||
defaultModel: 'outlineModel'
|
||||
}, serviceCollection
|
||||
}, async (editor, _viewModel, instantiationService) => {
|
||||
const stickyScrollController: StickyScrollController = editor.registerAndInstantiateContribution(StickyScrollController.ID, StickyScrollController);
|
||||
await stickyScrollController.stickyScrollCandidateProvider.update();
|
||||
const lineHeight = editor.getOption(EditorOption.lineHeight);
|
||||
|
||||
const stickyScrollController: StickyScrollController = editor.registerAndInstantiateContribution(StickyScrollController.ID, StickyScrollController);
|
||||
await stickyScrollController.stickyScrollCandidateProvider.update();
|
||||
const lineHeight = editor.getOption(EditorOption.lineHeight);
|
||||
const languageService = instantiationService.get(ILanguageFeaturesService);
|
||||
languageService.documentSymbolProvider.register('*', documentSymbolProviderForSecondTestModel());
|
||||
await stickyScrollController.stickyScrollCandidateProvider.update();
|
||||
let state;
|
||||
|
||||
const languageService = instantiationService.get(ILanguageFeaturesService);
|
||||
languageService.documentSymbolProvider.register('*', documentSymbolProviderForSecondTestModel());
|
||||
await stickyScrollController.stickyScrollCandidateProvider.update();
|
||||
let state;
|
||||
editor.setScrollTop(1);
|
||||
state = stickyScrollController.findScrollWidgetState();
|
||||
assert.deepStrictEqual(state.lineNumbers, [1, 2]);
|
||||
|
||||
editor.setScrollTop(1);
|
||||
state = stickyScrollController.findScrollWidgetState();
|
||||
assert.deepStrictEqual(state.lineNumbers, [1, 2]);
|
||||
editor.setScrollTop(lineHeight + 1);
|
||||
state = stickyScrollController.findScrollWidgetState();
|
||||
assert.deepStrictEqual(state.lineNumbers, [1, 2]);
|
||||
|
||||
editor.setScrollTop(lineHeight + 1);
|
||||
state = stickyScrollController.findScrollWidgetState();
|
||||
assert.deepStrictEqual(state.lineNumbers, [1, 2]);
|
||||
editor.setScrollTop(2 * lineHeight + 1);
|
||||
state = stickyScrollController.findScrollWidgetState();
|
||||
assert.deepStrictEqual(state.lineNumbers, [1]);
|
||||
|
||||
editor.setScrollTop(2 * lineHeight + 1);
|
||||
state = stickyScrollController.findScrollWidgetState();
|
||||
assert.deepStrictEqual(state.lineNumbers, [1]);
|
||||
editor.setScrollTop(3 * lineHeight + 1);
|
||||
state = stickyScrollController.findScrollWidgetState();
|
||||
assert.deepStrictEqual(state.lineNumbers, [1]);
|
||||
|
||||
editor.setScrollTop(3 * lineHeight + 1);
|
||||
state = stickyScrollController.findScrollWidgetState();
|
||||
assert.deepStrictEqual(state.lineNumbers, [1]);
|
||||
editor.setScrollTop(4 * lineHeight + 1);
|
||||
state = stickyScrollController.findScrollWidgetState();
|
||||
assert.deepStrictEqual(state.lineNumbers, []);
|
||||
|
||||
editor.setScrollTop(4 * lineHeight + 1);
|
||||
state = stickyScrollController.findScrollWidgetState();
|
||||
assert.deepStrictEqual(state.lineNumbers, []);
|
||||
|
||||
stickyScrollController.dispose();
|
||||
stickyScrollController.stickyScrollCandidateProvider.dispose();
|
||||
model.dispose();
|
||||
stickyScrollController.dispose();
|
||||
stickyScrollController.stickyScrollCandidateProvider.dispose();
|
||||
model.dispose();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
10
src/vs/monaco.d.ts
vendored
10
src/vs/monaco.d.ts
vendored
|
@ -3866,6 +3866,11 @@ declare namespace monaco.editor {
|
|||
* Configuration options for the diff editor.
|
||||
*/
|
||||
export interface IDiffEditorOptions extends IEditorOptions, IDiffEditorBaseOptions {
|
||||
/**
|
||||
* Is the diff editor inside another editor
|
||||
* Defaults to false
|
||||
*/
|
||||
isInEmbeddedEditor?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5502,11 +5507,6 @@ declare namespace monaco.editor {
|
|||
* Aria label for modified editor.
|
||||
*/
|
||||
modifiedAriaLabel?: string;
|
||||
/**
|
||||
* Is the diff editor inside another editor
|
||||
* Defaults to false
|
||||
*/
|
||||
isInEmbeddedEditor?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -190,7 +190,7 @@ suite('AbstractKeybindingService', () => {
|
|||
statusMessageCallsDisposed = null;
|
||||
});
|
||||
|
||||
function kbItem(keybinding: number, command: string, when?: ContextKeyExpression): ResolvedKeybindingItem {
|
||||
function kbItem(keybinding: number | number[], command: string | null, when?: ContextKeyExpression): ResolvedKeybindingItem {
|
||||
return new ResolvedKeybindingItem(
|
||||
createUSLayoutResolvedKeybinding(keybinding, OS),
|
||||
command,
|
||||
|
@ -206,6 +206,155 @@ suite('AbstractKeybindingService', () => {
|
|||
return createUSLayoutResolvedKeybinding(keybinding, OS)!.getLabel()!;
|
||||
}
|
||||
|
||||
suite('simple tests: single- and multi-chord keybindings are dispatched', () => {
|
||||
|
||||
test('a single-chord keybinding is dispatched correctly; this test makes sure the dispatch in general works before we test empty-string/null command ID', () => {
|
||||
|
||||
const key = KeyMod.CtrlCmd | KeyCode.KeyK;
|
||||
const kbService = createTestKeybindingService([
|
||||
kbItem(key, 'myCommand'),
|
||||
]);
|
||||
|
||||
currentContextValue = createContext({});
|
||||
const shouldPreventDefault = kbService.testDispatch(key);
|
||||
assert.deepStrictEqual(shouldPreventDefault, true);
|
||||
assert.deepStrictEqual(executeCommandCalls, ([{ commandId: "myCommand", args: [null] }]));
|
||||
assert.deepStrictEqual(showMessageCalls, []);
|
||||
assert.deepStrictEqual(statusMessageCalls, []);
|
||||
assert.deepStrictEqual(statusMessageCallsDisposed, []);
|
||||
|
||||
kbService.dispose();
|
||||
});
|
||||
|
||||
test('a multi-chord keybinding is dispatched correctly', () => {
|
||||
|
||||
const chord0 = KeyMod.CtrlCmd | KeyCode.KeyK;
|
||||
const chord1 = KeyMod.CtrlCmd | KeyCode.KeyI;
|
||||
const key = [chord0, chord1];
|
||||
const kbService = createTestKeybindingService([
|
||||
kbItem(key, 'myCommand'),
|
||||
]);
|
||||
|
||||
currentContextValue = createContext({});
|
||||
|
||||
let shouldPreventDefault = kbService.testDispatch(chord0);
|
||||
assert.deepStrictEqual(shouldPreventDefault, true);
|
||||
assert.deepStrictEqual(executeCommandCalls, []);
|
||||
assert.deepStrictEqual(showMessageCalls, []);
|
||||
assert.deepStrictEqual(statusMessageCalls, ([`(${toUsLabel(chord0)}) was pressed. Waiting for second key of chord...`]));
|
||||
assert.deepStrictEqual(statusMessageCallsDisposed, []);
|
||||
|
||||
shouldPreventDefault = kbService.testDispatch(chord1);
|
||||
assert.deepStrictEqual(shouldPreventDefault, true);
|
||||
assert.deepStrictEqual(executeCommandCalls, ([{ commandId: "myCommand", args: [null] }]));
|
||||
assert.deepStrictEqual(showMessageCalls, []);
|
||||
assert.deepStrictEqual(statusMessageCalls, ([`(${toUsLabel(chord0)}) was pressed. Waiting for second key of chord...`]));
|
||||
assert.deepStrictEqual(statusMessageCallsDisposed, ([`(${toUsLabel(chord0)}) was pressed. Waiting for second key of chord...`]));
|
||||
|
||||
kbService.dispose();
|
||||
});
|
||||
});
|
||||
|
||||
suite('keybindings with empty-string/null command ID', () => {
|
||||
|
||||
test('a single-chord keybinding with an empty string command ID unbinds the keybinding (shouldPreventDefault = false)', () => {
|
||||
|
||||
const kbService = createTestKeybindingService([
|
||||
kbItem(KeyMod.CtrlCmd | KeyCode.KeyK, 'myCommand'),
|
||||
kbItem(KeyMod.CtrlCmd | KeyCode.KeyK, ''),
|
||||
]);
|
||||
|
||||
// send Ctrl/Cmd + K
|
||||
currentContextValue = createContext({});
|
||||
const shouldPreventDefault = kbService.testDispatch(KeyMod.CtrlCmd | KeyCode.KeyK);
|
||||
assert.deepStrictEqual(shouldPreventDefault, false);
|
||||
assert.deepStrictEqual(executeCommandCalls, []);
|
||||
assert.deepStrictEqual(showMessageCalls, []);
|
||||
assert.deepStrictEqual(statusMessageCalls, []);
|
||||
assert.deepStrictEqual(statusMessageCallsDisposed, []);
|
||||
|
||||
kbService.dispose();
|
||||
});
|
||||
|
||||
test('a single-chord keybinding with a null command ID unbinds the keybinding (shouldPreventDefault = false)', () => {
|
||||
|
||||
const kbService = createTestKeybindingService([
|
||||
kbItem(KeyMod.CtrlCmd | KeyCode.KeyK, 'myCommand'),
|
||||
kbItem(KeyMod.CtrlCmd | KeyCode.KeyK, null),
|
||||
]);
|
||||
|
||||
// send Ctrl/Cmd + K
|
||||
currentContextValue = createContext({});
|
||||
const shouldPreventDefault = kbService.testDispatch(KeyMod.CtrlCmd | KeyCode.KeyK);
|
||||
assert.deepStrictEqual(shouldPreventDefault, false);
|
||||
assert.deepStrictEqual(executeCommandCalls, []);
|
||||
assert.deepStrictEqual(showMessageCalls, []);
|
||||
assert.deepStrictEqual(statusMessageCalls, []);
|
||||
assert.deepStrictEqual(statusMessageCallsDisposed, []);
|
||||
|
||||
kbService.dispose();
|
||||
});
|
||||
|
||||
test('a multi-chord keybinding with an empty-string command ID keeps the keybinding (shouldPreventDefault = true)', () => {
|
||||
|
||||
const chord0 = KeyMod.CtrlCmd | KeyCode.KeyK;
|
||||
const chord1 = KeyMod.CtrlCmd | KeyCode.KeyI;
|
||||
const key = [chord0, chord1];
|
||||
const kbService = createTestKeybindingService([
|
||||
kbItem(key, 'myCommand'),
|
||||
kbItem(key, ''),
|
||||
]);
|
||||
|
||||
currentContextValue = createContext({});
|
||||
|
||||
let shouldPreventDefault = kbService.testDispatch(KeyMod.CtrlCmd | KeyCode.KeyK);
|
||||
assert.deepStrictEqual(shouldPreventDefault, true);
|
||||
assert.deepStrictEqual(executeCommandCalls, []);
|
||||
assert.deepStrictEqual(showMessageCalls, []);
|
||||
assert.deepStrictEqual(statusMessageCalls, ([`(${toUsLabel(chord0)}) was pressed. Waiting for second key of chord...`]));
|
||||
assert.deepStrictEqual(statusMessageCallsDisposed, []);
|
||||
|
||||
shouldPreventDefault = kbService.testDispatch(KeyMod.CtrlCmd | KeyCode.KeyI);
|
||||
assert.deepStrictEqual(shouldPreventDefault, true);
|
||||
assert.deepStrictEqual(executeCommandCalls, []);
|
||||
assert.deepStrictEqual(showMessageCalls, []);
|
||||
assert.deepStrictEqual(statusMessageCalls, ([`(${toUsLabel(chord0)}) was pressed. Waiting for second key of chord...`, `The key combination (${toUsLabel(chord0)}, ${toUsLabel(chord1)}) is not a command.`]));
|
||||
assert.deepStrictEqual(statusMessageCallsDisposed, ([`(${toUsLabel(chord0)}) was pressed. Waiting for second key of chord...`]));
|
||||
|
||||
kbService.dispose();
|
||||
});
|
||||
|
||||
test('a multi-chord keybinding with a null command ID keeps the keybinding (shouldPreventDefault = true)', () => {
|
||||
|
||||
const chord0 = KeyMod.CtrlCmd | KeyCode.KeyK;
|
||||
const chord1 = KeyMod.CtrlCmd | KeyCode.KeyI;
|
||||
const key = [chord0, chord1];
|
||||
const kbService = createTestKeybindingService([
|
||||
kbItem(key, 'myCommand'),
|
||||
kbItem(key, null),
|
||||
]);
|
||||
|
||||
currentContextValue = createContext({});
|
||||
|
||||
let shouldPreventDefault = kbService.testDispatch(KeyMod.CtrlCmd | KeyCode.KeyK);
|
||||
assert.deepStrictEqual(shouldPreventDefault, true);
|
||||
assert.deepStrictEqual(executeCommandCalls, []);
|
||||
assert.deepStrictEqual(showMessageCalls, []);
|
||||
assert.deepStrictEqual(statusMessageCalls, ([`(${toUsLabel(chord0)}) was pressed. Waiting for second key of chord...`]));
|
||||
assert.deepStrictEqual(statusMessageCallsDisposed, []);
|
||||
|
||||
shouldPreventDefault = kbService.testDispatch(KeyMod.CtrlCmd | KeyCode.KeyI);
|
||||
assert.deepStrictEqual(shouldPreventDefault, true);
|
||||
assert.deepStrictEqual(executeCommandCalls, []);
|
||||
assert.deepStrictEqual(showMessageCalls, []);
|
||||
assert.deepStrictEqual(statusMessageCalls, ([`(${toUsLabel(chord0)}) was pressed. Waiting for second key of chord...`, `The key combination (${toUsLabel(chord0)}, ${toUsLabel(chord1)}) is not a command.`]));
|
||||
assert.deepStrictEqual(statusMessageCallsDisposed, ([`(${toUsLabel(chord0)}) was pressed. Waiting for second key of chord...`]));
|
||||
|
||||
kbService.dispose();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
test('issue #16498: chord mode is quit for invalid chords', () => {
|
||||
|
||||
const kbService = createTestKeybindingService([
|
||||
|
|
|
@ -316,8 +316,11 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati
|
|||
}
|
||||
|
||||
// Pass the sequence along to the capability
|
||||
const [command, ...args] = data.split(';');
|
||||
switch (command) {
|
||||
const argsIndex = data.indexOf(';');
|
||||
const sequenceCommand = argsIndex === -1 ? data : data.substring(0, argsIndex);
|
||||
// Cast to strict checked index access
|
||||
const args: (string | undefined)[] = argsIndex === -1 ? [] : data.substring(argsIndex + 1).split(';');
|
||||
switch (sequenceCommand) {
|
||||
case VSCodeOscPt.PromptStart:
|
||||
this._createOrGetCommandDetection(this._terminal).handlePromptStart();
|
||||
return true;
|
||||
|
@ -328,18 +331,21 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati
|
|||
this._createOrGetCommandDetection(this._terminal).handleCommandExecuted();
|
||||
return true;
|
||||
case VSCodeOscPt.CommandFinished: {
|
||||
const exitCode = args.length === 1 ? parseInt(args[0]) : undefined;
|
||||
const arg0 = args[0];
|
||||
const exitCode = arg0 !== undefined ? parseInt(arg0) : undefined;
|
||||
this._createOrGetCommandDetection(this._terminal).handleCommandFinished(exitCode);
|
||||
return true;
|
||||
}
|
||||
case VSCodeOscPt.CommandLine: {
|
||||
const arg0 = args[0];
|
||||
const arg1 = args[1];
|
||||
let commandLine: string;
|
||||
if (args.length >= 1 || args.length <= 2) {
|
||||
commandLine = deserializeMessage(args[0]);
|
||||
if (arg0 !== undefined) {
|
||||
commandLine = deserializeMessage(arg0);
|
||||
} else {
|
||||
commandLine = '';
|
||||
}
|
||||
this._createOrGetCommandDetection(this._terminal).setCommandLine(commandLine, args[1] === this._nonce);
|
||||
this._createOrGetCommandDetection(this._terminal).setCommandLine(commandLine, arg1 === this._nonce);
|
||||
return true;
|
||||
}
|
||||
case VSCodeOscPt.ContinuationStart: {
|
||||
|
@ -359,7 +365,8 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati
|
|||
return true;
|
||||
}
|
||||
case VSCodeOscPt.Property: {
|
||||
const deserialized = args.length ? deserializeMessage(args[0]) : '';
|
||||
const arg0 = args[0];
|
||||
const deserialized = arg0 !== undefined ? deserializeMessage(arg0) : '';
|
||||
const { key, value } = parseKeyValueAssignment(deserialized);
|
||||
if (value === undefined) {
|
||||
return true;
|
||||
|
@ -539,10 +546,14 @@ export function parseKeyValueAssignment(message: string): { key: string; value:
|
|||
}
|
||||
|
||||
|
||||
export function parseMarkSequence(sequence: string[]): { id?: string; hidden?: boolean } {
|
||||
export function parseMarkSequence(sequence: (string | undefined)[]): { id?: string; hidden?: boolean } {
|
||||
let id = undefined;
|
||||
let hidden = false;
|
||||
for (const property of sequence) {
|
||||
// Sanity check, this shouldn't happen in practice
|
||||
if (property === undefined) {
|
||||
continue;
|
||||
}
|
||||
if (property === 'Hidden') {
|
||||
hidden = true;
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ export class Win32UpdateService extends AbstractUpdateService {
|
|||
|
||||
@memoize
|
||||
get cachePath(): Promise<string> {
|
||||
const result = path.join(tmpdir(), `vscode-update-${this.productService.target}-${process.arch}`);
|
||||
const result = path.join(tmpdir(), `vscode-${this.productService.quality}-${this.productService.target}-${process.arch}`);
|
||||
return pfs.Promises.mkdir(result, { recursive: true }).then(() => result);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { createStringDataTransferItem, VSDataTransfer } from 'vs/base/common/dataTransfer';
|
||||
import { createStringDataTransferItem, IReadonlyVSDataTransfer, VSDataTransfer } from 'vs/base/common/dataTransfer';
|
||||
import { CancellationError } from 'vs/base/common/errors';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { combinedDisposable, Disposable, DisposableMap, toDisposable } from 'vs/base/common/lifecycle';
|
||||
|
@ -27,7 +27,7 @@ import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
|||
import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity';
|
||||
import { reviveWorkspaceEditDto } from 'vs/workbench/api/browser/mainThreadBulkEdits';
|
||||
import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { DataTransferCache } from 'vs/workbench/api/common/shared/dataTransferCache';
|
||||
import { DataTransferFileCache } from 'vs/workbench/api/common/shared/dataTransferCache';
|
||||
import * as callh from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
|
||||
import * as search from 'vs/workbench/contrib/search/common/search';
|
||||
import * as typeh from 'vs/workbench/contrib/typeHierarchy/common/typeHierarchy';
|
||||
|
@ -927,12 +927,12 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread
|
|||
|
||||
class MainThreadPasteEditProvider implements languages.DocumentPasteEditProvider {
|
||||
|
||||
private readonly dataTransfers = new DataTransferCache();
|
||||
private readonly dataTransfers = new DataTransferFileCache();
|
||||
|
||||
public readonly copyMimeTypes?: readonly string[];
|
||||
public readonly pasteMimeTypes: readonly string[];
|
||||
|
||||
readonly prepareDocumentPaste?: (model: ITextModel, ranges: readonly IRange[], dataTransfer: VSDataTransfer, token: CancellationToken) => Promise<undefined | VSDataTransfer>;
|
||||
readonly prepareDocumentPaste?: languages.DocumentPasteEditProvider['prepareDocumentPaste'];
|
||||
|
||||
constructor(
|
||||
private readonly handle: number,
|
||||
|
@ -944,40 +944,41 @@ class MainThreadPasteEditProvider implements languages.DocumentPasteEditProvider
|
|||
this.pasteMimeTypes = metadata.pasteMimeTypes;
|
||||
|
||||
if (metadata.supportsCopy) {
|
||||
this.prepareDocumentPaste = async (model: ITextModel, selections: readonly IRange[], dataTransfer: VSDataTransfer, token: CancellationToken): Promise<VSDataTransfer | undefined> => {
|
||||
const dataTransferDto = await typeConvert.DataTransfer.toDataTransferDTO(dataTransfer);
|
||||
this.prepareDocumentPaste = async (model: ITextModel, selections: readonly IRange[], dataTransfer: IReadonlyVSDataTransfer, token: CancellationToken): Promise<IReadonlyVSDataTransfer | undefined> => {
|
||||
const dataTransferDto = await typeConvert.DataTransfer.from(dataTransfer);
|
||||
if (token.isCancellationRequested) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const result = await this._proxy.$prepareDocumentPaste(handle, model.uri, selections, dataTransferDto, token);
|
||||
if (!result) {
|
||||
const newDataTransfer = await this._proxy.$prepareDocumentPaste(handle, model.uri, selections, dataTransferDto, token);
|
||||
if (!newDataTransfer) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const dataTransferOut = new VSDataTransfer();
|
||||
result.items.forEach(([type, item]) => {
|
||||
for (const [type, item] of newDataTransfer.items) {
|
||||
dataTransferOut.replace(type, createStringDataTransferItem(item.asString));
|
||||
});
|
||||
}
|
||||
return dataTransferOut;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async provideDocumentPasteEdits(model: ITextModel, selections: Selection[], dataTransfer: VSDataTransfer, token: CancellationToken) {
|
||||
async provideDocumentPasteEdits(model: ITextModel, selections: Selection[], dataTransfer: IReadonlyVSDataTransfer, token: CancellationToken) {
|
||||
const request = this.dataTransfers.add(dataTransfer);
|
||||
try {
|
||||
const d = await typeConvert.DataTransfer.toDataTransferDTO(dataTransfer);
|
||||
const result = await this._proxy.$providePasteEdits(this.handle, request.id, model.uri, selections, d, token);
|
||||
const dataTransferDto = await typeConvert.DataTransfer.from(dataTransfer);
|
||||
if (token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await this._proxy.$providePasteEdits(this.handle, request.id, model.uri, selections, dataTransferDto, token);
|
||||
if (!result) {
|
||||
return undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
return {
|
||||
id: result.id,
|
||||
label: result.label,
|
||||
detail: result.detail,
|
||||
insertText: result.insertText,
|
||||
...result,
|
||||
additionalEdit: result.additionalEdit ? reviveWorkspaceEditDto(result.additionalEdit, this._uriIdentService, dataId => this.resolveFileData(request.id, dataId)) : undefined,
|
||||
};
|
||||
} finally {
|
||||
|
@ -992,7 +993,7 @@ class MainThreadPasteEditProvider implements languages.DocumentPasteEditProvider
|
|||
|
||||
class MainThreadDocumentOnDropEditProvider implements languages.DocumentOnDropEditProvider {
|
||||
|
||||
private readonly dataTransfers = new DataTransferCache();
|
||||
private readonly dataTransfers = new DataTransferFileCache();
|
||||
|
||||
readonly dropMimeTypes?: readonly string[];
|
||||
|
||||
|
@ -1005,18 +1006,21 @@ class MainThreadDocumentOnDropEditProvider implements languages.DocumentOnDropEd
|
|||
this.dropMimeTypes = metadata?.dropMimeTypes ?? ['*/*'];
|
||||
}
|
||||
|
||||
async provideDocumentOnDropEdits(model: ITextModel, position: IPosition, dataTransfer: VSDataTransfer, token: CancellationToken): Promise<languages.DocumentOnDropEdit | null | undefined> {
|
||||
async provideDocumentOnDropEdits(model: ITextModel, position: IPosition, dataTransfer: IReadonlyVSDataTransfer, token: CancellationToken): Promise<languages.DocumentOnDropEdit | null | undefined> {
|
||||
const request = this.dataTransfers.add(dataTransfer);
|
||||
try {
|
||||
const dataTransferDto = await typeConvert.DataTransfer.toDataTransferDTO(dataTransfer);
|
||||
const dataTransferDto = await typeConvert.DataTransfer.from(dataTransfer);
|
||||
if (token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
|
||||
const edit = await this._proxy.$provideDocumentOnDropEdits(this.handle, request.id, model.uri, position, dataTransferDto, token);
|
||||
if (!edit) {
|
||||
return undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
return {
|
||||
id: edit.id,
|
||||
label: edit.label,
|
||||
insertText: edit.insertText,
|
||||
...edit,
|
||||
additionalEdit: reviveWorkspaceEditDto(edit.additionalEdit, this._uriIdentService, dataId => this.resolveDocumentOnDropFileData(request.id, dataId)),
|
||||
};
|
||||
} finally {
|
||||
|
|
|
@ -16,7 +16,7 @@ import { ILogService } from 'vs/platform/log/common/log';
|
|||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { createStringDataTransferItem, VSDataTransfer } from 'vs/base/common/dataTransfer';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { DataTransferCache } from 'vs/workbench/api/common/shared/dataTransferCache';
|
||||
import { DataTransferFileCache } from 'vs/workbench/api/common/shared/dataTransferCache';
|
||||
import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadTreeViews)
|
||||
|
@ -210,7 +210,7 @@ type TreeItemHandle = string;
|
|||
|
||||
class TreeViewDragAndDropController implements ITreeViewDragAndDropController {
|
||||
|
||||
private readonly dataTransfersCache = new DataTransferCache();
|
||||
private readonly dataTransfersCache = new DataTransferFileCache();
|
||||
|
||||
constructor(private readonly treeViewId: string,
|
||||
readonly dropMimeTypes: string[],
|
||||
|
@ -222,7 +222,11 @@ class TreeViewDragAndDropController implements ITreeViewDragAndDropController {
|
|||
operationUuid?: string, sourceTreeId?: string, sourceTreeItemHandles?: string[]): Promise<void> {
|
||||
const request = this.dataTransfersCache.add(dataTransfer);
|
||||
try {
|
||||
return await this._proxy.$handleDrop(this.treeViewId, request.id, await typeConvert.DataTransfer.toDataTransferDTO(dataTransfer), targetTreeItem?.handle, token, operationUuid, sourceTreeId, sourceTreeItemHandles);
|
||||
const dataTransferDto = await typeConvert.DataTransfer.from(dataTransfer);
|
||||
if (token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
return await this._proxy.$handleDrop(this.treeViewId, request.id, dataTransferDto, targetTreeItem?.handle, token, operationUuid, sourceTreeId, sourceTreeItemHandles);
|
||||
} finally {
|
||||
request.dispose();
|
||||
}
|
||||
|
|
|
@ -191,23 +191,27 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc
|
|||
}
|
||||
|
||||
public $disposeWebview(handle: extHostProtocol.WebviewHandle): void {
|
||||
const webview = this.getWebviewInput(handle);
|
||||
const webview = this.tryGetWebviewInput(handle);
|
||||
if (!webview) {
|
||||
return;
|
||||
}
|
||||
webview.dispose();
|
||||
}
|
||||
|
||||
public $setTitle(handle: extHostProtocol.WebviewHandle, value: string): void {
|
||||
const webview = this.getWebviewInput(handle);
|
||||
webview.setName(value);
|
||||
this.tryGetWebviewInput(handle)?.setName(value);
|
||||
}
|
||||
|
||||
public $setIconPath(handle: extHostProtocol.WebviewHandle, value: extHostProtocol.IWebviewIconPath | undefined): void {
|
||||
const webview = this.getWebviewInput(handle);
|
||||
webview.iconPath = reviveWebviewIcon(value);
|
||||
const webview = this.tryGetWebviewInput(handle);
|
||||
if (webview) {
|
||||
webview.iconPath = reviveWebviewIcon(value);
|
||||
}
|
||||
}
|
||||
|
||||
public $reveal(handle: extHostProtocol.WebviewHandle, showOptions: extHostProtocol.WebviewPanelShowOptions): void {
|
||||
const webview = this.getWebviewInput(handle);
|
||||
if (webview.isDisposed()) {
|
||||
const webview = this.tryGetWebviewInput(handle);
|
||||
if (!webview || webview.isDisposed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -342,14 +346,6 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc
|
|||
}
|
||||
}
|
||||
|
||||
private getWebviewInput(handle: extHostProtocol.WebviewHandle): WebviewInput {
|
||||
const webview = this.tryGetWebviewInput(handle);
|
||||
if (!webview) {
|
||||
throw new Error(`Unknown webview handle:${handle}`);
|
||||
}
|
||||
return webview;
|
||||
}
|
||||
|
||||
private tryGetWebviewInput(handle: extHostProtocol.WebviewHandle): WebviewInput | undefined {
|
||||
return this._webviewInputs.getInputForHandle(handle);
|
||||
}
|
||||
|
|
|
@ -53,17 +53,21 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
|||
}
|
||||
|
||||
public $setHtml(handle: extHostProtocol.WebviewHandle, value: string): void {
|
||||
const webview = this.getWebview(handle);
|
||||
webview.setHtml(value);
|
||||
this.tryGetWebview(handle)?.setHtml(value);
|
||||
}
|
||||
|
||||
public $setOptions(handle: extHostProtocol.WebviewHandle, options: extHostProtocol.IWebviewContentOptions): void {
|
||||
const webview = this.getWebview(handle);
|
||||
webview.contentOptions = reviveWebviewContentOptions(options);
|
||||
const webview = this.tryGetWebview(handle);
|
||||
if (webview) {
|
||||
webview.contentOptions = reviveWebviewContentOptions(options);
|
||||
}
|
||||
}
|
||||
|
||||
public async $postMessage(handle: extHostProtocol.WebviewHandle, jsonMessage: string, ...buffers: VSBuffer[]): Promise<boolean> {
|
||||
const webview = this.getWebview(handle);
|
||||
const webview = this.tryGetWebview(handle);
|
||||
if (!webview) {
|
||||
return false;
|
||||
}
|
||||
const { message, arrayBuffers } = deserializeWebviewMessage(jsonMessage, buffers);
|
||||
return webview.postMessage(message, arrayBuffers);
|
||||
}
|
||||
|
@ -113,8 +117,12 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
|||
return false;
|
||||
}
|
||||
|
||||
private tryGetWebview(handle: extHostProtocol.WebviewHandle): IWebview | undefined {
|
||||
return this._webviews.get(handle);
|
||||
}
|
||||
|
||||
private getWebview(handle: extHostProtocol.WebviewHandle): IWebview {
|
||||
const webview = this._webviews.get(handle);
|
||||
const webview = this.tryGetWebview(handle);
|
||||
if (!webview) {
|
||||
throw new Error(`Unknown webview handle:${handle}`);
|
||||
}
|
||||
|
|
|
@ -254,7 +254,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
|||
return extHostAuthentication.getSession(extension, providerId, scopes, options as any);
|
||||
},
|
||||
getSessions(providerId: string, scopes: readonly string[]) {
|
||||
checkProposedApiEnabled(extension, 'getSessions');
|
||||
checkProposedApiEnabled(extension, 'authGetSessions');
|
||||
return extHostAuthentication.getSessions(extension, providerId, scopes);
|
||||
},
|
||||
// TODO: remove this after GHPR and Codespaces move off of it
|
||||
|
|
|
@ -1493,12 +1493,12 @@ export interface ExtHostDocumentsAndEditorsShape {
|
|||
}
|
||||
|
||||
export interface IDataTransferFileDTO {
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly uri?: UriComponents;
|
||||
}
|
||||
|
||||
export interface DataTransferItemDTO {
|
||||
readonly id: string;
|
||||
readonly asString: string;
|
||||
readonly fileData: IDataTransferFileDTO | undefined;
|
||||
readonly uriListData?: ReadonlyArray<string | UriComponents>;
|
||||
|
@ -1838,6 +1838,7 @@ export interface IPasteEditDto {
|
|||
id: string;
|
||||
label: string;
|
||||
detail: string;
|
||||
priority: number;
|
||||
insertText: string | { snippet: string };
|
||||
additionalEdit?: IWorkspaceEditDto;
|
||||
}
|
||||
|
@ -1849,6 +1850,7 @@ export interface IDocumentDropEditProviderMetadata {
|
|||
export interface IDocumentOnDropEditDto {
|
||||
id: string;
|
||||
label: string;
|
||||
priority: number;
|
||||
insertText: string | { snippet: string };
|
||||
additionalEdit?: IWorkspaceEditDto;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import { URI, UriComponents } from 'vs/base/common/uri';
|
|||
import { mixin } from 'vs/base/common/objects';
|
||||
import type * as vscode from 'vscode';
|
||||
import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { Range, Disposable, CompletionList, SnippetString, CodeActionKind, SymbolInformation, DocumentSymbol, SemanticTokensEdits, SemanticTokens, SemanticTokensEdit, Location, InlineCompletionTriggerKind } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { Range, Disposable, CompletionList, SnippetString, CodeActionKind, SymbolInformation, DocumentSymbol, SemanticTokensEdits, SemanticTokens, SemanticTokensEdit, Location, InlineCompletionTriggerKind, InternalDataTransferItem } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { ISingleEditOperation } from 'vs/editor/common/core/editOperation';
|
||||
import * as languages from 'vs/editor/common/languages';
|
||||
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
|
||||
|
@ -510,7 +510,7 @@ class DocumentPasteEditProvider {
|
|||
|
||||
async prepareDocumentPaste(resource: URI, ranges: IRange[], dataTransferDto: extHostProtocol.DataTransferDTO, token: CancellationToken): Promise<extHostProtocol.DataTransferDTO | undefined> {
|
||||
if (!this._provider.prepareDocumentPaste) {
|
||||
return undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
const doc = this._documents.getDocument(resource);
|
||||
|
@ -520,8 +520,13 @@ class DocumentPasteEditProvider {
|
|||
throw new NotImplementedError();
|
||||
});
|
||||
await this._provider.prepareDocumentPaste(doc, vscodeRanges, dataTransfer, token);
|
||||
if (token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
|
||||
return typeConvert.DataTransfer.toDataTransferDTO(dataTransfer);
|
||||
// Only send back values that have been added to the data transfer
|
||||
const entries = Array.from(dataTransfer).filter(([, value]) => !(value instanceof InternalDataTransferItem));
|
||||
return typeConvert.DataTransfer.from(entries);
|
||||
}
|
||||
|
||||
async providePasteEdits(requestId: number, resource: URI, ranges: IRange[], dataTransferDto: extHostProtocol.DataTransferDTO, token: CancellationToken): Promise<undefined | extHostProtocol.IPasteEditDto> {
|
||||
|
@ -541,6 +546,7 @@ class DocumentPasteEditProvider {
|
|||
id: edit.id ? this._extension.identifier.value + '.' + edit.id : this._extension.identifier.value,
|
||||
label: edit.label ?? localize('defaultPasteLabel', "Paste using '{0}' extension", this._extension.displayName || this._extension.name),
|
||||
detail: this._extension.displayName || this._extension.name,
|
||||
priority: edit.priority ?? 0,
|
||||
insertText: typeof edit.insertText === 'string' ? edit.insertText : { snippet: edit.insertText.value },
|
||||
additionalEdit: edit.additionalEdit ? typeConvert.WorkspaceEdit.from(edit.additionalEdit, undefined) : undefined,
|
||||
};
|
||||
|
@ -1754,6 +1760,7 @@ class DocumentOnDropEditAdapter {
|
|||
return {
|
||||
id: edit.id ? this._extension.identifier.value + '.' + edit.id : this._extension.identifier.value,
|
||||
label: edit.label ?? localize('defaultDropLabel', "Drop using '{0}' extension", this._extension.displayName || this._extension.name),
|
||||
priority: edit.priority ?? 0,
|
||||
insertText: typeof edit.insertText === 'string' ? edit.insertText : { snippet: edit.insertText.value },
|
||||
additionalEdit: edit.additionalEdit ? typeConvert.WorkspaceEdit.from(edit.additionalEdit, undefined) : undefined,
|
||||
};
|
||||
|
|
|
@ -191,11 +191,11 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
|
|||
}
|
||||
|
||||
const treeDataTransfer = await this.addAdditionalTransferItems(new types.DataTransfer(), treeView, sourceTreeItemHandles, token, operationUuid);
|
||||
if (!treeDataTransfer) {
|
||||
if (!treeDataTransfer || token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
|
||||
return DataTransfer.toDataTransferDTO(treeDataTransfer);
|
||||
return DataTransfer.from(treeDataTransfer);
|
||||
}
|
||||
|
||||
async $hasResolve(treeViewId: string): Promise<boolean> {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import { asArray, coalesce, isNonEmptyArray } from 'vs/base/common/arrays';
|
||||
import { VSBuffer, encodeBase64 } from 'vs/base/common/buffer';
|
||||
import { IDataTransferItem, UriList, VSDataTransfer } from 'vs/base/common/dataTransfer';
|
||||
import { IDataTransferFile, IDataTransferItem, UriList } from 'vs/base/common/dataTransfer';
|
||||
import { once } from 'vs/base/common/functional';
|
||||
import * as htmlContent from 'vs/base/common/htmlContent';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
|
@ -2035,21 +2035,18 @@ export namespace ViewBadge {
|
|||
}
|
||||
|
||||
export namespace DataTransferItem {
|
||||
export function to(mime: string, item: extHostProtocol.DataTransferItemDTO, resolveFileData: () => Promise<Uint8Array>): types.DataTransferItem {
|
||||
export function to(mime: string, item: extHostProtocol.DataTransferItemDTO, resolveFileData: (id: string) => Promise<Uint8Array>): types.DataTransferItem {
|
||||
const file = item.fileData;
|
||||
if (file) {
|
||||
return new class extends types.DataTransferItem {
|
||||
override asFile() {
|
||||
return new types.DataTransferFile(file.name, URI.revive(file.uri), item.id, once(() => resolveFileData()));
|
||||
}
|
||||
}('', item.id);
|
||||
return new types.InternalFileDataTransferItem(
|
||||
new types.DataTransferFile(file.name, URI.revive(file.uri), file.id, once(() => resolveFileData(file.id))));
|
||||
}
|
||||
|
||||
if (mime === Mimes.uriList && item.uriListData) {
|
||||
return new types.DataTransferItem(reviveUriList(item.uriListData));
|
||||
return new types.InternalDataTransferItem(reviveUriList(item.uriListData));
|
||||
}
|
||||
|
||||
return new types.DataTransferItem(item.asString);
|
||||
return new types.InternalDataTransferItem(item.asString);
|
||||
}
|
||||
|
||||
export async function from(mime: string, item: vscode.DataTransferItem | IDataTransferItem): Promise<extHostProtocol.DataTransferItemDTO> {
|
||||
|
@ -2057,7 +2054,6 @@ export namespace DataTransferItem {
|
|||
|
||||
if (mime === Mimes.uriList) {
|
||||
return {
|
||||
id: (item as IDataTransferItem | types.DataTransferItem).id,
|
||||
asString: stringValue,
|
||||
fileData: undefined,
|
||||
uriListData: serializeUriList(stringValue),
|
||||
|
@ -2066,9 +2062,12 @@ export namespace DataTransferItem {
|
|||
|
||||
const fileValue = item.asFile();
|
||||
return {
|
||||
id: (item as IDataTransferItem | types.DataTransferItem).id,
|
||||
asString: stringValue,
|
||||
fileData: fileValue ? { name: fileValue.name, uri: fileValue.uri } : undefined,
|
||||
fileData: fileValue ? {
|
||||
name: fileValue.name,
|
||||
uri: fileValue.uri,
|
||||
id: (fileValue as types.DataTransferFile)._itemId ?? (fileValue as IDataTransferFile).id,
|
||||
} : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -2098,21 +2097,20 @@ export namespace DataTransferItem {
|
|||
export namespace DataTransfer {
|
||||
export function toDataTransfer(value: extHostProtocol.DataTransferDTO, resolveFileData: (itemId: string) => Promise<Uint8Array>): types.DataTransfer {
|
||||
const init = value.items.map(([type, item]) => {
|
||||
return [type, DataTransferItem.to(type, item, () => resolveFileData(item.id))] as const;
|
||||
return [type, DataTransferItem.to(type, item, resolveFileData)] as const;
|
||||
});
|
||||
return new types.DataTransfer(init);
|
||||
}
|
||||
|
||||
export async function toDataTransferDTO(value: vscode.DataTransfer | VSDataTransfer): Promise<extHostProtocol.DataTransferDTO> {
|
||||
export async function from(dataTransfer: Iterable<readonly [string, vscode.DataTransferItem | IDataTransferItem]>): Promise<extHostProtocol.DataTransferDTO> {
|
||||
const newDTO: extHostProtocol.DataTransferDTO = { items: [] };
|
||||
|
||||
const promises: Promise<any>[] = [];
|
||||
|
||||
value.forEach((value, key) => {
|
||||
for (const [mime, value] of dataTransfer) {
|
||||
promises.push((async () => {
|
||||
newDTO.items.push([key, await DataTransferItem.from(key, value)]);
|
||||
newDTO.items.push([mime, await DataTransferItem.from(mime, value)]);
|
||||
})());
|
||||
});
|
||||
}
|
||||
|
||||
await Promise.all(promises);
|
||||
|
||||
|
|
|
@ -2594,13 +2594,34 @@ export class DataTransferItem implements vscode.DataTransferItem {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
public readonly id: string;
|
||||
|
||||
constructor(
|
||||
public readonly value: any,
|
||||
id?: string,
|
||||
) {
|
||||
this.id = id ?? generateUuid();
|
||||
) { }
|
||||
}
|
||||
|
||||
/**
|
||||
* A data transfer item that has been created by VS Code instead of by a extension.
|
||||
*
|
||||
* Intentionally not exported to extensions.
|
||||
*/
|
||||
export class InternalDataTransferItem extends DataTransferItem { }
|
||||
|
||||
/**
|
||||
* A data transfer item for a file.
|
||||
*
|
||||
* Intentionally not exported to extensions as only we can create these.
|
||||
*/
|
||||
export class InternalFileDataTransferItem extends InternalDataTransferItem {
|
||||
|
||||
readonly #file: vscode.DataTransferFile;
|
||||
|
||||
constructor(file: vscode.DataTransferFile) {
|
||||
super('');
|
||||
this.#file = file;
|
||||
}
|
||||
|
||||
override asFile() {
|
||||
return this.#file;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,45 +3,41 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { coalesce } from 'vs/base/common/arrays';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { VSDataTransfer, IDataTransferItem } from 'vs/base/common/dataTransfer';
|
||||
import { IDataTransferFile, IReadonlyVSDataTransfer } from 'vs/base/common/dataTransfer';
|
||||
|
||||
export class DataTransferCache {
|
||||
export class DataTransferFileCache {
|
||||
|
||||
private requestIdPool = 0;
|
||||
private readonly dataTransfers = new Map</* requestId */ number, ReadonlyArray<IDataTransferItem>>();
|
||||
private readonly dataTransferFiles = new Map</* requestId */ number, ReadonlyArray<IDataTransferFile>>();
|
||||
|
||||
public add(dataTransfer: VSDataTransfer): { id: number; dispose: () => void } {
|
||||
public add(dataTransfer: IReadonlyVSDataTransfer): { id: number; dispose: () => void } {
|
||||
const requestId = this.requestIdPool++;
|
||||
this.dataTransfers.set(requestId, [...dataTransfer.values()]);
|
||||
this.dataTransferFiles.set(requestId, coalesce(Array.from(dataTransfer, ([, item]) => item.asFile())));
|
||||
return {
|
||||
id: requestId,
|
||||
dispose: () => {
|
||||
this.dataTransfers.delete(requestId);
|
||||
this.dataTransferFiles.delete(requestId);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async resolveFileData(requestId: number, dataItemId: string): Promise<VSBuffer> {
|
||||
const entry = this.dataTransfers.get(requestId);
|
||||
if (!entry) {
|
||||
const files = this.dataTransferFiles.get(requestId);
|
||||
if (!files) {
|
||||
throw new Error('No data transfer found');
|
||||
}
|
||||
|
||||
const item = entry.find(x => x.id === dataItemId);
|
||||
if (!item) {
|
||||
throw new Error('No item found in data transfer');
|
||||
}
|
||||
|
||||
const file = item.asFile();
|
||||
const file = files.find(file => file.id === dataItemId);
|
||||
if (!file) {
|
||||
throw new Error('Found data transfer item is not a file');
|
||||
throw new Error('No matching file found in data transfer');
|
||||
}
|
||||
|
||||
return VSBuffer.wrap(await file.data());
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.dataTransfers.clear();
|
||||
this.dataTransferFiles.clear();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ export class EditorGroupWatermark extends Disposable {
|
|||
|
||||
private readonly shortcuts: HTMLElement;
|
||||
private transientDisposables = this._register(new DisposableStore());
|
||||
private enabled: boolean;
|
||||
private enabled: boolean = false;
|
||||
private workbenchState: WorkbenchState;
|
||||
|
||||
constructor(
|
||||
|
@ -83,14 +83,10 @@ export class EditorGroupWatermark extends Disposable {
|
|||
append(container, elements.root);
|
||||
this.shortcuts = elements.shortcuts;
|
||||
|
||||
this.workbenchState = contextService.getWorkbenchState();
|
||||
this.enabled = this.configurationService.getValue<boolean>('workbench.tips.enabled');
|
||||
|
||||
this.registerListeners();
|
||||
|
||||
if (this.enabled) {
|
||||
this.render();
|
||||
}
|
||||
this.workbenchState = contextService.getWorkbenchState();
|
||||
this.render();
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
|
@ -98,24 +94,12 @@ export class EditorGroupWatermark extends Disposable {
|
|||
|
||||
this._register(this.configurationService.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration('workbench.tips.enabled')) {
|
||||
const enabled = this.configurationService.getValue<boolean>('workbench.tips.enabled');
|
||||
|
||||
if (enabled === this.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.enabled = enabled;
|
||||
|
||||
if (this.enabled) {
|
||||
this.render();
|
||||
} else {
|
||||
this.clear();
|
||||
}
|
||||
this.render();
|
||||
}
|
||||
}));
|
||||
|
||||
this._register(this.contextService.onDidChangeWorkbenchState(workbenchState => {
|
||||
if (!this.enabled || this.workbenchState === workbenchState) {
|
||||
if (this.workbenchState === workbenchState) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -134,8 +118,19 @@ export class EditorGroupWatermark extends Disposable {
|
|||
}
|
||||
|
||||
private render(): void {
|
||||
const enabled = this.configurationService.getValue<boolean>('workbench.tips.enabled');
|
||||
|
||||
if (enabled === this.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.enabled = enabled;
|
||||
this.clear();
|
||||
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const box = append(this.shortcuts, $('.watermark-box'));
|
||||
const folder = this.workbenchState !== WorkbenchState.EMPTY;
|
||||
const selected = (folder ? folderEntries : noFolderEntries)
|
||||
|
|
|
@ -1547,7 +1547,7 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop<ITreeItem> {
|
|||
return dndController.handleDrag(itemHandles, uuid, dragCancellationToken).then(additionalDataTransfer => {
|
||||
if (additionalDataTransfer) {
|
||||
const unlistedTypes: string[] = [];
|
||||
for (const item of additionalDataTransfer.entries()) {
|
||||
for (const item of additionalDataTransfer) {
|
||||
if ((item[0] !== this.treeMimeType) && (dndController.dragMimeTypes.findIndex(value => value === item[0]) < 0)) {
|
||||
unlistedTypes.push(item[0]);
|
||||
}
|
||||
|
@ -1625,7 +1625,7 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop<ITreeItem> {
|
|||
onDragOver(data: IDragAndDropData, targetElement: ITreeItem, targetIndex: number, originalEvent: DragEvent): boolean | ITreeDragOverReaction {
|
||||
const dataTransfer = toExternalVSDataTransfer(originalEvent.dataTransfer!);
|
||||
|
||||
const types = new Set<string>(Array.from(dataTransfer.entries()).map(x => x[0]));
|
||||
const types = new Set<string>(Array.from(dataTransfer, x => x[0]));
|
||||
|
||||
if (originalEvent.dataTransfer) {
|
||||
// Also add uri-list if we have any files. At this stage we can't actually access the file itself though.
|
||||
|
@ -1689,7 +1689,7 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop<ITreeItem> {
|
|||
const originalDataTransfer = toExternalVSDataTransfer(originalEvent.dataTransfer, true);
|
||||
|
||||
const outDataTransfer = new VSDataTransfer();
|
||||
for (const [type, item] of originalDataTransfer.entries()) {
|
||||
for (const [type, item] of originalDataTransfer) {
|
||||
if (type === this.treeMimeType || dndController.dropMimeTypes.includes(type) || (item.asFile() && dndController.dropMimeTypes.includes(DataTransfers.FILES.toLowerCase()))) {
|
||||
outDataTransfer.append(type, item);
|
||||
if (type === this.treeMimeType) {
|
||||
|
@ -1704,7 +1704,7 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop<ITreeItem> {
|
|||
|
||||
const additionalDataTransfer = await this.treeViewsDragAndDropService.removeDragOperationTransfer(willDropUuid);
|
||||
if (additionalDataTransfer) {
|
||||
for (const [type, item] of additionalDataTransfer.entries()) {
|
||||
for (const [type, item] of additionalDataTransfer) {
|
||||
outDataTransfer.append(type, item);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,8 +9,8 @@ import * as nls from 'vs/nls';
|
|||
import { contrastBorder, disabledForeground, listFocusOutline, registerColor, transparent } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IColorTheme } from 'vs/platform/theme/common/themeService';
|
||||
|
||||
const resolvedCommentViewIcon = registerColor('commentsView.resolvedIcon', { dark: disabledForeground, light: disabledForeground, hcDark: contrastBorder, hcLight: contrastBorder }, nls.localize('resolvedCommentBorder', 'Color of borders and arrow for resolved comments.'));
|
||||
const unresolvedCommentViewIcon = registerColor('commentsView.unresolvedIcon', { dark: listFocusOutline, light: listFocusOutline, hcDark: contrastBorder, hcLight: contrastBorder }, nls.localize('unresolvedCommentBorder', 'Color of borders and arrow for unresolved comments.'));
|
||||
const resolvedCommentViewIcon = registerColor('commentsView.resolvedIcon', { dark: disabledForeground, light: disabledForeground, hcDark: contrastBorder, hcLight: contrastBorder }, nls.localize('resolvedCommentIcon', 'Icon color for resolved comments.'));
|
||||
const unresolvedCommentViewIcon = registerColor('commentsView.unresolvedIcon', { dark: listFocusOutline, light: listFocusOutline, hcDark: contrastBorder, hcLight: contrastBorder }, nls.localize('unresolvedCommentIcon', 'Icon color for unresolved comments.'));
|
||||
|
||||
const resolvedCommentBorder = registerColor('editorCommentsWidget.resolvedBorder', { dark: resolvedCommentViewIcon, light: resolvedCommentViewIcon, hcDark: contrastBorder, hcLight: contrastBorder }, nls.localize('resolvedCommentBorder', 'Color of borders and arrow for resolved comments.'));
|
||||
const unresolvedCommentBorder = registerColor('editorCommentsWidget.unresolvedBorder', { dark: unresolvedCommentViewIcon, light: unresolvedCommentViewIcon, hcDark: contrastBorder, hcLight: contrastBorder }, nls.localize('unresolvedCommentBorder', 'Color of borders and arrow for unresolved comments.'));
|
||||
|
|
|
@ -14,6 +14,9 @@ import { IEditorDecorationsCollection } from 'vs/editor/common/editorCommon';
|
|||
import { CommentThreadState } from 'vs/editor/common/languages';
|
||||
|
||||
export const overviewRulerCommentingRangeForeground = registerColor('editorGutter.commentRangeForeground', { dark: opaque(listInactiveSelectionBackground, editorBackground), light: darken(opaque(listInactiveSelectionBackground, editorBackground), .05), hcDark: Color.white, hcLight: Color.black }, nls.localize('editorGutterCommentRangeForeground', 'Editor gutter decoration color for commenting ranges. This color should be opaque.'));
|
||||
const overviewRulerCommentForeground = registerColor('editorOverviewRuler.commentForeground', { dark: overviewRulerCommentingRangeForeground, light: overviewRulerCommentingRangeForeground, hcDark: overviewRulerCommentingRangeForeground, hcLight: overviewRulerCommentingRangeForeground }, nls.localize('editorOverviewRuler.commentForeground', 'Editor overview ruler decoration color for resolved comments. This color should be opaque.'));
|
||||
const overviewRulerCommentUnresolvedForeground = registerColor('editorOverviewRuler.commentUnresolvedForeground', { dark: overviewRulerCommentForeground, light: overviewRulerCommentForeground, hcDark: overviewRulerCommentForeground, hcLight: overviewRulerCommentForeground }, nls.localize('editorOverviewRuler.commentUnresolvedForeground', 'Editor overview ruler decoration color for unresolved comments. This color should be opaque.'));
|
||||
|
||||
const editorGutterCommentGlyphForeground = registerColor('editorGutter.commentGlyphForeground', { dark: editorForeground, light: editorForeground, hcDark: Color.black, hcLight: Color.white }, nls.localize('editorGutterCommentGlyphForeground', 'Editor gutter decoration color for commenting glyphs.'));
|
||||
registerColor('editorGutter.commentUnresolvedGlyphForeground', { dark: editorGutterCommentGlyphForeground, light: editorGutterCommentGlyphForeground, hcDark: editorGutterCommentGlyphForeground, hcLight: editorGutterCommentGlyphForeground }, nls.localize('editorGutterCommentUnresolvedGlyphForeground', 'Editor gutter decoration color for commenting glyphs for unresolved comment threads.'));
|
||||
|
||||
|
@ -33,15 +36,16 @@ export class CommentGlyphWidget {
|
|||
}
|
||||
|
||||
private createDecorationOptions(): ModelDecorationOptions {
|
||||
const unresolved = this._threadState === CommentThreadState.Unresolved;
|
||||
const decorationOptions: IModelDecorationOptions = {
|
||||
description: CommentGlyphWidget.description,
|
||||
isWholeLine: true,
|
||||
overviewRuler: {
|
||||
color: themeColorFromId(overviewRulerCommentingRangeForeground),
|
||||
color: themeColorFromId(unresolved ? overviewRulerCommentUnresolvedForeground : overviewRulerCommentForeground),
|
||||
position: OverviewRulerLane.Center
|
||||
},
|
||||
collapseOnReplaceEdit: true,
|
||||
linesDecorationsClassName: `comment-range-glyph comment-thread${this._threadState === CommentThreadState.Unresolved ? '-unresolved' : ''}`
|
||||
linesDecorationsClassName: `comment-range-glyph comment-thread${unresolved ? '-unresolved' : ''}`
|
||||
};
|
||||
|
||||
return ModelDecorationOptions.createDynamic(decorationOptions);
|
||||
|
|
|
@ -148,7 +148,7 @@ export class InteractiveEditor extends EditorPane {
|
|||
this.#notebookExecutionStateService = notebookExecutionStateService;
|
||||
this.#extensionService = extensionService;
|
||||
|
||||
this.#notebookOptions = new NotebookOptions(configurationService, notebookExecutionStateService, { cellToolbarInteraction: 'hover', globalToolbar: true, dragAndDropEnabled: false });
|
||||
this.#notebookOptions = new NotebookOptions(configurationService, notebookExecutionStateService, true, { cellToolbarInteraction: 'hover', globalToolbar: true, dragAndDropEnabled: false });
|
||||
this.#editorMemento = this.getEditorMemento<InteractiveEditorViewState>(editorGroupService, textResourceConfigurationService, INTERACTIVE_EDITOR_VIEW_STATE_PREFERENCE_KEY);
|
||||
|
||||
codeEditorService.registerDecorationType('interactive-decoration', DECORATION_KEY, {});
|
||||
|
|
|
@ -157,7 +157,7 @@ export class ArrowOutUpAction extends AbstractInteractiveEditorAction {
|
|||
super({
|
||||
id: 'interactiveEditor.arrowOutUp',
|
||||
title: localize('arrowUp', 'Cursor Up'),
|
||||
precondition: ContextKeyExpr.and(CTX_INTERACTIVE_EDITOR_FOCUSED, CTX_INTERACTIVE_EDITOR_INNER_CURSOR_FIRST, CTX_INTERACTIVE_EDITOR_EDIT_MODE.notEqualsTo(EditMode.LivePreview)),
|
||||
precondition: ContextKeyExpr.and(CTX_INTERACTIVE_EDITOR_FOCUSED, CTX_INTERACTIVE_EDITOR_INNER_CURSOR_FIRST, EditorContextKeys.isEmbeddedDiffEditor.negate()),
|
||||
keybinding: {
|
||||
weight: KeybindingWeight.EditorCore,
|
||||
primary: KeyCode.UpArrow
|
||||
|
@ -175,7 +175,7 @@ export class ArrowOutDownAction extends AbstractInteractiveEditorAction {
|
|||
super({
|
||||
id: 'interactiveEditor.arrowOutDown',
|
||||
title: localize('arrowDown', 'Cursor Down'),
|
||||
precondition: ContextKeyExpr.and(CTX_INTERACTIVE_EDITOR_FOCUSED, CTX_INTERACTIVE_EDITOR_INNER_CURSOR_LAST, CTX_INTERACTIVE_EDITOR_EDIT_MODE.notEqualsTo(EditMode.LivePreview)),
|
||||
precondition: ContextKeyExpr.and(CTX_INTERACTIVE_EDITOR_FOCUSED, CTX_INTERACTIVE_EDITOR_INNER_CURSOR_LAST, EditorContextKeys.isEmbeddedDiffEditor.negate()),
|
||||
keybinding: {
|
||||
weight: KeybindingWeight.EditorCore,
|
||||
primary: KeyCode.DownArrow
|
||||
|
@ -198,11 +198,11 @@ export class FocusInteractiveEditor extends EditorAction2 {
|
|||
precondition: ContextKeyExpr.and(EditorContextKeys.editorTextFocus, CTX_INTERACTIVE_EDITOR_VISIBLE, CTX_INTERACTIVE_EDITOR_FOCUSED.negate()),
|
||||
keybinding: [{
|
||||
weight: KeybindingWeight.EditorCore + 10, // win against core_command
|
||||
when: ContextKeyExpr.and(CTX_INTERACTIVE_EDITOR_OUTER_CURSOR_POSITION.isEqualTo('above'), CTX_INTERACTIVE_EDITOR_EDIT_MODE.notEqualsTo(EditMode.LivePreview)),
|
||||
when: ContextKeyExpr.and(CTX_INTERACTIVE_EDITOR_OUTER_CURSOR_POSITION.isEqualTo('above'), EditorContextKeys.isEmbeddedDiffEditor.negate()),
|
||||
primary: KeyCode.DownArrow,
|
||||
}, {
|
||||
weight: KeybindingWeight.EditorCore + 10, // win against core_command
|
||||
when: ContextKeyExpr.and(CTX_INTERACTIVE_EDITOR_OUTER_CURSOR_POSITION.isEqualTo('below'), CTX_INTERACTIVE_EDITOR_EDIT_MODE.notEqualsTo(EditMode.LivePreview)),
|
||||
when: ContextKeyExpr.and(CTX_INTERACTIVE_EDITOR_OUTER_CURSOR_POSITION.isEqualTo('below'), EditorContextKeys.isEmbeddedDiffEditor.negate()),
|
||||
primary: KeyCode.UpArrow,
|
||||
}]
|
||||
});
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -6,7 +6,7 @@
|
|||
import { Dimension, h } from 'vs/base/browser/dom';
|
||||
import { DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle';
|
||||
import { assertType } from 'vs/base/common/types';
|
||||
import { IActiveCodeEditor, ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { EmbeddedCodeEditorWidget, EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
|
@ -43,7 +43,7 @@ export class InteractiveEditorLivePreviewWidget extends ZoneWidget {
|
|||
private _dim: Dimension | undefined;
|
||||
|
||||
constructor(
|
||||
editor: IActiveCodeEditor,
|
||||
editor: ICodeEditor,
|
||||
private readonly _textModelv0: ITextModel,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
|
@ -51,6 +51,7 @@ export class InteractiveEditorLivePreviewWidget extends ZoneWidget {
|
|||
) {
|
||||
super(editor, { showArrow: false, showFrame: false, isResizeable: false, isAccessible: true, allowUnlimitedHeight: true, showInHiddenAreas: true, ordinal: 10000 + 1 });
|
||||
super.create();
|
||||
assertType(editor.hasModel());
|
||||
|
||||
this._inlineDiffDecorations = editor.createDecorationsCollection();
|
||||
|
||||
|
@ -73,6 +74,7 @@ export class InteractiveEditorLivePreviewWidget extends ZoneWidget {
|
|||
diffCodeLens: false,
|
||||
stickyScroll: { enabled: false },
|
||||
minimap: { enabled: false },
|
||||
isInEmbeddedEditor: true
|
||||
}, {
|
||||
originalEditor: { contributions: diffContributions },
|
||||
modifiedEditor: { contributions: diffContributions }
|
||||
|
@ -350,7 +352,13 @@ export class InteractiveEditorFileCreatePreviewWidget extends ZoneWidget {
|
|||
super.create();
|
||||
|
||||
this._title = instaService.createInstance(ResourceLabel, this._elements.title, { supportIcons: true });
|
||||
this._previewEditor = instaService.createInstance(EmbeddedCodeEditorWidget, this._elements.editor, { scrollBeyondLastLine: false, stickyScroll: { enabled: false }, readOnly: true, minimap: { enabled: false } }, { isSimpleWidget: true, contributions: [] }, parentEditor);
|
||||
this._previewEditor = instaService.createInstance(EmbeddedCodeEditorWidget, this._elements.editor, {
|
||||
scrollBeyondLastLine: false,
|
||||
stickyScroll: { enabled: false },
|
||||
readOnly: true,
|
||||
minimap: { enabled: false },
|
||||
scrollbar: { alwaysConsumeMouseWheel: false },
|
||||
}, { isSimpleWidget: true, contributions: [] }, parentEditor);
|
||||
|
||||
const doStyle = () => {
|
||||
const theme = themeService.getColorTheme();
|
||||
|
|
|
@ -5,15 +5,25 @@
|
|||
|
||||
import { isEqual } from 'vs/base/common/resources';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { ResourceEdit, ResourceFileEdit, ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService';
|
||||
import { TextEdit } from 'vs/editor/common/languages';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { EditMode, IInteractiveEditorSessionProvider, IInteractiveEditorSession, IInteractiveEditorBulkEditResponse, IInteractiveEditorEditResponse, IInteractiveEditorMessageResponse, IInteractiveEditorResponse } from 'vs/workbench/contrib/interactiveEditor/common/interactiveEditor';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { EditMode, IInteractiveEditorSessionProvider, IInteractiveEditorSession, IInteractiveEditorBulkEditResponse, IInteractiveEditorEditResponse, IInteractiveEditorMessageResponse, IInteractiveEditorResponse, IInteractiveEditorService } from 'vs/workbench/contrib/interactiveEditor/common/interactiveEditor';
|
||||
import { IRange, Range } from 'vs/editor/common/core/range';
|
||||
import { IActiveCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ResourceMap } from 'vs/base/common/map';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IModelService } from 'vs/editor/common/services/model';
|
||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { Iterable } from 'vs/base/common/iterator';
|
||||
import { toErrorMessage } from 'vs/base/common/errorMessage';
|
||||
import { isCancellationError } from 'vs/base/common/errors';
|
||||
|
||||
export type Recording = {
|
||||
when: Date;
|
||||
|
@ -45,16 +55,19 @@ type TelemetryDataClassification = {
|
|||
|
||||
export class Session {
|
||||
|
||||
private _lastInput: string | undefined;
|
||||
private readonly _exchange: SessionExchange[] = [];
|
||||
private readonly _startTime = new Date();
|
||||
private readonly _teldata: Partial<TelemetryData>;
|
||||
|
||||
constructor(
|
||||
readonly editMode: EditMode,
|
||||
readonly model0: ITextModel,
|
||||
readonly modelN: ITextModel,
|
||||
readonly editor: ICodeEditor,
|
||||
readonly textModel0: ITextModel,
|
||||
readonly textModelN: ITextModel,
|
||||
readonly provider: IInteractiveEditorSessionProvider,
|
||||
readonly session: IInteractiveEditorSession,
|
||||
private readonly _wholeRangeMarkerId: string
|
||||
) {
|
||||
this._teldata = {
|
||||
extension: provider.debugName,
|
||||
|
@ -66,6 +79,19 @@ export class Session {
|
|||
};
|
||||
}
|
||||
|
||||
addInput(input: string): void {
|
||||
this._lastInput = input;
|
||||
}
|
||||
|
||||
get lastInput() {
|
||||
return this._lastInput;
|
||||
}
|
||||
|
||||
get wholeRange(): Range {
|
||||
return this.textModelN.getDecorationRange(this._wholeRangeMarkerId)!;
|
||||
// return new Range(1, 1, 1, 1);
|
||||
}
|
||||
|
||||
addExchange(exchange: SessionExchange): void {
|
||||
const newLen = this._exchange.push(exchange);
|
||||
this._teldata.rounds += `${newLen}|`;
|
||||
|
@ -87,11 +113,18 @@ export class Session {
|
|||
}
|
||||
|
||||
asRecording(): Recording {
|
||||
return {
|
||||
const result: Recording = {
|
||||
session: this.session,
|
||||
when: this._startTime,
|
||||
exchanges: this._exchange.map(e => ({ prompt: e.prompt, res: e.response.raw }))
|
||||
exchanges: []
|
||||
};
|
||||
for (const exchange of this._exchange) {
|
||||
const response = exchange.response;
|
||||
if (response instanceof MarkdownResponse || response instanceof EditResponse) {
|
||||
result.exchanges.push({ prompt: exchange.prompt, res: response.raw });
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,10 +132,27 @@ export class Session {
|
|||
export class SessionExchange {
|
||||
constructor(
|
||||
readonly prompt: string,
|
||||
readonly response: MarkdownResponse | EditResponse
|
||||
readonly response: MarkdownResponse | EditResponse | EmptyResponse | ErrorResponse
|
||||
) { }
|
||||
}
|
||||
|
||||
export class EmptyResponse {
|
||||
|
||||
}
|
||||
|
||||
export class ErrorResponse {
|
||||
|
||||
readonly message: string;
|
||||
readonly isCancellation: boolean;
|
||||
|
||||
constructor(
|
||||
readonly error: any
|
||||
) {
|
||||
this.message = toErrorMessage(error, false);
|
||||
this.isCancellation = isCancellationError(error);
|
||||
}
|
||||
}
|
||||
|
||||
export class MarkdownResponse {
|
||||
constructor(
|
||||
readonly localUri: URI,
|
||||
|
@ -171,47 +221,111 @@ export const IInteractiveEditorSessionService = createDecorator<IInteractiveEdit
|
|||
export interface IInteractiveEditorSessionService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
retrieveSession(editor: ICodeEditor, uri: URI): Session | undefined;
|
||||
createSession(editor: IActiveCodeEditor, options: { editMode: EditMode; wholeRange?: IRange }, token: CancellationToken): Promise<Session | undefined>;
|
||||
|
||||
storeSession(editor: ICodeEditor, uri: URI, session: Session): void;
|
||||
getSession(editor: ICodeEditor, uri: URI): Session | undefined;
|
||||
|
||||
releaseSession(editor: ICodeEditor, uri: URI, session: Session): void;
|
||||
releaseSession(session: Session): void;
|
||||
|
||||
//
|
||||
|
||||
recordings(): readonly Recording[];
|
||||
}
|
||||
|
||||
type SessionData = {
|
||||
session: Session;
|
||||
store: IDisposable;
|
||||
};
|
||||
|
||||
export class InteractiveEditorSessionService implements IInteractiveEditorSessionService {
|
||||
|
||||
declare _serviceBrand: undefined;
|
||||
|
||||
private readonly _sessions = new Map<ICodeEditor, ResourceMap<Session>>();
|
||||
private readonly _sessions = new Map<ICodeEditor, ResourceMap<SessionData>>();
|
||||
private _recordings: Recording[] = [];
|
||||
|
||||
constructor(
|
||||
@IInteractiveEditorService private readonly _interactiveEditorService: IInteractiveEditorService,
|
||||
@ITelemetryService private readonly _telemetryService: ITelemetryService,
|
||||
@IModelService private readonly _modelService: IModelService,
|
||||
@ITextModelService private readonly _textModelService: ITextModelService,
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
) { }
|
||||
|
||||
storeSession(editor: ICodeEditor, uri: URI, session: Session): void {
|
||||
|
||||
async createSession(editor: IActiveCodeEditor, options: { editMode: EditMode; wholeRange?: Range }, token: CancellationToken): Promise<Session | undefined> {
|
||||
|
||||
const provider = Iterable.first(this._interactiveEditorService.getAllProvider());
|
||||
if (!provider) {
|
||||
this._logService.trace('[IE] NO provider found');
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const textModel = editor.getModel();
|
||||
const selection = editor.getSelection();
|
||||
const raw = await provider.prepareInteractiveEditorSession(textModel, selection, token);
|
||||
if (!raw) {
|
||||
this._logService.trace('[IE] NO session', provider.debugName);
|
||||
return undefined;
|
||||
}
|
||||
this._logService.trace('[IE] NEW session', provider.debugName);
|
||||
|
||||
this._logService.trace(`[IE] creating NEW session for ${editor.getId()}, ${provider.debugName}`);
|
||||
const store = new DisposableStore();
|
||||
|
||||
// create: keep a reference to prevent disposal of the "actual" model
|
||||
const refTextModelN = await this._textModelService.createModelReference(textModel.uri);
|
||||
store.add(refTextModelN);
|
||||
|
||||
// create: keep a snapshot of the "actual" model
|
||||
const textModel0 = this._modelService.createModel(
|
||||
createTextBufferFactoryFromSnapshot(textModel.createSnapshot()),
|
||||
{ languageId: textModel.getLanguageId(), onDidChange: Event.None },
|
||||
undefined, true
|
||||
);
|
||||
store.add(textModel0);
|
||||
|
||||
let wholeRange = options.wholeRange;
|
||||
if (!wholeRange) {
|
||||
wholeRange = raw.wholeRange ? Range.lift(raw.wholeRange) : editor.getSelection();
|
||||
}
|
||||
if (Range.isEmpty(wholeRange)) {
|
||||
wholeRange = new Range(wholeRange.startLineNumber, 1, wholeRange.endLineNumber, textModel.getLineMaxColumn(wholeRange.endLineNumber));
|
||||
}
|
||||
|
||||
// install a marker for the decoration range
|
||||
const [wholeRangeDecorationId] = textModel.deltaDecorations([], [{ range: wholeRange, options: { description: 'interactiveEditor/session/wholeRange' } }]);
|
||||
store.add(toDisposable(() => textModel.deltaDecorations([wholeRangeDecorationId], [])));
|
||||
|
||||
const session = new Session(options.editMode, editor, textModel0, textModel, provider, raw, wholeRangeDecorationId);
|
||||
|
||||
// store: editor -> uri -> session
|
||||
let map = this._sessions.get(editor);
|
||||
if (!map) {
|
||||
map = new ResourceMap<Session>();
|
||||
map = new ResourceMap<SessionData>();
|
||||
this._sessions.set(editor, map);
|
||||
}
|
||||
if (map.has(uri)) {
|
||||
throw new Error(`Session already stored for ${uri}`);
|
||||
if (map.has(textModel.uri)) {
|
||||
throw new Error(`Session already stored for ${textModel.uri}`);
|
||||
}
|
||||
map.set(uri, session);
|
||||
map.set(textModel.uri, { session, store });
|
||||
return session;
|
||||
}
|
||||
|
||||
releaseSession(editor: ICodeEditor, uri: URI, session: Session): void {
|
||||
releaseSession(session: Session): void {
|
||||
|
||||
const { editor, textModelN } = session;
|
||||
|
||||
// cleanup
|
||||
const map = this._sessions.get(editor);
|
||||
if (map) {
|
||||
map.delete(uri);
|
||||
const data = map.get(textModelN.uri);
|
||||
if (data) {
|
||||
data.store.dispose();
|
||||
data.session.session.dispose?.();
|
||||
|
||||
map.delete(textModelN.uri);
|
||||
}
|
||||
if (map.size === 0) {
|
||||
this._sessions.delete(editor);
|
||||
}
|
||||
|
@ -227,8 +341,8 @@ export class InteractiveEditorSessionService implements IInteractiveEditorSessio
|
|||
this._telemetryService.publicLog2<TelemetryData, TelemetryDataClassification>('interactiveEditor/session', session.asTelemetryData());
|
||||
}
|
||||
|
||||
retrieveSession(editor: ICodeEditor, uri: URI): Session | undefined {
|
||||
return this._sessions.get(editor)?.get(uri);
|
||||
getSession(editor: ICodeEditor, uri: URI): Session | undefined {
|
||||
return this._sessions.get(editor)?.get(uri)?.session;
|
||||
}
|
||||
|
||||
// --- debug
|
||||
|
|
|
@ -0,0 +1,364 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import 'vs/css!./interactiveEditor';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
|
||||
import { EditOperation, ISingleEditOperation } from 'vs/editor/common/core/editOperation';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { LineRangeMapping } from 'vs/editor/common/diff/linesDiffComputer';
|
||||
import { IEditorDecorationsCollection } from 'vs/editor/common/editorCommon';
|
||||
import { ICursorStateComputer, IModelDecorationOptions, IModelDeltaDecoration, IValidEditOperation } from 'vs/editor/common/model';
|
||||
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { InteractiveEditorFileCreatePreviewWidget, InteractiveEditorLivePreviewWidget } from 'vs/workbench/contrib/interactiveEditor/browser/interactiveEditorLivePreviewWidget';
|
||||
import { EditResponse, Session } from 'vs/workbench/contrib/interactiveEditor/browser/interactiveEditorSession';
|
||||
import { InteractiveEditorWidget } from 'vs/workbench/contrib/interactiveEditor/browser/interactiveEditorWidget';
|
||||
import { CTX_INTERACTIVE_EDITOR_INLNE_DIFF, CTX_INTERACTIVE_EDITOR_DOCUMENT_CHANGED } from 'vs/workbench/contrib/interactiveEditor/common/interactiveEditor';
|
||||
|
||||
export abstract class EditModeStrategy {
|
||||
|
||||
dispose(): void { }
|
||||
|
||||
abstract checkChanges(response: EditResponse): boolean;
|
||||
|
||||
abstract apply(): Promise<void>;
|
||||
|
||||
abstract cancel(): Promise<void>;
|
||||
|
||||
abstract renderChanges(response: EditResponse, edits: ISingleEditOperation[], changes: LineRangeMapping[]): Promise<void>;
|
||||
|
||||
abstract hide(): Promise<void>;
|
||||
|
||||
abstract toggleInlineDiff(): void;
|
||||
}
|
||||
|
||||
export class PreviewStrategy extends EditModeStrategy {
|
||||
|
||||
private readonly _ctxDocumentChanged: IContextKey<boolean>;
|
||||
private readonly _listener: IDisposable;
|
||||
|
||||
constructor(
|
||||
private readonly _session: Session,
|
||||
private readonly _widget: InteractiveEditorWidget,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IBulkEditService private readonly _bulkEditService: IBulkEditService,
|
||||
) {
|
||||
super();
|
||||
|
||||
this._ctxDocumentChanged = CTX_INTERACTIVE_EDITOR_DOCUMENT_CHANGED.bindTo(contextKeyService);
|
||||
this._listener = Event.debounce(_session.textModelN.onDidChangeContent.bind(_session.textModelN), () => { }, 350)(_ => {
|
||||
this._ctxDocumentChanged.set(!_session.textModelN.equalsTextBuffer(_session.textModel0.getTextBuffer()));
|
||||
});
|
||||
}
|
||||
|
||||
override dispose(): void {
|
||||
this._listener.dispose();
|
||||
this._ctxDocumentChanged.reset();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
checkChanges(response: EditResponse): boolean {
|
||||
if (!response.workspaceEdits || response.singleCreateFileEdit) {
|
||||
// preview stategy can handle simple workspace edit (single file create)
|
||||
return true;
|
||||
}
|
||||
this._bulkEditService.apply(response.workspaceEdits, { showPreview: true });
|
||||
return false;
|
||||
}
|
||||
|
||||
async apply() {
|
||||
|
||||
if (!(this._session.lastExchange?.response instanceof EditResponse)) {
|
||||
return;
|
||||
}
|
||||
const editResponse = this._session.lastExchange?.response;
|
||||
if (editResponse.workspaceEdits) {
|
||||
await this._bulkEditService.apply(editResponse.workspaceEdits);
|
||||
|
||||
} else if (!editResponse.workspaceEditsIncludeLocalEdits) {
|
||||
|
||||
const { textModelN: modelN } = this._session;
|
||||
|
||||
if (modelN.equalsTextBuffer(this._session.textModel0.getTextBuffer())) {
|
||||
modelN.pushStackElement();
|
||||
const edits = editResponse.localEdits.map(edit => EditOperation.replace(Range.lift(edit.range), edit.text));
|
||||
modelN.pushEditOperations(null, edits, () => null);
|
||||
modelN.pushStackElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override async hide(): Promise<void> {
|
||||
// nothing to do, input widget will be hidden by controller
|
||||
}
|
||||
|
||||
async cancel(): Promise<void> {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
override async renderChanges(response: EditResponse, edits: ISingleEditOperation[], changes: LineRangeMapping[]): Promise<void> {
|
||||
if (response.localEdits.length > 0) {
|
||||
this._widget.showEditsPreview(this._session.textModel0, edits, changes);
|
||||
} else {
|
||||
this._widget.hideEditsPreview();
|
||||
}
|
||||
|
||||
if (response.singleCreateFileEdit) {
|
||||
this._widget.showCreatePreview(response.singleCreateFileEdit.uri, await Promise.all(response.singleCreateFileEdit.edits));
|
||||
} else {
|
||||
this._widget.hideCreatePreview();
|
||||
}
|
||||
}
|
||||
|
||||
toggleInlineDiff(): void { }
|
||||
}
|
||||
|
||||
class InlineDiffDecorations {
|
||||
|
||||
private readonly _collection: IEditorDecorationsCollection;
|
||||
|
||||
private _data: { tracking: IModelDeltaDecoration; decorating: IModelDecorationOptions }[] = [];
|
||||
private _visible: boolean = false;
|
||||
|
||||
constructor(editor: ICodeEditor, visible: boolean = false) {
|
||||
this._collection = editor.createDecorationsCollection();
|
||||
this._visible = visible;
|
||||
}
|
||||
|
||||
get visible() {
|
||||
return this._visible;
|
||||
}
|
||||
|
||||
set visible(value: boolean) {
|
||||
this._visible = value;
|
||||
this.update();
|
||||
}
|
||||
|
||||
clear() {
|
||||
this._collection.clear();
|
||||
this._data.length = 0;
|
||||
}
|
||||
|
||||
collectEditOperation(op: IValidEditOperation) {
|
||||
this._data.push(InlineDiffDecorations._asDecorationData(op));
|
||||
}
|
||||
|
||||
update() {
|
||||
this._collection.set(this._data.map(d => {
|
||||
const res = { ...d.tracking };
|
||||
if (this._visible) {
|
||||
res.options = { ...res.options, ...d.decorating };
|
||||
}
|
||||
return res;
|
||||
}));
|
||||
}
|
||||
|
||||
private static _asDecorationData(edit: IValidEditOperation): { tracking: IModelDeltaDecoration; decorating: IModelDecorationOptions } {
|
||||
let content = edit.text;
|
||||
if (content.length > 12) {
|
||||
content = content.substring(0, 12) + '…';
|
||||
}
|
||||
const tracking: IModelDeltaDecoration = {
|
||||
range: edit.range,
|
||||
options: {
|
||||
description: 'interactive-editor-inline-diff',
|
||||
}
|
||||
};
|
||||
|
||||
const decorating: IModelDecorationOptions = {
|
||||
description: 'interactive-editor-inline-diff',
|
||||
className: !edit.range.isEmpty() ? 'interactive-editor-lines-inserted-range' : undefined,
|
||||
showIfCollapsed: true,
|
||||
before: {
|
||||
content,
|
||||
inlineClassName: 'interactive-editor-lines-deleted-range-inline',
|
||||
attachedData: edit,
|
||||
}
|
||||
};
|
||||
|
||||
return { tracking, decorating };
|
||||
}
|
||||
}
|
||||
|
||||
export class LiveStrategy extends EditModeStrategy {
|
||||
|
||||
private static _inlineDiffStorageKey: string = 'interactiveEditor.storage.inlineDiff';
|
||||
private _inlineDiffEnabled: boolean = false;
|
||||
|
||||
private readonly _inlineDiffDecorations: InlineDiffDecorations;
|
||||
private readonly _ctxInlineDiff: IContextKey<boolean>;
|
||||
private _lastResponse?: EditResponse;
|
||||
|
||||
constructor(
|
||||
protected readonly _session: Session,
|
||||
protected readonly _editor: ICodeEditor,
|
||||
protected readonly _widget: InteractiveEditorWidget,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IStorageService protected _storageService: IStorageService,
|
||||
@IBulkEditService protected readonly _bulkEditService: IBulkEditService,
|
||||
@IEditorWorkerService protected readonly _editorWorkerService: IEditorWorkerService
|
||||
) {
|
||||
super();
|
||||
this._inlineDiffDecorations = new InlineDiffDecorations(this._editor, this._inlineDiffEnabled);
|
||||
this._ctxInlineDiff = CTX_INTERACTIVE_EDITOR_INLNE_DIFF.bindTo(contextKeyService);
|
||||
|
||||
this._inlineDiffEnabled = _storageService.getBoolean(LiveStrategy._inlineDiffStorageKey, StorageScope.PROFILE, false);
|
||||
this._ctxInlineDiff.set(this._inlineDiffEnabled);
|
||||
this._inlineDiffDecorations.visible = this._inlineDiffEnabled;
|
||||
}
|
||||
|
||||
override dispose(): void {
|
||||
this._inlineDiffEnabled = this._inlineDiffDecorations.visible;
|
||||
this._storageService.store(LiveStrategy._inlineDiffStorageKey, this._inlineDiffEnabled, StorageScope.PROFILE, StorageTarget.USER);
|
||||
this._inlineDiffDecorations.clear();
|
||||
this._ctxInlineDiff.reset();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
toggleInlineDiff(): void {
|
||||
this._inlineDiffEnabled = !this._inlineDiffEnabled;
|
||||
this._ctxInlineDiff.set(this._inlineDiffEnabled);
|
||||
this._inlineDiffDecorations.visible = this._inlineDiffEnabled;
|
||||
this._storageService.store(LiveStrategy._inlineDiffStorageKey, this._inlineDiffEnabled, StorageScope.PROFILE, StorageTarget.USER);
|
||||
}
|
||||
|
||||
checkChanges(response: EditResponse): boolean {
|
||||
this._lastResponse = response;
|
||||
if (response.singleCreateFileEdit) {
|
||||
// preview stategy can handle simple workspace edit (single file create)
|
||||
return true;
|
||||
}
|
||||
if (response.workspaceEdits) {
|
||||
this._bulkEditService.apply(response.workspaceEdits, { showPreview: true });
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async apply() {
|
||||
if (this._lastResponse?.workspaceEdits) {
|
||||
await this._bulkEditService.apply(this._lastResponse.workspaceEdits);
|
||||
}
|
||||
}
|
||||
|
||||
override async hide(): Promise<void> {
|
||||
this._inlineDiffDecorations.clear();
|
||||
}
|
||||
|
||||
async cancel() {
|
||||
const { textModelN: modelN, textModel0: model0 } = this._session;
|
||||
if (modelN.isDisposed() || model0.isDisposed()) {
|
||||
return;
|
||||
}
|
||||
const edits = await this._editorWorkerService.computeMoreMinimalEdits(modelN.uri, [{ range: modelN.getFullModelRange(), text: model0.getValue() }]);
|
||||
if (edits) {
|
||||
const operations = edits.map(e => EditOperation.replace(Range.lift(e.range), e.text));
|
||||
modelN.pushEditOperations(null, operations, () => null);
|
||||
}
|
||||
}
|
||||
|
||||
override async renderChanges(response: EditResponse, edits: ISingleEditOperation[], textModel0Changes: LineRangeMapping[]) {
|
||||
|
||||
const cursorStateComputerAndInlineDiffCollection: ICursorStateComputer = (undoEdits) => {
|
||||
let last: Position | null = null;
|
||||
for (const edit of undoEdits) {
|
||||
last = !last || last.isBefore(edit.range.getEndPosition()) ? edit.range.getEndPosition() : last;
|
||||
this._inlineDiffDecorations.collectEditOperation(edit);
|
||||
}
|
||||
return last && [Selection.fromPositions(last)];
|
||||
};
|
||||
|
||||
this._editor.pushUndoStop();
|
||||
this._editor.executeEdits('interactive-editor-live', edits, cursorStateComputerAndInlineDiffCollection);
|
||||
this._editor.pushUndoStop();
|
||||
this._inlineDiffDecorations.update();
|
||||
this._updateSummaryMessage(textModel0Changes);
|
||||
|
||||
if (response.singleCreateFileEdit) {
|
||||
this._widget.showCreatePreview(response.singleCreateFileEdit.uri, await Promise.all(response.singleCreateFileEdit.edits));
|
||||
} else {
|
||||
this._widget.hideCreatePreview();
|
||||
}
|
||||
}
|
||||
|
||||
protected _updateSummaryMessage(textModel0Changes: LineRangeMapping[]) {
|
||||
let linesChanged = 0;
|
||||
if (textModel0Changes) {
|
||||
for (const change of textModel0Changes) {
|
||||
linesChanged += change.changedLineCount;
|
||||
}
|
||||
}
|
||||
let message: string;
|
||||
if (linesChanged === 0) {
|
||||
message = localize('lines.0', "Generated reply");
|
||||
} else if (linesChanged === 1) {
|
||||
message = localize('lines.1', "Generated reply and changed 1 line");
|
||||
} else {
|
||||
message = localize('lines.N', "Generated reply and changed {0} lines", linesChanged);
|
||||
}
|
||||
this._widget.updateStatus(message);
|
||||
}
|
||||
}
|
||||
|
||||
export class LivePreviewStrategy extends LiveStrategy {
|
||||
|
||||
private readonly _diffZone: InteractiveEditorLivePreviewWidget;
|
||||
private readonly _previewZone: InteractiveEditorFileCreatePreviewWidget;
|
||||
|
||||
constructor(
|
||||
session: Session,
|
||||
editor: ICodeEditor,
|
||||
widget: InteractiveEditorWidget,
|
||||
private _getWholeRange: () => Range,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
@IBulkEditService bulkEditService: IBulkEditService,
|
||||
@IEditorWorkerService editorWorkerService: IEditorWorkerService,
|
||||
@IInstantiationService instaService: IInstantiationService,
|
||||
) {
|
||||
super(session, editor, widget, contextKeyService, storageService, bulkEditService, editorWorkerService);
|
||||
|
||||
this._diffZone = instaService.createInstance(InteractiveEditorLivePreviewWidget, editor, session.textModel0);
|
||||
this._previewZone = instaService.createInstance(InteractiveEditorFileCreatePreviewWidget, editor);
|
||||
}
|
||||
|
||||
override dispose(): void {
|
||||
this._diffZone.hide();
|
||||
this._diffZone.dispose();
|
||||
this._previewZone.hide();
|
||||
this._previewZone.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
override async hide(): Promise<void> {
|
||||
this._diffZone.hide();
|
||||
super.hide();
|
||||
}
|
||||
|
||||
override async renderChanges(response: EditResponse, edits: ISingleEditOperation[], changes: LineRangeMapping[]) {
|
||||
|
||||
this._editor.pushUndoStop();
|
||||
this._editor.executeEdits('interactive-editor-livePreview', edits);
|
||||
this._editor.pushUndoStop();
|
||||
|
||||
this._diffZone.showDiff(() => this._getWholeRange(), changes);
|
||||
this._updateSummaryMessage(changes);
|
||||
|
||||
if (response.singleCreateFileEdit) {
|
||||
this._previewZone.showCreation(this._getWholeRange(), response.singleCreateFileEdit.uri, await Promise.all(response.singleCreateFileEdit.edits));
|
||||
} else {
|
||||
this._previewZone.hide();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -31,7 +31,7 @@ import { DEFAULT_FONT_FAMILY } from 'vs/workbench/browser/style';
|
|||
import { createActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
|
||||
import { CompletionItem, CompletionItemInsertTextRule, CompletionItemKind, CompletionItemProvider, CompletionList, ProviderResult, TextEdit } from 'vs/editor/common/languages';
|
||||
import { EditOperation, ISingleEditOperation } from 'vs/editor/common/core/editOperation';
|
||||
import { ILanguageSelection } from 'vs/editor/common/languages/language';
|
||||
import { ILanguageSelection, ILanguageService } from 'vs/editor/common/languages/language';
|
||||
import { ResourceLabel } from 'vs/workbench/browser/labels';
|
||||
import { FileKind } from 'vs/platform/files/common/files';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
|
@ -99,6 +99,7 @@ const _previewEditorEditorOptions: IDiffEditorConstructionOptions = {
|
|||
modifiedAriaLabel: localize('original', 'Original'),
|
||||
diffAlgorithm: 'advanced',
|
||||
readOnly: true,
|
||||
isInEmbeddedEditor: true
|
||||
};
|
||||
|
||||
export interface InteractiveEditorWidgetViewState {
|
||||
|
@ -167,6 +168,7 @@ export class InteractiveEditorWidget {
|
|||
constructor(
|
||||
parentEditor: ICodeEditor,
|
||||
@IModelService private readonly _modelService: IModelService,
|
||||
@ILanguageService private readonly _languageService: ILanguageService,
|
||||
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
|
||||
@ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
|
@ -326,13 +328,13 @@ export class InteractiveEditorWidget {
|
|||
const editorViewState = this._inputEditor.saveViewState();
|
||||
return {
|
||||
editorViewState,
|
||||
input: this.input,
|
||||
input: this.value,
|
||||
placeholder: this.placeholder
|
||||
};
|
||||
}
|
||||
|
||||
restoreViewState(state: InteractiveEditorWidgetViewState) {
|
||||
this.input = state.input;
|
||||
this.value = state.input;
|
||||
this.placeholder = state.placeholder;
|
||||
this._inputEditor.restoreViewState(state.editorViewState);
|
||||
}
|
||||
|
@ -345,11 +347,11 @@ export class InteractiveEditorWidget {
|
|||
}
|
||||
}
|
||||
|
||||
get input(): string {
|
||||
get value(): string {
|
||||
return this._inputModel.getValue();
|
||||
}
|
||||
|
||||
set input(value: string) {
|
||||
set value(value: string) {
|
||||
this._inputModel.setValue(value);
|
||||
this._inputEditor.setPosition(this._inputModel.getFullModelRange().getEndPosition());
|
||||
}
|
||||
|
@ -367,14 +369,18 @@ export class InteractiveEditorWidget {
|
|||
this._onDidChangeHeight.fire();
|
||||
}
|
||||
|
||||
updateMarkdownMessage(message: Node) {
|
||||
const messageDom = this._elements.message;
|
||||
reset(messageDom, message);
|
||||
this._elements.markdownMessage.classList.toggle('hidden', false);
|
||||
if (messageDom.scrollHeight > messageDom.clientHeight) {
|
||||
this._ctxMessageCropState.set('cropped');
|
||||
updateMarkdownMessage(message: Node | undefined) {
|
||||
this._elements.markdownMessage.classList.toggle('hidden', !message);
|
||||
if (!message) {
|
||||
reset(this._elements.message);
|
||||
|
||||
} else {
|
||||
this._ctxMessageCropState.set('not_cropped');
|
||||
reset(this._elements.message, message);
|
||||
if (this._elements.message.scrollHeight > this._elements.message.clientHeight) {
|
||||
this._ctxMessageCropState.set('cropped');
|
||||
} else {
|
||||
this._ctxMessageCropState.set('not_cropped');
|
||||
}
|
||||
}
|
||||
this._onDidChangeHeight.fire();
|
||||
}
|
||||
|
@ -387,8 +393,6 @@ export class InteractiveEditorWidget {
|
|||
setTimeout(() => {
|
||||
this.updateStatus(statusLabel, { classes, keepMessage: true });
|
||||
}, ops.resetAfter);
|
||||
} else if (!isTempMessage && !ops.keepMessage) {
|
||||
this._elements.markdownMessage.classList.toggle('hidden', true);
|
||||
}
|
||||
reset(this._elements.statusLabel, message);
|
||||
this._elements.statusLabel.className = `label ${(ops.classes ?? []).join(' ')}`;
|
||||
|
@ -403,6 +407,9 @@ export class InteractiveEditorWidget {
|
|||
|
||||
reset() {
|
||||
this._ctxInputEmpty.reset();
|
||||
this.value = '';
|
||||
this.updateMarkdownMessage(undefined);
|
||||
|
||||
reset(this._elements.statusLabel);
|
||||
this._elements.statusLabel.classList.toggle('hidden', true);
|
||||
this._elements.statusToolbar.classList.add('hidden');
|
||||
|
@ -424,6 +431,11 @@ export class InteractiveEditorWidget {
|
|||
// --- preview
|
||||
|
||||
showEditsPreview(textModelv0: ITextModel, edits: ISingleEditOperation[], changes: LineRangeMapping[]) {
|
||||
if (changes.length === 0) {
|
||||
this.hideEditsPreview();
|
||||
return;
|
||||
}
|
||||
|
||||
this._elements.previewDiff.classList.remove('hidden');
|
||||
|
||||
const languageSelection: ILanguageSelection = { languageId: textModelv0.getLanguageId(), onDidChange: Event.None };
|
||||
|
@ -445,8 +457,10 @@ export class InteractiveEditorWidget {
|
|||
modifiedLineRange = new LineRange(newStartLine, modifiedLineRange.endLineNumberExclusive);
|
||||
originalLineRange = new LineRange(newStartLine, originalLineRange.endLineNumberExclusive);
|
||||
|
||||
modifiedLineRange = new LineRange(modifiedLineRange.startLineNumber, modifiedLineRange.endLineNumberExclusive + pad);
|
||||
originalLineRange = new LineRange(originalLineRange.startLineNumber, originalLineRange.endLineNumberExclusive + pad);
|
||||
const newEndLineModified = Math.min(modifiedLineRange.endLineNumberExclusive + pad, modified.getLineCount());
|
||||
modifiedLineRange = new LineRange(modifiedLineRange.startLineNumber, newEndLineModified);
|
||||
const newEndLineOriginal = Math.min(originalLineRange.endLineNumberExclusive + pad, textModelv0.getLineCount());
|
||||
originalLineRange = new LineRange(originalLineRange.startLineNumber, newEndLineOriginal);
|
||||
|
||||
const hiddenOriginal = invertLineRange(originalLineRange, textModelv0);
|
||||
const hiddenModified = invertLineRange(modifiedLineRange, modified);
|
||||
|
@ -470,7 +484,8 @@ export class InteractiveEditorWidget {
|
|||
|
||||
this._previewCreateTitle.element.setFile(uri, { fileKind: FileKind.FILE });
|
||||
|
||||
const model = this._modelService.createModel('', null, undefined, true);
|
||||
const langSelection = this._languageService.createByFilepathOrFirstLine(uri, undefined);
|
||||
const model = this._modelService.createModel('', langSelection, undefined, true);
|
||||
model.applyEdits(edits.map(edit => EditOperation.replace(Range.lift(edit.range), edit.text)));
|
||||
this._previewCreateModel.value = model;
|
||||
this._previewCreateEditor.setModel(model);
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { isEqual } from 'vs/base/common/resources';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
import { localize } from 'vs/nls';
|
||||
|
@ -16,8 +18,10 @@ import { InputFocusedContextKey } from 'vs/platform/contextkey/common/contextkey
|
|||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { InteractiveEditorController } from 'vs/workbench/contrib/interactiveEditor/browser/interactiveEditorController';
|
||||
import { CTX_INTERACTIVE_EDITOR_FOCUSED, CTX_INTERACTIVE_EDITOR_INNER_CURSOR_LAST } from 'vs/workbench/contrib/interactiveEditor/common/interactiveEditor';
|
||||
import { INotebookActionContext, INotebookCellActionContext, NotebookAction, NotebookCellAction, NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT } from 'vs/workbench/contrib/notebook/browser/controller/coreActions';
|
||||
import { CellEditState } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { CellEditState, ICellViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { CellKind, NOTEBOOK_EDITOR_CURSOR_BOUNDARY } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_TYPE, NOTEBOOK_CURSOR_NAVIGATION_MODE, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_OUTPUT_FOCUSED } from 'vs/workbench/contrib/notebook/common/notebookContextKeys';
|
||||
|
||||
|
@ -33,6 +37,17 @@ const NOTEBOOK_CURSOR_PAGEUP_SELECT_COMMAND_ID = 'notebook.cell.cursorPageUpSele
|
|||
const NOTEBOOK_CURSOR_PAGEDOWN_COMMAND_ID = 'notebook.cell.cursorPageDown';
|
||||
const NOTEBOOK_CURSOR_PAGEDOWN_SELECT_COMMAND_ID = 'notebook.cell.cursorPageDownSelect';
|
||||
|
||||
function findTargetCellEditor(context: INotebookCellActionContext, targetCell: ICellViewModel) {
|
||||
let foundEditor: ICodeEditor | undefined = undefined;
|
||||
for (const [, codeEditor] of context.notebookEditor.codeEditors) {
|
||||
if (isEqual(codeEditor.getModel()?.uri, targetCell.uri)) {
|
||||
foundEditor = codeEditor;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return foundEditor;
|
||||
}
|
||||
|
||||
registerAction2(class FocusNextCellAction extends NotebookCellAction {
|
||||
constructor() {
|
||||
|
@ -51,6 +66,7 @@ registerAction2(class FocusNextCellAction extends NotebookCellAction {
|
|||
NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('top'),
|
||||
NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('none'),
|
||||
),
|
||||
EditorContextKeys.isEmbeddedDiffEditor.negate()
|
||||
),
|
||||
primary: KeyCode.DownArrow,
|
||||
weight: NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT, // code cell keybinding, focus inside editor: lower weight to not override suggest widget
|
||||
|
@ -63,7 +79,8 @@ registerAction2(class FocusNextCellAction extends NotebookCellAction {
|
|||
ContextKeyExpr.and(
|
||||
NOTEBOOK_CELL_TYPE.isEqualTo('markup'),
|
||||
NOTEBOOK_CELL_MARKDOWN_EDIT_MODE.isEqualTo(false),
|
||||
NOTEBOOK_CURSOR_NAVIGATION_MODE)
|
||||
NOTEBOOK_CURSOR_NAVIGATION_MODE),
|
||||
EditorContextKeys.isEmbeddedDiffEditor.negate()
|
||||
),
|
||||
primary: KeyCode.DownArrow,
|
||||
weight: KeybindingWeight.WorkbenchContrib, // markdown keybinding, focus on list: higher weight to override list.focusDown
|
||||
|
@ -73,6 +90,39 @@ registerAction2(class FocusNextCellAction extends NotebookCellAction {
|
|||
primary: KeyMod.CtrlCmd | KeyCode.DownArrow,
|
||||
mac: { primary: KeyMod.WinCtrl | KeyMod.CtrlCmd | KeyCode.DownArrow, },
|
||||
weight: KeybindingWeight.WorkbenchContrib
|
||||
},
|
||||
{
|
||||
when: ContextKeyExpr.and(
|
||||
NOTEBOOK_EDITOR_FOCUSED,
|
||||
CONTEXT_ACCESSIBILITY_MODE_ENABLED.negate(),
|
||||
ContextKeyExpr.equals('config.notebook.navigation.allowNavigateToSurroundingCells', true),
|
||||
ContextKeyExpr.and(
|
||||
ContextKeyExpr.has(InputFocusedContextKey),
|
||||
NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('top'),
|
||||
NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('none'),
|
||||
),
|
||||
CTX_INTERACTIVE_EDITOR_FOCUSED,
|
||||
CTX_INTERACTIVE_EDITOR_INNER_CURSOR_LAST,
|
||||
EditorContextKeys.isEmbeddedDiffEditor.negate()
|
||||
),
|
||||
primary: KeyCode.DownArrow,
|
||||
weight: KeybindingWeight.EditorCore
|
||||
},
|
||||
{
|
||||
when: ContextKeyExpr.and(
|
||||
NOTEBOOK_EDITOR_FOCUSED,
|
||||
CONTEXT_ACCESSIBILITY_MODE_ENABLED.negate(),
|
||||
ContextKeyExpr.equals('config.notebook.navigation.allowNavigateToSurroundingCells', true),
|
||||
ContextKeyExpr.and(
|
||||
NOTEBOOK_CELL_TYPE.isEqualTo('markup'),
|
||||
NOTEBOOK_CELL_MARKDOWN_EDIT_MODE.isEqualTo(false),
|
||||
NOTEBOOK_CURSOR_NAVIGATION_MODE),
|
||||
CTX_INTERACTIVE_EDITOR_FOCUSED,
|
||||
CTX_INTERACTIVE_EDITOR_INNER_CURSOR_LAST,
|
||||
EditorContextKeys.isEmbeddedDiffEditor.negate()
|
||||
),
|
||||
primary: KeyCode.DownArrow,
|
||||
weight: KeybindingWeight.EditorCore
|
||||
}
|
||||
]
|
||||
});
|
||||
|
@ -92,9 +142,17 @@ registerAction2(class FocusNextCellAction extends NotebookCellAction {
|
|||
return;
|
||||
}
|
||||
|
||||
const newCell = editor.cellAt(idx + 1);
|
||||
const newFocusMode = newCell.cellKind === CellKind.Markup && newCell.getEditState() === CellEditState.Preview ? 'container' : 'editor';
|
||||
await editor.focusNotebookCell(newCell, newFocusMode, { focusEditorLine: 1 });
|
||||
const focusEditorLine = activeCell.textBuffer.getLineCount();
|
||||
const targetCell = (context.cell ?? context.selectedCells?.[0]);
|
||||
const foundEditor: ICodeEditor | undefined = targetCell ? findTargetCellEditor(context, targetCell) : undefined;
|
||||
|
||||
if (foundEditor && foundEditor.hasTextFocus() && InteractiveEditorController.get(foundEditor)?.getWidgetPosition()?.lineNumber === focusEditorLine) {
|
||||
InteractiveEditorController.get(foundEditor)?.focus();
|
||||
} else {
|
||||
const newCell = editor.cellAt(idx + 1);
|
||||
const newFocusMode = newCell.cellKind === CellKind.Markup && newCell.getEditState() === CellEditState.Preview ? 'container' : 'editor';
|
||||
await editor.focusNotebookCell(newCell, newFocusMode, { focusEditorLine: 1 });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -117,6 +175,7 @@ registerAction2(class FocusPreviousCellAction extends NotebookCellAction {
|
|||
NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('bottom'),
|
||||
NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('none'),
|
||||
),
|
||||
EditorContextKeys.isEmbeddedDiffEditor.negate()
|
||||
),
|
||||
primary: KeyCode.UpArrow,
|
||||
weight: NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT, // code cell keybinding, focus inside editor: lower weight to not override suggest widget
|
||||
|
@ -130,7 +189,8 @@ registerAction2(class FocusPreviousCellAction extends NotebookCellAction {
|
|||
NOTEBOOK_CELL_TYPE.isEqualTo('markup'),
|
||||
NOTEBOOK_CELL_MARKDOWN_EDIT_MODE.isEqualTo(false),
|
||||
NOTEBOOK_CURSOR_NAVIGATION_MODE
|
||||
)
|
||||
),
|
||||
EditorContextKeys.isEmbeddedDiffEditor.negate()
|
||||
),
|
||||
primary: KeyCode.UpArrow,
|
||||
weight: KeybindingWeight.WorkbenchContrib, // markdown keybinding, focus on list: higher weight to override list.focusDown
|
||||
|
@ -155,7 +215,14 @@ registerAction2(class FocusPreviousCellAction extends NotebookCellAction {
|
|||
|
||||
const newCell = editor.cellAt(idx - 1);
|
||||
const newFocusMode = newCell.cellKind === CellKind.Markup && newCell.getEditState() === CellEditState.Preview ? 'container' : 'editor';
|
||||
await editor.focusNotebookCell(newCell, newFocusMode, { focusEditorLine: newCell.textBuffer.getLineCount() });
|
||||
const focusEditorLine = newCell.textBuffer.getLineCount();
|
||||
await editor.focusNotebookCell(newCell, newFocusMode, { focusEditorLine: focusEditorLine });
|
||||
|
||||
const foundEditor: ICodeEditor | undefined = findTargetCellEditor(context, newCell);
|
||||
|
||||
if (foundEditor && InteractiveEditorController.get(foundEditor)?.getWidgetPosition()?.lineNumber === focusEditorLine) {
|
||||
InteractiveEditorController.get(foundEditor)?.focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -151,7 +151,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD
|
|||
@INotebookExecutionStateService notebookExecutionStateService: INotebookExecutionStateService,
|
||||
) {
|
||||
super(NotebookTextDiffEditor.ID, telemetryService, themeService, storageService);
|
||||
this._notebookOptions = new NotebookOptions(this.configurationService, notebookExecutionStateService);
|
||||
this._notebookOptions = new NotebookOptions(this.configurationService, notebookExecutionStateService, false);
|
||||
this._register(this._notebookOptions);
|
||||
const editorOptions = this.configurationService.getValue<ICodeEditorOptions>('editor');
|
||||
this._fontInfo = FontMeasurements.readFontInfo(BareFontInfo.createFromRawSettings(editorOptions, PixelRatio.value));
|
||||
|
|
|
@ -292,7 +292,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD
|
|||
this.isEmbedded = creationOptions.isEmbedded ?? false;
|
||||
this._readOnly = creationOptions.isReadOnly ?? false;
|
||||
|
||||
this._notebookOptions = creationOptions.options ?? new NotebookOptions(this.configurationService, notebookExecutionStateService);
|
||||
this._notebookOptions = creationOptions.options ?? new NotebookOptions(this.configurationService, notebookExecutionStateService, this._readOnly);
|
||||
this._register(this._notebookOptions);
|
||||
this._viewContext = new ViewContext(
|
||||
this._notebookOptions,
|
||||
|
@ -1176,6 +1176,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD
|
|||
}
|
||||
|
||||
this.viewModel.updateOptions({ isReadOnly: this._readOnly });
|
||||
this.notebookOptions.updateOptions(this._readOnly);
|
||||
|
||||
// reveal cell if editor options tell to do so
|
||||
const cellOptions = options?.cellOptions ?? this._parseIndexedCellOptions(options);
|
||||
|
@ -1364,6 +1365,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD
|
|||
|
||||
this.viewModel = this.instantiationService.createInstance(NotebookViewModel, textModel.viewType, textModel, this._viewContext, this.getLayoutInfo(), { isReadOnly: this._readOnly });
|
||||
this._viewContext.eventDispatcher.emit([new NotebookLayoutChangedEvent({ width: true, fontInfo: true }, this.getLayoutInfo())]);
|
||||
this.notebookOptions.updateOptions(this._readOnly);
|
||||
|
||||
this._updateForOptions();
|
||||
this._updateForNotebookConfiguration();
|
||||
|
|
|
@ -133,6 +133,7 @@ export class NotebookOptions extends Disposable {
|
|||
constructor(
|
||||
private readonly configurationService: IConfigurationService,
|
||||
private readonly notebookExecutionStateService: INotebookExecutionStateService,
|
||||
private isReadonly: boolean,
|
||||
private readonly overrides?: { cellToolbarInteraction: string; globalToolbar: boolean; dragAndDropEnabled: boolean }
|
||||
) {
|
||||
super();
|
||||
|
@ -145,7 +146,7 @@ export class NotebookOptions extends Disposable {
|
|||
const cellToolbarInteraction = overrides?.cellToolbarInteraction ?? this.configurationService.getValue<string>(NotebookSetting.cellToolbarVisibility);
|
||||
const compactView = this.configurationService.getValue<boolean | undefined>(NotebookSetting.compactView) ?? true;
|
||||
const focusIndicator = this._computeFocusIndicatorOption();
|
||||
const insertToolbarPosition = this._computeInsertToolbarPositionOption();
|
||||
const insertToolbarPosition = this._computeInsertToolbarPositionOption(this.isReadonly);
|
||||
const insertToolbarAlignment = this._computeInsertToolbarAlignmentOption();
|
||||
const showFoldingControls = this._computeShowFoldingControlsOption();
|
||||
// const { bottomToolbarGap, bottomToolbarHeight } = this._computeBottomToolbarDimensions(compactView, insertToolbarPosition, insertToolbarAlignment);
|
||||
|
@ -248,6 +249,22 @@ export class NotebookOptions extends Disposable {
|
|||
}));
|
||||
}
|
||||
|
||||
updateOptions(isReadonly: boolean) {
|
||||
if (this.isReadonly !== isReadonly) {
|
||||
this.isReadonly = isReadonly;
|
||||
|
||||
this._updateConfiguration({
|
||||
affectsConfiguration(configuration: string): boolean {
|
||||
return configuration === NotebookSetting.insertToolbarLocation;
|
||||
},
|
||||
source: ConfigurationTarget.DEFAULT,
|
||||
affectedKeys: new Set([NotebookSetting.insertToolbarLocation]),
|
||||
change: { keys: [NotebookSetting.insertToolbarLocation], overrides: [] },
|
||||
sourceConfig: undefined
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private _migrateDeprecatedSetting(deprecatedKey: string, key: string): void {
|
||||
const deprecatedSetting = this.configurationService.inspect(deprecatedKey);
|
||||
|
||||
|
@ -390,7 +407,7 @@ export class NotebookOptions extends Disposable {
|
|||
}
|
||||
|
||||
if (insertToolbarPosition) {
|
||||
configuration.insertToolbarPosition = this._computeInsertToolbarPositionOption();
|
||||
configuration.insertToolbarPosition = this._computeInsertToolbarPositionOption(this.isReadonly);
|
||||
}
|
||||
|
||||
if (globalToolbar && this.overrides?.globalToolbar === undefined) {
|
||||
|
@ -479,8 +496,8 @@ export class NotebookOptions extends Disposable {
|
|||
});
|
||||
}
|
||||
|
||||
private _computeInsertToolbarPositionOption() {
|
||||
return this.configurationService.getValue<'betweenCells' | 'notebookToolbar' | 'both' | 'hidden'>(NotebookSetting.insertToolbarLocation) ?? 'both';
|
||||
private _computeInsertToolbarPositionOption(isReadOnly: boolean) {
|
||||
return isReadOnly ? 'hidden' : this.configurationService.getValue<'betweenCells' | 'notebookToolbar' | 'both' | 'hidden'>(NotebookSetting.insertToolbarLocation) ?? 'both';
|
||||
}
|
||||
|
||||
private _computeInsertToolbarAlignmentOption() {
|
||||
|
|
|
@ -202,7 +202,7 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
|
|||
// recompute
|
||||
this._ensureOutputsTop();
|
||||
const notebookLayoutConfiguration = this.viewContext.notebookOptions.getLayoutConfiguration();
|
||||
const bottomToolbarDimensions = this.viewContext.notebookOptions.computeBottomToolbarDimensions();
|
||||
const bottomToolbarDimensions = this.viewContext.notebookOptions.computeBottomToolbarDimensions(this.viewType);
|
||||
const outputShowMoreContainerHeight = state.outputShowMoreContainerHeight ? state.outputShowMoreContainerHeight : this._layoutInfo.outputShowMoreContainerHeight;
|
||||
const outputTotalHeight = Math.max(this._outputMinHeight, this.isOutputCollapsed ? notebookLayoutConfiguration.collapsedIndicatorHeight : this._outputsTop!.getTotalSum());
|
||||
const commentHeight = state.commentHeight ? this._commentHeight : this._layoutInfo.commentHeight;
|
||||
|
|
|
@ -21,7 +21,7 @@ suite('NotebookCellList', () => {
|
|||
suiteSetup(() => {
|
||||
disposables = new DisposableStore();
|
||||
instantiationService = setupInstantiationService(disposables);
|
||||
notebookDefaultOptions = new NotebookOptions(instantiationService.get(IConfigurationService), instantiationService.get(INotebookExecutionStateService));
|
||||
notebookDefaultOptions = new NotebookOptions(instantiationService.get(IConfigurationService), instantiationService.get(INotebookExecutionStateService), false);
|
||||
topInsertToolbarHeight = notebookDefaultOptions.computeTopInsertToolbarHeight();
|
||||
|
||||
});
|
||||
|
|
|
@ -58,7 +58,7 @@ suite('NotebookViewModel', () => {
|
|||
test('ctor', function () {
|
||||
const notebook = new NotebookTextModel('notebook', URI.parse('test'), [], {}, { transientCellMetadata: {}, transientDocumentMetadata: {}, transientOutputs: false, cellContentMetadata: {} }, undoRedoService, modelService, languageService);
|
||||
const model = new NotebookEditorTestModel(notebook);
|
||||
const viewContext = new ViewContext(new NotebookOptions(instantiationService.get(IConfigurationService), instantiationService.get(INotebookExecutionStateService)), new NotebookEventDispatcher(), () => ({} as IBaseCellEditorOptions));
|
||||
const viewContext = new ViewContext(new NotebookOptions(instantiationService.get(IConfigurationService), instantiationService.get(INotebookExecutionStateService), false), new NotebookEventDispatcher(), () => ({} as IBaseCellEditorOptions));
|
||||
const viewModel = new NotebookViewModel('notebook', model.notebook, viewContext, null, { isReadOnly: false }, instantiationService, bulkEditService, undoRedoService, textModelService, notebookExecutionStateService);
|
||||
assert.strictEqual(viewModel.viewType, 'notebook');
|
||||
});
|
||||
|
|
|
@ -201,7 +201,7 @@ function _createTestNotebookEditor(instantiationService: TestInstantiationServic
|
|||
}), {}, { transientCellMetadata: {}, transientDocumentMetadata: {}, cellContentMetadata: {}, transientOutputs: false });
|
||||
|
||||
const model = new NotebookEditorTestModel(notebook);
|
||||
const notebookOptions = new NotebookOptions(instantiationService.get(IConfigurationService), instantiationService.get(INotebookExecutionStateService));
|
||||
const notebookOptions = new NotebookOptions(instantiationService.get(IConfigurationService), instantiationService.get(INotebookExecutionStateService), false);
|
||||
const viewContext = new ViewContext(notebookOptions, new NotebookEventDispatcher(), () => ({} as IBaseCellEditorOptions));
|
||||
const viewModel: NotebookViewModel = instantiationService.createInstance(NotebookViewModel, viewType, model.notebook, viewContext, null, { isReadOnly: false });
|
||||
|
||||
|
@ -374,7 +374,7 @@ export function createNotebookCellList(instantiationService: TestInstantiationSe
|
|||
NotebookCellList,
|
||||
'NotebookCellList',
|
||||
DOM.$('container'),
|
||||
viewContext ?? new ViewContext(new NotebookOptions(instantiationService.get(IConfigurationService), instantiationService.get(INotebookExecutionStateService)), new NotebookEventDispatcher(), () => ({} as IBaseCellEditorOptions)),
|
||||
viewContext ?? new ViewContext(new NotebookOptions(instantiationService.get(IConfigurationService), instantiationService.get(INotebookExecutionStateService), false), new NotebookEventDispatcher(), () => ({} as IBaseCellEditorOptions)),
|
||||
delegate,
|
||||
[renderer],
|
||||
instantiationService.get<IContextKeyService>(IContextKeyService),
|
||||
|
|
|
@ -21,7 +21,7 @@ import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal
|
|||
import { IDebugService } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
import { isWeb, OperatingSystem } from 'vs/base/common/platform';
|
||||
import { ITunnelService, RemoteTunnel } from 'vs/platform/tunnel/common/tunnel';
|
||||
import { ITunnelService, RemoteTunnel, TunnelPrivacyId } from 'vs/platform/tunnel/common/tunnel';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer';
|
||||
import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity';
|
||||
|
@ -348,6 +348,10 @@ class OnAutoForwardedAction extends Disposable {
|
|||
choices.unshift(this.elevateChoice(tunnel));
|
||||
}
|
||||
|
||||
if (tunnel.privacy === TunnelPrivacyId.Private && isWeb && this.tunnelService.canChangePrivacy) {
|
||||
choices.push(this.makePublicChoice(tunnel));
|
||||
}
|
||||
|
||||
message += this.linkMessage();
|
||||
|
||||
this.lastNotification = this.notificationService.prompt(Severity.Info, message, choices, { neverShowAgain: { id: 'remote.tunnelsView.autoForwardNeverShow', isSecondary: true } });
|
||||
|
@ -359,6 +363,20 @@ class OnAutoForwardedAction extends Disposable {
|
|||
});
|
||||
}
|
||||
|
||||
private makePublicChoice(tunnel: RemoteTunnel): IPromptChoice {
|
||||
return {
|
||||
label: nls.localize('remote.tunnelsView.makePublic', "Make Public"),
|
||||
run: async () => {
|
||||
await this.remoteExplorerService.close({ host: tunnel.tunnelRemoteHost, port: tunnel.tunnelRemotePort }, TunnelCloseReason.Other);
|
||||
return this.remoteExplorerService.forward({
|
||||
remote: { host: tunnel.tunnelRemoteHost, port: tunnel.tunnelRemotePort },
|
||||
local: tunnel.tunnelLocalPort,
|
||||
privacy: TunnelPrivacyId.Public,
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private openBrowserChoice(tunnel: RemoteTunnel): IPromptChoice {
|
||||
const address = makeAddress(tunnel.tunnelRemoteHost, tunnel.tunnelRemotePort);
|
||||
return {
|
||||
|
|
|
@ -86,24 +86,19 @@ export class RemoteStartEntry extends Disposable implements IWorkbenchContributi
|
|||
|
||||
if (isWeb) {
|
||||
const remoteExtensionTips = this.productService.remoteExtensionTips?.['tunnel'];
|
||||
if (remoteExtensionTips) {
|
||||
const metadata: RemoteExtensionMetadata = {
|
||||
id: remoteExtensionTips.extensionId,
|
||||
installed: false,
|
||||
friendlyName: remoteExtensionTips.friendlyName,
|
||||
remoteCommands: [],
|
||||
isPlatformCompatible: false,
|
||||
dependencies: [],
|
||||
helpLink: remoteExtensionTips.startEntry?.helpLink ?? '',
|
||||
startConnectLabel: remoteExtensionTips.startEntry?.startConnectLabel ?? '',
|
||||
startCommand: remoteExtensionTips.startEntry?.startCommand ?? '',
|
||||
priority: remoteExtensionTips.startEntry?.priority ?? 10
|
||||
this.remoteExtensionMetadata = remoteExtensionTips ? [{
|
||||
id: remoteExtensionTips.extensionId,
|
||||
installed: false,
|
||||
friendlyName: remoteExtensionTips.friendlyName,
|
||||
remoteCommands: [],
|
||||
isPlatformCompatible: false,
|
||||
dependencies: [],
|
||||
helpLink: remoteExtensionTips.startEntry?.helpLink ?? '',
|
||||
startConnectLabel: remoteExtensionTips.startEntry?.startConnectLabel ?? '',
|
||||
startCommand: remoteExtensionTips.startEntry?.startCommand ?? '',
|
||||
priority: remoteExtensionTips.startEntry?.priority ?? 10
|
||||
|
||||
};
|
||||
this.remoteExtensionMetadata = [metadata];
|
||||
} else {
|
||||
this.remoteExtensionMetadata = [];
|
||||
}
|
||||
}] : [];
|
||||
}
|
||||
else {
|
||||
const remoteExtensionTips = { ...this.productService.remoteExtensionTips, ...this.productService.virtualWorkspaceExtensionTips };
|
||||
|
@ -325,38 +320,10 @@ export class RemoteStartEntry extends Disposable implements IWorkbenchContributi
|
|||
|
||||
private async showRemoteTunnelStartActions() {
|
||||
await this._init();
|
||||
|
||||
const computeItems = async () => {
|
||||
const metadata = this.remoteExtensionMetadata[0];
|
||||
if (!metadata) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!metadata.installed) {
|
||||
await this.installAndRunStartCommand(metadata);
|
||||
}
|
||||
|
||||
return this.getRemoteCommandQuickPickItems(metadata.remoteCommands);
|
||||
};
|
||||
|
||||
const quickPick = this.quickInputService.createQuickPick();
|
||||
quickPick.placeholder = nls.localize('remote.startActions.quickPickPlaceholder', 'Select an option to connect');
|
||||
quickPick.items = await computeItems();
|
||||
quickPick.sortByLabel = false;
|
||||
quickPick.canSelectMany = false;
|
||||
quickPick.ignoreFocusOut = false;
|
||||
once(quickPick.onDidAccept)(async () => {
|
||||
|
||||
const selectedItems = quickPick.selectedItems;
|
||||
if (selectedItems.length === 1) {
|
||||
const selectedItem = selectedItems[0].id!;
|
||||
this.executeCommandWithTelemetry(selectedItem);
|
||||
quickPick.dispose();
|
||||
}
|
||||
});
|
||||
|
||||
quickPick.onDidHide(() => quickPick.dispose());
|
||||
quickPick.show();
|
||||
const metadata = this.remoteExtensionMetadata[0];
|
||||
if (metadata.installed) {
|
||||
this.executeCommandWithTelemetry(metadata.startCommand);
|
||||
}
|
||||
}
|
||||
|
||||
private async installAndRunStartCommand(metadata: RemoteExtensionMetadata) {
|
||||
|
|
|
@ -11,7 +11,7 @@ import { VIEW_PANE_ID, ISCMService, ISCMRepository, ISCMViewService } from 'vs/w
|
|||
import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity';
|
||||
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IStatusbarService, StatusbarAlignment as MainThreadStatusBarAlignment } from 'vs/workbench/services/statusbar/browser/statusbar';
|
||||
import { IStatusbarEntry, IStatusbarService, StatusbarAlignment as MainThreadStatusBarAlignment } from 'vs/workbench/services/statusbar/browser/statusbar';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { EditorResourceAccessor } from 'vs/workbench/common/editor';
|
||||
|
@ -167,13 +167,18 @@ export class SCMStatusController implements IWorkbenchContribution {
|
|||
repoAgnosticActionName = '';
|
||||
}
|
||||
|
||||
disposables.add(this.statusbarService.addEntry({
|
||||
const statusbarEntry: IStatusbarEntry = {
|
||||
name: localize('status.scm', "Source Control") + (repoAgnosticActionName ? ` ${repoAgnosticActionName}` : ''),
|
||||
text: command.title,
|
||||
ariaLabel: tooltip,
|
||||
tooltip,
|
||||
command: command.id ? command : undefined
|
||||
}, `status.scm.${index}`, MainThreadStatusBarAlignment.LEFT, 10000 - index));
|
||||
};
|
||||
|
||||
disposables.add(index === 0 ?
|
||||
this.statusbarService.addEntry(statusbarEntry, `status.scm.${index}`, MainThreadStatusBarAlignment.LEFT, 10000) :
|
||||
this.statusbarService.addEntry(statusbarEntry, `status.scm.${index}`, MainThreadStatusBarAlignment.LEFT, { id: `status.scm.${index - 1}`, alignment: MainThreadStatusBarAlignment.RIGHT, compact: true })
|
||||
);
|
||||
}
|
||||
|
||||
this.statusBarDisposable = disposables;
|
||||
|
|
|
@ -38,6 +38,8 @@ export interface ITerminalStatusList {
|
|||
|
||||
/**
|
||||
* Adds a status to the list.
|
||||
* @param status The status object. Ideally a single status object that does not change will be
|
||||
* shared as this call will no-op if the status is already set (checked by by object reference).
|
||||
* @param duration An optional duration in milliseconds of the status, when specified the status
|
||||
* will remove itself when the duration elapses unless the status gets re-added.
|
||||
*/
|
||||
|
@ -87,6 +89,11 @@ export class TerminalStatusList extends Disposable implements ITerminalStatusLis
|
|||
const timeout = window.setTimeout(() => this.remove(status), duration);
|
||||
this._statusTimeouts.set(status.id, timeout);
|
||||
}
|
||||
const existingStatus = this._statuses.get(status.id);
|
||||
if (existingStatus && existingStatus !== status) {
|
||||
this._onDidRemoveStatus.fire(existingStatus);
|
||||
this._statuses.delete(existingStatus.id);
|
||||
}
|
||||
if (!this._statuses.has(status.id)) {
|
||||
const oldPrimary = this.primary;
|
||||
this._statuses.set(status.id, status);
|
||||
|
@ -95,11 +102,6 @@ export class TerminalStatusList extends Disposable implements ITerminalStatusLis
|
|||
if (oldPrimary !== newPrimary) {
|
||||
this._onDidChangePrimaryStatus.fire(newPrimary);
|
||||
}
|
||||
} else {
|
||||
this._statuses.set(status.id, status);
|
||||
// It maybe the case that status hasn't changed, there isn't a good way to check this based on
|
||||
// `ITerminalStatus`, so just fire the event anyway.
|
||||
this._onDidAddStatus.fire(status);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -130,6 +130,19 @@ suite('Workbench - TerminalStatusList', () => {
|
|||
strictEqual(list.statuses[1].icon!.id, Codicon.zap.id, 'zap~spin should have animation removed only');
|
||||
});
|
||||
|
||||
test('add should fire onDidRemoveStatus if same status id with a different object reference was added', () => {
|
||||
const eventCalls: string[] = [];
|
||||
list.onDidAddStatus(() => eventCalls.push('add'));
|
||||
list.onDidRemoveStatus(() => eventCalls.push('remove'));
|
||||
list.add({ id: 'test', severity: Severity.Info });
|
||||
list.add({ id: 'test', severity: Severity.Info });
|
||||
deepStrictEqual(eventCalls, [
|
||||
'add',
|
||||
'remove',
|
||||
'add'
|
||||
]);
|
||||
});
|
||||
|
||||
test('remove', () => {
|
||||
list.add({ id: 'info', severity: Severity.Info });
|
||||
list.add({ id: 'warning', severity: Severity.Warning });
|
||||
|
|
|
@ -186,6 +186,18 @@ suite('ShellIntegrationAddon', () => {
|
|||
await writeP(xterm, '\x1b]633;D;7\x07');
|
||||
mock.verify();
|
||||
});
|
||||
test('should pass command line sequence to the capability', async () => {
|
||||
const mock = shellIntegrationAddon.getCommandDetectionMock(xterm);
|
||||
mock.expects('setCommandLine').once().withExactArgs('', false);
|
||||
await writeP(xterm, '\x1b]633;E\x07');
|
||||
mock.verify();
|
||||
|
||||
const mock2 = shellIntegrationAddon.getCommandDetectionMock(xterm);
|
||||
mock2.expects('setCommandLine').twice().withExactArgs('cmd', false);
|
||||
await writeP(xterm, '\x1b]633;E;cmd\x07');
|
||||
await writeP(xterm, '\x1b]633;E;cmd;invalid-nonce\x07');
|
||||
mock2.verify();
|
||||
});
|
||||
test('should not activate capability on the cwd sequence (OSC 633 ; P=Cwd=<cwd> ST)', async () => {
|
||||
strictEqual(capabilities.has(TerminalCapability.CommandDetection), false);
|
||||
await writeP(xterm, 'foo');
|
||||
|
|
|
@ -13,6 +13,7 @@ import { ITerminalCapabilityStore, TerminalCapability } from 'vs/platform/termin
|
|||
import { IBufferLine, IBufferRange, Terminal } from 'xterm';
|
||||
import { ITerminalBackend, ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { detectLinks } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLinkParsing';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
||||
const enum Constants {
|
||||
/**
|
||||
|
@ -69,6 +70,7 @@ export class TerminalLocalLinkDetector implements ITerminalLinkDetector {
|
|||
private readonly _capabilities: ITerminalCapabilityStore,
|
||||
private readonly _processManager: Pick<ITerminalProcessManager, 'initialCwd' | 'os' | 'remoteAuthority' | 'userHome'> & { backend?: Pick<ITerminalBackend, 'getWslPath'> },
|
||||
private readonly _linkResolver: ITerminalLinkResolver,
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
@IUriIdentityService private readonly _uriIdentityService: IUriIdentityService,
|
||||
@IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService
|
||||
) {
|
||||
|
@ -88,7 +90,10 @@ export class TerminalLocalLinkDetector implements ITerminalLinkDetector {
|
|||
|
||||
const os = this._processManager.os || OS;
|
||||
const parsedLinks = detectLinks(text, os);
|
||||
this._logService.trace('terminalLocaLinkDetector#detect text', text);
|
||||
this._logService.trace('terminalLocaLinkDetector#detect parsedLinks', parsedLinks);
|
||||
for (const parsedLink of parsedLinks) {
|
||||
|
||||
// Don't try resolve any links of excessive length
|
||||
if (parsedLink.path.text.length > Constants.MaxResolvedLinkLength) {
|
||||
continue;
|
||||
|
@ -147,6 +152,7 @@ export class TerminalLocalLinkDetector implements ITerminalLinkDetector {
|
|||
}
|
||||
}
|
||||
linkCandidates.push(...specialEndLinkCandidates);
|
||||
this._logService.trace('terminalLocaLinkDetector#detect linkCandidates', linkCandidates);
|
||||
|
||||
// Validate the path and convert to the outgoing type
|
||||
const simpleLink = await this._validateAndGetLink(undefined, bufferRange, linkCandidates, trimRangeMap);
|
||||
|
@ -156,6 +162,7 @@ export class TerminalLocalLinkDetector implements ITerminalLinkDetector {
|
|||
parsedLink.prefix?.index ?? parsedLink.path.index,
|
||||
parsedLink.suffix ? parsedLink.suffix.suffix.index + parsedLink.suffix.suffix.text.length : parsedLink.path.index + parsedLink.path.text.length
|
||||
);
|
||||
this._logService.trace('terminalLocaLinkDetector#detect verified link', simpleLink);
|
||||
links.push(simpleLink);
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ import { TerminalLinkResolver } from 'vs/workbench/contrib/terminalContrib/links
|
|||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { createFileStat } from 'vs/workbench/test/common/workbenchTestServices';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ILogService, NullLogService } from 'vs/platform/log/common/log';
|
||||
|
||||
const unixLinks: (string | { link: string; resource: URI })[] = [
|
||||
// Absolute
|
||||
|
@ -181,6 +182,7 @@ suite('Workbench - TerminalLocalLinkDetector', () => {
|
|||
return createFileStat(resource);
|
||||
}
|
||||
});
|
||||
instantiationService.stub(ILogService, new NullLogService());
|
||||
resolver = instantiationService.createInstance(TerminalLinkResolver);
|
||||
validResources = [];
|
||||
|
||||
|
|
|
@ -14,6 +14,8 @@ import { stripIcons } from 'vs/base/common/iconLabels';
|
|||
import { Iterable } from 'vs/base/common/iterator';
|
||||
import { Disposable, DisposableStore, IReference, MutableDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ResourceMap } from 'vs/base/common/map';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import { ThemeIcon } from 'vs/base/common/themables';
|
||||
import { Constants } from 'vs/base/common/uint';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
|
@ -21,7 +23,7 @@ import { ContentWidgetPositionPreference, ICodeEditor, IContentWidgetPosition, I
|
|||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { overviewRulerError, overviewRulerInfo } from 'vs/editor/common/core/editorColorRegistry';
|
||||
import { IRange, Range } from 'vs/editor/common/core/range';
|
||||
import { IRange } from 'vs/editor/common/core/range';
|
||||
import { IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||
import { IModelDeltaDecoration, ITextModel, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model';
|
||||
import { IModelService } from 'vs/editor/common/services/model';
|
||||
|
@ -34,24 +36,22 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
|||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { themeColorFromId } from 'vs/platform/theme/common/themeService';
|
||||
import { ThemeIcon } from 'vs/base/common/themables';
|
||||
import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity';
|
||||
import { EditorLineNumberContextMenu, GutterActionsRegistry } from 'vs/workbench/contrib/codeEditor/browser/editorLineNumberMenu';
|
||||
import { getTestItemContextOverlay } from 'vs/workbench/contrib/testing/browser/explorerProjections/testItemContextOverlay';
|
||||
import { testingRunAllIcon, testingRunIcon, testingStatesToIcons } from 'vs/workbench/contrib/testing/browser/icons';
|
||||
import { DefaultGutterClickAction, getTestingConfiguration, TestingConfigKeys } from 'vs/workbench/contrib/testing/common/configuration';
|
||||
import { labelForTestInState, Testing } from 'vs/workbench/contrib/testing/common/constants';
|
||||
import { DefaultGutterClickAction, TestingConfigKeys, getTestingConfiguration } from 'vs/workbench/contrib/testing/common/configuration';
|
||||
import { Testing, labelForTestInState } from 'vs/workbench/contrib/testing/common/constants';
|
||||
import { TestId } from 'vs/workbench/contrib/testing/common/testId';
|
||||
import { ITestDecoration as IPublicTestDecoration, ITestingDecorationsService, TestDecorations } from 'vs/workbench/contrib/testing/common/testingDecorations';
|
||||
import { ITestingPeekOpener } from 'vs/workbench/contrib/testing/common/testingPeekOpener';
|
||||
import { isFailedState, maxPriority } from 'vs/workbench/contrib/testing/common/testingStates';
|
||||
import { buildTestUri, parseTestUri, TestUriType } from 'vs/workbench/contrib/testing/common/testingUri';
|
||||
import { ITestProfileService } from 'vs/workbench/contrib/testing/common/testProfileService';
|
||||
import { LiveTestResult } from 'vs/workbench/contrib/testing/common/testResult';
|
||||
import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService';
|
||||
import { getContextForTestItem, ITestService, testsInFile } from 'vs/workbench/contrib/testing/common/testService';
|
||||
import { IncrementalTestCollectionItem, InternalTestItem, IRichLocation, ITestMessage, ITestRunProfile, TestDiffOpType, TestMessageType, TestResultItem, TestResultState, TestRunProfileBitset } from 'vs/workbench/contrib/testing/common/testTypes';
|
||||
import { EditorLineNumberContextMenu, GutterActionsRegistry } from 'vs/workbench/contrib/codeEditor/browser/editorLineNumberMenu';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import { ITestService, getContextForTestItem, testsInFile } from 'vs/workbench/contrib/testing/common/testService';
|
||||
import { IRichLocation, ITestMessage, ITestRunProfile, IncrementalTestCollectionItem, InternalTestItem, TestDiffOpType, TestMessageType, TestResultItem, TestResultState, TestRunProfileBitset } from 'vs/workbench/contrib/testing/common/testTypes';
|
||||
import { ITestDecoration as IPublicTestDecoration, ITestingDecorationsService, TestDecorations } from 'vs/workbench/contrib/testing/common/testingDecorations';
|
||||
import { ITestingPeekOpener } from 'vs/workbench/contrib/testing/common/testingPeekOpener';
|
||||
import { isFailedState, maxPriority } from 'vs/workbench/contrib/testing/common/testingStates';
|
||||
import { TestUriType, buildTestUri, parseTestUri } from 'vs/workbench/contrib/testing/common/testingUri';
|
||||
|
||||
const MAX_INLINE_MESSAGE_LENGTH = 128;
|
||||
|
||||
|
@ -423,7 +423,7 @@ export class TestingDecorations extends Disposable implements IEditorContributio
|
|||
this._register(this.editor.onDidChangeModel(e => this.attachModel(e.newModelUrl || undefined)));
|
||||
this._register(this.editor.onMouseDown(e => {
|
||||
if (e.target.position && this.currentUri) {
|
||||
const modelDecorations = editor.getModel()?.getDecorationsInRange(Range.fromPositions(e.target.position)) ?? [];
|
||||
const modelDecorations = editor.getModel()?.getLineDecorations(e.target.position.lineNumber) ?? [];
|
||||
if (!modelDecorations.length) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import { Event, EventMultiplexer } from 'vs/base/common/event';
|
||||
import {
|
||||
ILocalExtension, IGalleryExtension, IExtensionIdentifier, IExtensionsControlManifest, IExtensionGalleryService, InstallOptions, UninstallOptions, InstallVSIXOptions, InstallExtensionResult, ExtensionManagementError, ExtensionManagementErrorCode, Metadata, InstallOperation
|
||||
ILocalExtension, IGalleryExtension, IExtensionIdentifier, IExtensionsControlManifest, IExtensionGalleryService, InstallOptions, UninstallOptions, InstallVSIXOptions, InstallExtensionResult, ExtensionManagementError, ExtensionManagementErrorCode, Metadata, InstallOperation, EXTENSION_INSTALL_SYNC_CONTEXT
|
||||
} from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { DidChangeProfileForServerEvent, DidUninstallExtensionOnServerEvent, IExtensionManagementServer, IExtensionManagementServerService, InstallExtensionOnServerEvent, IWorkbenchExtensionManagementService, UninstallExtensionOnServerEvent } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
|
||||
import { ExtensionType, isLanguagePackExtension, IExtensionManifest, getWorkspaceSupportTypeMessage, TargetPlatform } from 'vs/platform/extensions/common/extensions';
|
||||
|
@ -333,7 +333,9 @@ export class ExtensionManagementService extends Disposable implements IWorkbench
|
|||
servers.push(this.extensionManagementServerService.localExtensionManagementServer);
|
||||
}
|
||||
}
|
||||
await this.checkForWorkspaceTrust(manifest);
|
||||
if (!installOptions.context?.[EXTENSION_INSTALL_SYNC_CONTEXT]) {
|
||||
await this.checkForWorkspaceTrust(manifest);
|
||||
}
|
||||
if (!installOptions.donotIncludePackAndDependencies) {
|
||||
await this.checkInstallingExtensionOnWeb(gallery, manifest);
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
// THIS IS A GENERATED FILE. DO NOT EDIT DIRECTLY.
|
||||
|
||||
export const allApiProposals = Object.freeze({
|
||||
authGetSessions: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.authGetSessions.d.ts',
|
||||
authSession: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.authSession.d.ts',
|
||||
codiconDecoration: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.codiconDecoration.d.ts',
|
||||
commentsDraftState: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.commentsDraftState.d.ts',
|
||||
|
@ -41,7 +42,6 @@ export const allApiProposals = Object.freeze({
|
|||
findTextInFiles: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.findTextInFiles.d.ts',
|
||||
formatMultipleRanges: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.formatMultipleRanges.d.ts',
|
||||
fsChunks: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.fsChunks.d.ts',
|
||||
getSessions: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.getSessions.d.ts',
|
||||
handleIssueUri: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.handleIssueUri.d.ts',
|
||||
idToken: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.idToken.d.ts',
|
||||
indentSize: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.indentSize.d.ts',
|
||||
|
|
7
src/vscode-dts/vscode.d.ts
vendored
7
src/vscode-dts/vscode.d.ts
vendored
|
@ -15648,7 +15648,14 @@ declare module 'vscode' {
|
|||
readonly label: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional options to be used when calling {@link authentication.getSession} with the flag `forceNewSession`.
|
||||
*/
|
||||
export interface AuthenticationForceNewSessionOptions {
|
||||
/**
|
||||
* An optional message that will be displayed to the user when we ask to re-authenticate. Providing additional context
|
||||
* as to why you are asking a user to re-authenticate can help increase the odds that they will accept.
|
||||
*/
|
||||
detail?: string;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,10 @@ declare module 'vscode' {
|
|||
// https://github.com/microsoft/vscode/issues/152399
|
||||
|
||||
export interface AuthenticationForceNewSessionOptions {
|
||||
/**
|
||||
* The session that you are asking to be recreated. The Auth Provider can use this to
|
||||
* help guide the user to log in to the correct account.
|
||||
*/
|
||||
sessionToRecreate?: AuthenticationSession;
|
||||
}
|
||||
|
|
@ -56,6 +56,13 @@ declare module 'vscode' {
|
|||
*/
|
||||
label: string;
|
||||
|
||||
/**
|
||||
* The relative priority of this edit. Higher priority items are shown first in the UI.
|
||||
*
|
||||
* Defaults to `0`.
|
||||
*/
|
||||
priority?: number;
|
||||
|
||||
/**
|
||||
* The text or snippet to insert at the pasted locations.
|
||||
*/
|
||||
|
|
|
@ -13,7 +13,14 @@ declare module 'vscode' {
|
|||
*
|
||||
* This id should be unique within the extension but does not need to be unique across extensions.
|
||||
*/
|
||||
id: string;
|
||||
id?: string;
|
||||
|
||||
/**
|
||||
* The relative priority of this edit. Higher priority items are shown first in the UI.
|
||||
*
|
||||
* Defaults to `0`.
|
||||
*/
|
||||
priority?: number;
|
||||
|
||||
/**
|
||||
* Human readable label that describes the edit.
|
||||
|
|
Loading…
Reference in a new issue