mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-25 17:57:50 +01:00
Merge branch 'main' into fix_call_hierarchy_tooltip
This commit is contained in:
@@ -233,6 +233,7 @@
|
||||
"electron",
|
||||
"events",
|
||||
"fs",
|
||||
"fs/promises",
|
||||
"graceful-fs",
|
||||
"http",
|
||||
"https",
|
||||
@@ -244,6 +245,7 @@
|
||||
"os",
|
||||
"path",
|
||||
"perf_hooks",
|
||||
"readline",
|
||||
"stream",
|
||||
"string_decoder",
|
||||
"tas-client-umd",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
disturl "https://electronjs.org/headers"
|
||||
target "22.3.10"
|
||||
target "22.3.11"
|
||||
ms_build_id "21658788"
|
||||
runtime "electron"
|
||||
build_from_source "true"
|
||||
|
||||
@@ -1,34 +1,7 @@
|
||||
dfb37570ef34ac04f34c26d0ec558df60a9665df5961c01c1657c0ca495f2f01 node-v16.17.1-aix-ppc64.tar.gz
|
||||
f9f02f7872e2e8ee54320fce13deb9d56904f32bb0615b6e21aa3371d8899150 node-v16.17.1-darwin-arm64.tar.gz
|
||||
09a45f60bfb9dfbea4f69044dc733ef983945acd92ca89ccccac267f3d71bd44 node-v16.17.1-darwin-arm64.tar.xz
|
||||
3db26761ad8493b894d42260d7e65094b7af9bc473588739e61bc1c32d6ff955 node-v16.17.1-darwin-x64.tar.gz
|
||||
8e7089956fa01cf7d0045945c0863d282dc6818fb0476237c1396497e29a4254 node-v16.17.1-darwin-x64.tar.xz
|
||||
35ccb95caf02cda3bd680da4350a8ae5d666a7a9eae3afe5c2a1b3ef29aef108 node-v16.17.1-headers.tar.gz
|
||||
554c8d1b4b16e0f4c073b9df7c49c893716a3a533f25ac646f23619f5ccee7df node-v16.17.1-headers.tar.xz
|
||||
adc7032888d4e672a4aac886baede8c04fccdd1a2e7ab4bcf325e3f336f44a3d node-v16.17.1-linux-arm64.tar.gz
|
||||
3dfb8fd8f6b97df69cdc56524abc906c50ef1d0bf091188616802e6c7c731389 node-v16.17.1-linux-arm64.tar.xz
|
||||
aeab05e35f1d2824ecfb88ca321f1408b44d292b2775f2890972c828e00216d0 node-v16.17.1-linux-armv7l.tar.gz
|
||||
a035ceefb5e16f5fce98c8ddfdf721b96eec20542c72fb8781bcbb6ef20c5550 node-v16.17.1-linux-armv7l.tar.xz
|
||||
1f48de7bed99e973c4c50f1b7fc99fc9af5144d093fd6d2b50a1e43b5818bf05 node-v16.17.1-linux-ppc64le.tar.gz
|
||||
70305934661f89fca64053b85317a75f233d5e3fdb2caa6546a19262a519cf20 node-v16.17.1-linux-ppc64le.tar.xz
|
||||
029dad48018bda07b481213816549b632059fc673c30fdc7a353e04619128344 node-v16.17.1-linux-s390x.tar.gz
|
||||
1a47f604944c6aff37cb7483503155671cdb34bda9bfb8962007bc440fa04d77 node-v16.17.1-linux-s390x.tar.xz
|
||||
da5658693243b3ecf6a4cba6751a71df1eb9e9703ca93b42a9404aed85f58ad0 node-v16.17.1-linux-x64.tar.gz
|
||||
06ba2eb34aa385967f5f58c87a44753f83212f6cccea892b33f80a2e7fda8384 node-v16.17.1-linux-x64.tar.xz
|
||||
12d10476ea7483298364c810c037b9316d1a73dc8c81cfeff7d794aecadde498 node-v16.17.1.pkg
|
||||
e423985f6019b2026f9a191adb56a96ae83ecd56cdf839cf94aa980168b7a90f node-v16.17.1.tar.gz
|
||||
6721feb4152d56d2c6b358ce397abd5a7f1daf09ee2e25c5021b9b4d3f86a330 node-v16.17.1.tar.xz
|
||||
9777e8c4b2864c5b54a0e4e9400f14887db68560a09b94b4113b560a64d1e680 node-v16.17.1-win-x64.7z
|
||||
ed290151efb417262b9808a70738d4ab79e9d53653a6a9f4b8dd97912e279dce node-v16.17.1-win-x64.zip
|
||||
0f8101648d5c9e49e89fee541da9e574f899716c32b7c51a732b1766b9fc4526 node-v16.17.1-win-x86.7z
|
||||
189b5e8b23226403e7b07a46614de19b444d369e694901e3668e2f549799cbcd node-v16.17.1-win-x86.zip
|
||||
1bdff65fb7642425c0d6826084d63c4be43520316f0ea0b46e6a51999a0ed7fc node-v16.17.1-x64.msi
|
||||
b737eb23a2c67c253b9364b5284123faf5220d567615bebd4ec4b81070e4d177 node-v16.17.1-x86.msi
|
||||
f518a70dcab7c3fac5b2e1ef100b4f628edfb160f4fafa9a94ef222da8a6e9ab win-x64/node.exe
|
||||
2f459a64647db493da63c790ce368ad54f59f086d9f22f59c5018680420197b3 win-x64/node.lib
|
||||
23215ce7d1e9de9777c3407239e7cf18d29d60f757b772219421ab361ac67c74 win-x64/node_pdb.7z
|
||||
8e32ec12028fd3e3147435be79a858ed9c870aaafa1fcb291362307ef3c47547 win-x64/node_pdb.zip
|
||||
2393aff88be19dbe0205cbde4ff0c1d89911b15de5c99c80f6e5e29604eecd12 win-x86/node.exe
|
||||
5018c3d42f3fbacbd06cb943b3f2696c8e67ca9bdf6864d0e263d6d6911dffd2 win-x86/node.lib
|
||||
05a4db56444a60ee70b0d2642d7f2d82a33339894d2d73bd07b1a41d6c869e04 win-x86/node_pdb.7z
|
||||
8f86eacb7f13a1bf6738cb0819d7854a2abca40fc2e9e1f91421e44ba52cad7e win-x86/node_pdb.zip
|
||||
|
||||
@@ -125,8 +125,9 @@ const serverWithWebEntryPoints = [
|
||||
|
||||
function getNodeVersion() {
|
||||
const yarnrc = fs.readFileSync(path.join(REPO_ROOT, 'remote', '.yarnrc'), 'utf8');
|
||||
const target = /^target "(.*)"$/m.exec(yarnrc)[1];
|
||||
return target;
|
||||
const nodeVersion = /^target "(.*)"$/m.exec(yarnrc)[1];
|
||||
const internalNodeVersion = /^ms_build_id "(.*)"$/m.exec(yarnrc)[1];
|
||||
return { nodeVersion, internalNodeVersion };
|
||||
}
|
||||
|
||||
function getNodeChecksum(nodeVersion, platform, arch) {
|
||||
@@ -156,7 +157,7 @@ function getNodeChecksum(nodeVersion, platform, arch) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const nodeVersion = getNodeVersion();
|
||||
const { nodeVersion, internalNodeVersion } = getNodeVersion();
|
||||
|
||||
BUILD_TARGETS.forEach(({ platform, arch }) => {
|
||||
gulp.task(task.define(`node-${platform}-${arch}`, () => {
|
||||
@@ -193,7 +194,7 @@ function nodejs(platform, arch) {
|
||||
arch = 'x64';
|
||||
}
|
||||
|
||||
log(`Downloading node.js ${nodeVersion} ${platform} ${arch} from ${product.nodejs.repository}...`);
|
||||
log(`Downloading node.js ${nodeVersion} ${platform} ${arch} from ${product.nodejsRepository}...`);
|
||||
|
||||
const checksumSha256 = getNodeChecksum(nodeVersion, platform, arch);
|
||||
|
||||
@@ -205,14 +206,14 @@ function nodejs(platform, arch) {
|
||||
|
||||
switch (platform) {
|
||||
case 'win32':
|
||||
return (product.nodejs.repository !== 'https://nodejs.org' ?
|
||||
fetchGithub(product.nodejs.repository, { version: product.nodejs.version, name: `win-${arch}-node.exe`, checksumSha256 }) :
|
||||
return (product.nodejsRepository !== 'https://nodejs.org' ?
|
||||
fetchGithub(product.nodejsRepository, { version: `${nodeVersion}-${internalNodeVersion}`, name: `win-${arch}-node.exe`, checksumSha256 }) :
|
||||
fetchUrls(`/dist/v${nodeVersion}/win-${arch}/node.exe`, { base: 'https://nodejs.org', checksumSha256 }))
|
||||
.pipe(rename('node.exe'));
|
||||
case 'darwin':
|
||||
case 'linux':
|
||||
return (product.nodejs.repository !== 'https://nodejs.org' ?
|
||||
fetchGithub(product.nodejs.repository, { version: product.nodejs.version, name: `node-v${nodeVersion}-${platform}-${arch}.tar.gz`, checksumSha256 }) :
|
||||
return (product.nodejsRepository !== 'https://nodejs.org' ?
|
||||
fetchGithub(product.nodejsRepository, { version: `${nodeVersion}-${internalNodeVersion}`, name: `node-v${nodeVersion}-${platform}-${arch}.tar.gz`, checksumSha256 }) :
|
||||
fetchUrls(`/dist/v${nodeVersion}/node-v${nodeVersion}-${platform}-${arch}.tar.gz`, { base: 'https://nodejs.org', checksumSha256 })
|
||||
).pipe(flatmap(stream => stream.pipe(gunzip()).pipe(untar())))
|
||||
.pipe(filter('**/node'))
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -90,8 +90,11 @@ function darwinBundleDocumentTypes(types: { [name: string]: string | string[] },
|
||||
});
|
||||
}
|
||||
|
||||
const { electronVersion, msBuildId } = util.getElectronVersion();
|
||||
|
||||
export const config = {
|
||||
version: product.electronRepository ? '22.5.7' : util.getElectronVersion(),
|
||||
version: electronVersion,
|
||||
tag: product.electronRepository ? `v${electronVersion}-${msBuildId}` : undefined,
|
||||
productAppName: product.nameLong,
|
||||
companyName: 'Microsoft Corporation',
|
||||
copyright: 'Copyright (C) 2023 Microsoft. All rights reserved',
|
||||
@@ -212,7 +215,7 @@ function getElectron(arch: string): () => NodeJS.ReadWriteStream {
|
||||
}
|
||||
|
||||
async function main(arch = process.arch): Promise<void> {
|
||||
const version = product.electronRepository ? '22.5.7' : util.getElectronVersion();
|
||||
const version = electronVersion;
|
||||
const electronPath = path.join(root, '.build', 'electron');
|
||||
const versionFile = path.join(electronPath, 'version');
|
||||
const isUpToDate = fs.existsSync(versionFile) && fs.readFileSync(versionFile, 'utf8') === `${version}`;
|
||||
|
||||
@@ -312,6 +312,7 @@
|
||||
"--vscode-inputValidation-warningBackground",
|
||||
"--vscode-inputValidation-warningBorder",
|
||||
"--vscode-inputValidation-warningForeground",
|
||||
"--vscode-inlineChat-background",
|
||||
"--vscode-inlineChat-border",
|
||||
"--vscode-inlineChat-regionHighlight",
|
||||
"--vscode-inlineChat-shadow",
|
||||
|
||||
+4
-3
File diff suppressed because one or more lines are too long
+4
-3
@@ -384,10 +384,11 @@ export function streamToPromise(stream: NodeJS.ReadWriteStream): Promise<void> {
|
||||
});
|
||||
}
|
||||
|
||||
export function getElectronVersion(): string {
|
||||
export function getElectronVersion(): Record<string, string> {
|
||||
const yarnrc = fs.readFileSync(path.join(root, '.yarnrc'), 'utf8');
|
||||
const target = /^target "(.*)"$/m.exec(yarnrc)![1];
|
||||
return target;
|
||||
const electronVersion = /^target "(.*)"$/m.exec(yarnrc)![1];
|
||||
const msBuildId = /^ms_build_id "(.*)"$/m.exec(yarnrc)![1];
|
||||
return { electronVersion, msBuildId };
|
||||
}
|
||||
|
||||
export function acquireWebNodePaths() {
|
||||
|
||||
@@ -30,7 +30,7 @@ function getSha(filename) {
|
||||
return hash.digest('hex');
|
||||
}
|
||||
async function getSysroot(arch) {
|
||||
const sysrootJSONUrl = `https://raw.githubusercontent.com/electron/electron/v${util.getElectronVersion()}/script/sysroots.json`;
|
||||
const sysrootJSONUrl = `https://raw.githubusercontent.com/electron/electron/v${util.getElectronVersion().electronVersion}/script/sysroots.json`;
|
||||
const sysrootDictLocation = `${(0, os_1.tmpdir)()}/sysroots.json`;
|
||||
const result = (0, child_process_1.spawnSync)('curl', [sysrootJSONUrl, '-o', sysrootDictLocation]);
|
||||
if (result.status !== 0) {
|
||||
@@ -87,4 +87,4 @@ async function getSysroot(arch) {
|
||||
return sysroot;
|
||||
}
|
||||
exports.getSysroot = getSysroot;
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5zdGFsbC1zeXNyb290LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiaW5zdGFsbC1zeXNyb290LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7O2dHQUdnRzs7O0FBRWhHLGlEQUEwQztBQUMxQyxtQ0FBb0M7QUFDcEMsMkJBQTRCO0FBQzVCLHlCQUF5QjtBQUN6QiwrQkFBK0I7QUFDL0IsNkJBQTZCO0FBRTdCLHVDQUF1QztBQUV2QyxvSEFBb0g7QUFDcEgsTUFBTSxVQUFVLEdBQUcsNENBQTRDLENBQUM7QUFDaEUsTUFBTSxRQUFRLEdBQUcsb0JBQW9CLENBQUM7QUFFdEMsU0FBUyxNQUFNLENBQUMsUUFBcUI7SUFDcEMsTUFBTSxJQUFJLEdBQUcsSUFBQSxtQkFBVSxFQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQ2hDLDJCQUEyQjtJQUMzQixNQUFNLEVBQUUsR0FBRyxFQUFFLENBQUMsUUFBUSxDQUFDLFFBQVEsRUFBRSxHQUFHLENBQUMsQ0FBQztJQUN0QyxNQUFNLE1BQU0sR0FBRyxNQUFNLENBQUMsS0FBSyxDQUFDLElBQUksR0FBRyxJQUFJLENBQUMsQ0FBQztJQUN6QyxJQUFJLFFBQVEsR0FBRyxDQUFDLENBQUM7SUFDakIsSUFBSSxTQUFTLEdBQUcsQ0FBQyxDQUFDO0lBQ2xCLE9BQU8sQ0FBQyxTQUFTLEdBQUcsRUFBRSxDQUFDLFFBQVEsQ0FBQyxFQUFFLEVBQUUsTUFBTSxFQUFFLENBQUMsRUFBRSxNQUFNLENBQUMsTUFBTSxFQUFFLFFBQVEsQ0FBQyxDQUFDLEtBQUssTUFBTSxDQUFDLE1BQU0sRUFBRTtRQUMzRixJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ3BCLFFBQVEsSUFBSSxTQUFTLENBQUM7S0FDdEI7SUFDRCxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLFNBQVMsQ0FBQyxDQUFDLENBQUM7SUFDeEMsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO0FBQzNCLENBQUM7QUFRTSxLQUFLLFVBQVUsVUFBVSxDQUFDLElBQXNCO0lBQ3RELE1BQU0sY0FBYyxHQUFHLHdEQUF3RCxJQUFJLENBQUMsa0JBQWtCLEVBQUUsdUJBQXVCLENBQUM7SUFDaEksTUFBTSxtQkFBbUIsR0FBRyxHQUFHLElBQUEsV0FBTSxHQUFFLGdCQUFnQixDQUFDO0lBQ3hELE1BQU0sTUFBTSxHQUFHLElBQUEseUJBQVMsRUFBQyxNQUFNLEVBQUUsQ0FBQyxjQUFjLEVBQUUsSUFBSSxFQUFFLG1CQUFtQixDQUFDLENBQUMsQ0FBQztJQUM5RSxJQUFJLE1BQU0sQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFO1FBQ3hCLE1BQU0sSUFBSSxLQUFLLENBQUMsMENBQTBDLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDO0tBQzVFO0lBQ0QsTUFBTSxXQUFXLEdBQUcsT0FBTyxDQUFDLG1CQUFtQixDQUFDLENBQUM7SUFDakQsTUFBTSxXQUFXLEdBQUcsSUFBSSxLQUFLLE9BQU8sQ0FBQyxDQUFDLENBQUMsY0FBYyxDQUFDLENBQUMsQ0FBQyxZQUFZLElBQUksRUFBRSxDQUFDO0lBQzNFLE1BQU0sV0FBVyxHQUFxQixXQUFXLENBQUMsV0FBVyxDQUFDLENBQUM7SUFDL0QsTUFBTSxlQUFlLEdBQUcsV0FBVyxDQUFDLFNBQVMsQ0FBQyxDQUFDO0lBQy9DLE1BQU0sVUFBVSxHQUFHLFdBQVcsQ0FBQyxTQUFTLENBQUMsQ0FBQztJQUMxQyxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUEsV0FBTSxHQUFFLEVBQUUsV0FBVyxDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUM7SUFDL0QsTUFBTSxHQUFHLEdBQUcsQ0FBQyxVQUFVLEVBQUUsUUFBUSxFQUFFLFVBQVUsRUFBRSxlQUFlLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDMUUsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsUUFBUSxDQUFDLENBQUM7SUFDM0MsSUFBSSxFQUFFLENBQUMsVUFBVSxDQUFDLEtBQUssQ0FBQyxJQUFJLEVBQUUsQ0FBQyxZQUFZLENBQUMsS0FBSyxDQUFDLENBQUMsUUFBUSxFQUFFLEtBQUssR0FBRyxFQUFFO1FBQ3RFLE9BQU8sT0FBTyxDQUFDO0tBQ2Y7SUFFRCxPQUFPLENBQUMsR0FBRyxDQUFDLHFCQUFxQixJQUFJLGdCQUFnQixPQUFPLEVBQUUsQ0FBQyxDQUFDO0lBQ2hFLEVBQUUsQ0FBQyxNQUFNLENBQUMsT0FBTyxFQUFFLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztJQUNyRCxFQUFFLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQ3RCLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLGVBQWUsQ0FBQyxDQUFDO0lBQ3BELE9BQU8sQ0FBQyxHQUFHLENBQUMsZUFBZSxHQUFHLEVBQUUsQ0FBQyxDQUFDO0lBQ2xDLElBQUksZUFBZSxHQUFHLEtBQUssQ0FBQztJQUM1QixLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsZUFBZSxFQUFFLENBQUMsRUFBRSxFQUFFO1FBQy9DLEVBQUUsQ0FBQyxhQUFhLENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBQzlCLE1BQU0sSUFBSSxPQUFPLENBQU8sQ0FBQyxDQUFDLEVBQUUsRUFBRTtZQUM3QixLQUFLLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRSxDQUFDLEdBQUcsRUFBRSxFQUFFO2dCQUN0QixHQUFHLENBQUMsRUFBRSxDQUFDLE1BQU0sRUFBRSxDQUFDLEtBQUssRUFBRSxFQUFFO29CQUN4QixFQUFFLENBQUMsY0FBYyxDQUFDLE9BQU8sRUFBRSxLQUFLLENBQUMsQ0FBQztnQkFDbkMsQ0FBQyxDQUFDLENBQUM7Z0JBQ0gsR0FBRyxDQUFDLEVBQUUsQ0FBQyxLQUFLLEVBQUUsR0FBRyxFQUFFO29CQUNsQixlQUFlLEdBQUcsSUFBSSxDQUFDO29CQUN2QixDQUFDLEVBQUUsQ0FBQztnQkFDTCxDQUFDLENBQUMsQ0FBQztZQUNKLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxHQUFHLEVBQUUsRUFBRTtnQkFDdEIsT0FBTyxDQUFDLEtBQUssQ0FBQyxvREFBb0QsR0FBRyxHQUFHLENBQUMsT0FBTyxDQUFDLENBQUM7Z0JBQ2xGLENBQUMsRUFBRSxDQUFDO1lBQ0wsQ0FBQyxDQUFDLENBQUM7UUFDSixDQUFDLENBQUMsQ0FBQztLQUNIO0lBQ0QsSUFBSSxDQUFDLGVBQWUsRUFBRTtRQUNyQixFQUFFLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ25CLE1BQU0sSUFBSSxLQUFLLENBQUMscUJBQXFCLEdBQUcsR0FBRyxDQUFDLENBQUM7S0FDN0M7SUFDRCxNQUFNLEdBQUcsR0FBRyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDNUIsSUFBSSxHQUFHLEtBQUssVUFBVSxFQUFFO1FBQ3ZCLE1BQU0sSUFBSSxLQUFLLENBQUMsc0NBQXNDLFVBQVUsWUFBWSxHQUFHLEVBQUUsQ0FBQyxDQUFDO0tBQ25GO0lBRUQsTUFBTSxJQUFJLEdBQUcsSUFBQSx5QkFBUyxFQUFDLEtBQUssRUFBRSxDQUFDLElBQUksRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUM7SUFDOUQsSUFBSSxJQUFJLENBQUMsTUFBTSxFQUFFO1FBQ2hCLE1BQU0sSUFBSSxLQUFLLENBQUMsc0NBQXNDLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDO0tBQ3RFO0lBQ0QsRUFBRSxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUNuQixFQUFFLENBQUMsYUFBYSxDQUFDLEtBQUssRUFBRSxHQUFHLENBQUMsQ0FBQztJQUM3QixPQUFPLE9BQU8sQ0FBQztBQUNoQixDQUFDO0FBMURELGdDQTBEQyJ9
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5zdGFsbC1zeXNyb290LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiaW5zdGFsbC1zeXNyb290LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7O2dHQUdnRzs7O0FBRWhHLGlEQUEwQztBQUMxQyxtQ0FBb0M7QUFDcEMsMkJBQTRCO0FBQzVCLHlCQUF5QjtBQUN6QiwrQkFBK0I7QUFDL0IsNkJBQTZCO0FBRTdCLHVDQUF1QztBQUV2QyxvSEFBb0g7QUFDcEgsTUFBTSxVQUFVLEdBQUcsNENBQTRDLENBQUM7QUFDaEUsTUFBTSxRQUFRLEdBQUcsb0JBQW9CLENBQUM7QUFFdEMsU0FBUyxNQUFNLENBQUMsUUFBcUI7SUFDcEMsTUFBTSxJQUFJLEdBQUcsSUFBQSxtQkFBVSxFQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQ2hDLDJCQUEyQjtJQUMzQixNQUFNLEVBQUUsR0FBRyxFQUFFLENBQUMsUUFBUSxDQUFDLFFBQVEsRUFBRSxHQUFHLENBQUMsQ0FBQztJQUN0QyxNQUFNLE1BQU0sR0FBRyxNQUFNLENBQUMsS0FBSyxDQUFDLElBQUksR0FBRyxJQUFJLENBQUMsQ0FBQztJQUN6QyxJQUFJLFFBQVEsR0FBRyxDQUFDLENBQUM7SUFDakIsSUFBSSxTQUFTLEdBQUcsQ0FBQyxDQUFDO0lBQ2xCLE9BQU8sQ0FBQyxTQUFTLEdBQUcsRUFBRSxDQUFDLFFBQVEsQ0FBQyxFQUFFLEVBQUUsTUFBTSxFQUFFLENBQUMsRUFBRSxNQUFNLENBQUMsTUFBTSxFQUFFLFFBQVEsQ0FBQyxDQUFDLEtBQUssTUFBTSxDQUFDLE1BQU0sRUFBRTtRQUMzRixJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ3BCLFFBQVEsSUFBSSxTQUFTLENBQUM7S0FDdEI7SUFDRCxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLFNBQVMsQ0FBQyxDQUFDLENBQUM7SUFDeEMsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO0FBQzNCLENBQUM7QUFRTSxLQUFLLFVBQVUsVUFBVSxDQUFDLElBQXNCO0lBQ3RELE1BQU0sY0FBYyxHQUFHLHdEQUF3RCxJQUFJLENBQUMsa0JBQWtCLEVBQUUsQ0FBQyxlQUFlLHVCQUF1QixDQUFDO0lBQ2hKLE1BQU0sbUJBQW1CLEdBQUcsR0FBRyxJQUFBLFdBQU0sR0FBRSxnQkFBZ0IsQ0FBQztJQUN4RCxNQUFNLE1BQU0sR0FBRyxJQUFBLHlCQUFTLEVBQUMsTUFBTSxFQUFFLENBQUMsY0FBYyxFQUFFLElBQUksRUFBRSxtQkFBbUIsQ0FBQyxDQUFDLENBQUM7SUFDOUUsSUFBSSxNQUFNLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRTtRQUN4QixNQUFNLElBQUksS0FBSyxDQUFDLDBDQUEwQyxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQztLQUM1RTtJQUNELE1BQU0sV0FBVyxHQUFHLE9BQU8sQ0FBQyxtQkFBbUIsQ0FBQyxDQUFDO0lBQ2pELE1BQU0sV0FBVyxHQUFHLElBQUksS0FBSyxPQUFPLENBQUMsQ0FBQyxDQUFDLGNBQWMsQ0FBQyxDQUFDLENBQUMsWUFBWSxJQUFJLEVBQUUsQ0FBQztJQUMzRSxNQUFNLFdBQVcsR0FBcUIsV0FBVyxDQUFDLFdBQVcsQ0FBQyxDQUFDO0lBQy9ELE1BQU0sZUFBZSxHQUFHLFdBQVcsQ0FBQyxTQUFTLENBQUMsQ0FBQztJQUMvQyxNQUFNLFVBQVUsR0FBRyxXQUFXLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDMUMsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFBLFdBQU0sR0FBRSxFQUFFLFdBQVcsQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFDO0lBQy9ELE1BQU0sR0FBRyxHQUFHLENBQUMsVUFBVSxFQUFFLFFBQVEsRUFBRSxVQUFVLEVBQUUsZUFBZSxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQzFFLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLFFBQVEsQ0FBQyxDQUFDO0lBQzNDLElBQUksRUFBRSxDQUFDLFVBQVUsQ0FBQyxLQUFLLENBQUMsSUFBSSxFQUFFLENBQUMsWUFBWSxDQUFDLEtBQUssQ0FBQyxDQUFDLFFBQVEsRUFBRSxLQUFLLEdBQUcsRUFBRTtRQUN0RSxPQUFPLE9BQU8sQ0FBQztLQUNmO0lBRUQsT0FBTyxDQUFDLEdBQUcsQ0FBQyxxQkFBcUIsSUFBSSxnQkFBZ0IsT0FBTyxFQUFFLENBQUMsQ0FBQztJQUNoRSxFQUFFLENBQUMsTUFBTSxDQUFDLE9BQU8sRUFBRSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsS0FBSyxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7SUFDckQsRUFBRSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUN0QixNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxlQUFlLENBQUMsQ0FBQztJQUNwRCxPQUFPLENBQUMsR0FBRyxDQUFDLGVBQWUsR0FBRyxFQUFFLENBQUMsQ0FBQztJQUNsQyxJQUFJLGVBQWUsR0FBRyxLQUFLLENBQUM7SUFDNUIsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLGVBQWUsRUFBRSxDQUFDLEVBQUUsRUFBRTtRQUMvQyxFQUFFLENBQUMsYUFBYSxDQUFDLE9BQU8sRUFBRSxFQUFFLENBQUMsQ0FBQztRQUM5QixNQUFNLElBQUksT0FBTyxDQUFPLENBQUMsQ0FBQyxFQUFFLEVBQUU7WUFDN0IsS0FBSyxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxHQUFHLEVBQUUsRUFBRTtnQkFDdEIsR0FBRyxDQUFDLEVBQUUsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxLQUFLLEVBQUUsRUFBRTtvQkFDeEIsRUFBRSxDQUFDLGNBQWMsQ0FBQyxPQUFPLEVBQUUsS0FBSyxDQUFDLENBQUM7Z0JBQ25DLENBQUMsQ0FBQyxDQUFDO2dCQUNILEdBQUcsQ0FBQyxFQUFFLENBQUMsS0FBSyxFQUFFLEdBQUcsRUFBRTtvQkFDbEIsZUFBZSxHQUFHLElBQUksQ0FBQztvQkFDdkIsQ0FBQyxFQUFFLENBQUM7Z0JBQ0wsQ0FBQyxDQUFDLENBQUM7WUFDSixDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLENBQUMsR0FBRyxFQUFFLEVBQUU7Z0JBQ3RCLE9BQU8sQ0FBQyxLQUFLLENBQUMsb0RBQW9ELEdBQUcsR0FBRyxDQUFDLE9BQU8sQ0FBQyxDQUFDO2dCQUNsRixDQUFDLEVBQUUsQ0FBQztZQUNMLENBQUMsQ0FBQyxDQUFDO1FBQ0osQ0FBQyxDQUFDLENBQUM7S0FDSDtJQUNELElBQUksQ0FBQyxlQUFlLEVBQUU7UUFDckIsRUFBRSxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUNuQixNQUFNLElBQUksS0FBSyxDQUFDLHFCQUFxQixHQUFHLEdBQUcsQ0FBQyxDQUFDO0tBQzdDO0lBQ0QsTUFBTSxHQUFHLEdBQUcsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQzVCLElBQUksR0FBRyxLQUFLLFVBQVUsRUFBRTtRQUN2QixNQUFNLElBQUksS0FBSyxDQUFDLHNDQUFzQyxVQUFVLFlBQVksR0FBRyxFQUFFLENBQUMsQ0FBQztLQUNuRjtJQUVELE1BQU0sSUFBSSxHQUFHLElBQUEseUJBQVMsRUFBQyxLQUFLLEVBQUUsQ0FBQyxJQUFJLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxPQUFPLENBQUMsQ0FBQyxDQUFDO0lBQzlELElBQUksSUFBSSxDQUFDLE1BQU0sRUFBRTtRQUNoQixNQUFNLElBQUksS0FBSyxDQUFDLHNDQUFzQyxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQztLQUN0RTtJQUNELEVBQUUsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDbkIsRUFBRSxDQUFDLGFBQWEsQ0FBQyxLQUFLLEVBQUUsR0FBRyxDQUFDLENBQUM7SUFDN0IsT0FBTyxPQUFPLENBQUM7QUFDaEIsQ0FBQztBQTFERCxnQ0EwREMifQ==
|
||||
@@ -38,7 +38,7 @@ type SysrootDictEntry = {
|
||||
};
|
||||
|
||||
export async function getSysroot(arch: DebianArchString): Promise<string> {
|
||||
const sysrootJSONUrl = `https://raw.githubusercontent.com/electron/electron/v${util.getElectronVersion()}/script/sysroots.json`;
|
||||
const sysrootJSONUrl = `https://raw.githubusercontent.com/electron/electron/v${util.getElectronVersion().electronVersion}/script/sysroots.json`;
|
||||
const sysrootDictLocation = `${tmpdir()}/sysroots.json`;
|
||||
const result = spawnSync('curl', [sysrootJSONUrl, '-o', sysrootDictLocation]);
|
||||
if (result.status !== 0) {
|
||||
|
||||
+1
-1
@@ -533,7 +533,7 @@
|
||||
},
|
||||
"isOnlyProductionDependency": true,
|
||||
"license": "MIT",
|
||||
"version": "22.3.10"
|
||||
"version": "22.3.11"
|
||||
},
|
||||
{
|
||||
"component": {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
use std::{fmt, path::PathBuf};
|
||||
|
||||
use crate::{constants, log, options, tunnels::code_server::CodeServerArgs};
|
||||
use clap::{ValueEnum, Args, Parser, Subcommand};
|
||||
use clap::{Args, Parser, Subcommand, ValueEnum};
|
||||
use const_format::concatcp;
|
||||
|
||||
const CLI_NAME: &str = concatcp!(constants::PRODUCT_NAME_LONG, " CLI");
|
||||
|
||||
+38
-14
@@ -4,7 +4,8 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
use async_trait::async_trait;
|
||||
use base64::{Engine as _, engine::general_purpose as b64};
|
||||
use base64::{engine::general_purpose as b64, Engine as _};
|
||||
use serde::Serialize;
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::{str::FromStr, time::Duration};
|
||||
use sysinfo::Pid;
|
||||
@@ -247,8 +248,14 @@ pub async fn kill(ctx: CommandContext) -> Result<i32, AnyError> {
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct StatusOutput {
|
||||
pub tunnel: Option<protocol::singleton::TunnelState>,
|
||||
pub service_installed: bool,
|
||||
}
|
||||
|
||||
pub async fn status(ctx: CommandContext) -> Result<i32, AnyError> {
|
||||
let status = do_single_rpc_call::<_, protocol::singleton::Status>(
|
||||
let tunnel_status = do_single_rpc_call::<_, protocol::singleton::Status>(
|
||||
&ctx.paths.tunnel_lockfile(),
|
||||
ctx.log.clone(),
|
||||
protocol::singleton::METHOD_STATUS,
|
||||
@@ -256,17 +263,24 @@ pub async fn status(ctx: CommandContext) -> Result<i32, AnyError> {
|
||||
)
|
||||
.await;
|
||||
|
||||
match status {
|
||||
Err(CodeError::NoRunningTunnel) => {
|
||||
ctx.log.result(CodeError::NoRunningTunnel.to_string());
|
||||
Ok(1)
|
||||
}
|
||||
Err(e) => Err(e.into()),
|
||||
Ok(s) => {
|
||||
ctx.log.result(serde_json::to_string(&s).unwrap());
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
let service_installed = create_service_manager(ctx.log.clone(), &ctx.paths)
|
||||
.is_installed()
|
||||
.await
|
||||
.unwrap_or(false);
|
||||
|
||||
ctx.log.result(
|
||||
serde_json::to_string(&StatusOutput {
|
||||
service_installed,
|
||||
tunnel: match tunnel_status {
|
||||
Ok(s) => Some(s.tunnel),
|
||||
Err(CodeError::NoRunningTunnel) => None,
|
||||
Err(e) => return Err(e.into()),
|
||||
},
|
||||
})
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
/// Removes unused servers.
|
||||
@@ -331,6 +345,13 @@ async fn serve_with_csa(
|
||||
log = log.tee(log_broadcast.clone());
|
||||
log::install_global_logger(log.clone()); // re-install so that library logs are captured
|
||||
|
||||
debug!(
|
||||
log,
|
||||
"Starting tunnel with `{} {}`",
|
||||
APPLICATION_NAME,
|
||||
std::env::args().collect::<Vec<_>>().join(" ")
|
||||
);
|
||||
|
||||
// Intentionally read before starting the server. If the server updated and
|
||||
// respawn is requested, the old binary will get renamed, and then
|
||||
// current_exe will point to the wrong path.
|
||||
@@ -421,7 +442,10 @@ async fn serve_with_csa(
|
||||
|
||||
return Ok(exit.code().unwrap_or(1));
|
||||
}
|
||||
Next::Exit => return Ok(0),
|
||||
Next::Exit => {
|
||||
debug!(log, "Tunnel shut down");
|
||||
return Ok(0);
|
||||
}
|
||||
Next::Restart => continue,
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -18,8 +18,8 @@ pub mod tunnels;
|
||||
pub mod update_service;
|
||||
pub mod util;
|
||||
|
||||
mod download_cache;
|
||||
mod async_pipe;
|
||||
mod download_cache;
|
||||
mod json_rpc;
|
||||
mod msgpack_rpc;
|
||||
mod rpc;
|
||||
|
||||
+13
-1
@@ -159,9 +159,21 @@ pub struct FileLogSink {
|
||||
file: Arc<std::sync::Mutex<std::fs::File>>,
|
||||
}
|
||||
|
||||
const FILE_LOG_SIZE_LIMIT: u64 = 1024 * 1024 * 10; // 10MB
|
||||
|
||||
impl FileLogSink {
|
||||
pub fn new(level: Level, path: &Path) -> std::io::Result<Self> {
|
||||
let file = std::fs::File::create(path)?;
|
||||
// Truncate the service log occasionally to avoid growing infinitely
|
||||
if matches!(path.metadata(), Ok(m) if m.len() > FILE_LOG_SIZE_LIMIT) {
|
||||
// ignore errors, can happen if another process is writing right now
|
||||
let _ = std::fs::remove_file(path);
|
||||
}
|
||||
|
||||
let file = std::fs::OpenOptions::new()
|
||||
.append(true)
|
||||
.create(true)
|
||||
.open(path)?;
|
||||
|
||||
Ok(Self {
|
||||
level,
|
||||
file: Arc::new(std::sync::Mutex::new(file)),
|
||||
|
||||
@@ -41,6 +41,9 @@ pub trait ServiceManager {
|
||||
/// Show logs from the running service to standard out.
|
||||
async fn show_logs(&self) -> Result<(), AnyError>;
|
||||
|
||||
/// Gets whether the tunnel service is installed.
|
||||
async fn is_installed(&self) -> Result<bool, AnyError>;
|
||||
|
||||
/// Unregisters the current executable as a service.
|
||||
async fn unregister(&self) -> Result<(), AnyError>;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ use crate::{
|
||||
constants::{APPLICATION_NAME, PRODUCT_NAME_LONG},
|
||||
log,
|
||||
state::LauncherPaths,
|
||||
util::errors::{wrap, AnyError},
|
||||
util::errors::{wrap, AnyError, DbusConnectFailedError},
|
||||
};
|
||||
|
||||
use super::ServiceManager;
|
||||
@@ -40,7 +40,7 @@ impl SystemdService {
|
||||
async fn connect() -> Result<Connection, AnyError> {
|
||||
let connection = Connection::session()
|
||||
.await
|
||||
.map_err(|e| wrap(e, "error creating dbus session"))?;
|
||||
.map_err(|e| DbusConnectFailedError(e.to_string()))?;
|
||||
Ok(connection)
|
||||
}
|
||||
|
||||
@@ -110,9 +110,27 @@ impl ServiceManager for SystemdService {
|
||||
|
||||
info!(self.log, "Tunnel service successfully started");
|
||||
|
||||
if std::env::var("SSH_CLIENT").is_ok() || std::env::var("SSH_TTY").is_ok() {
|
||||
info!(self.log, "Tip: run `sudo loginctl enable-linger $USER` to ensure the service stays running after you disconnect.");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn is_installed(&self) -> Result<bool, AnyError> {
|
||||
let connection = SystemdService::connect().await?;
|
||||
let proxy = SystemdService::proxy(&connection).await?;
|
||||
let state = proxy
|
||||
.get_unit_file_state(SystemdService::service_name_string())
|
||||
.await;
|
||||
|
||||
if let Ok(s) = state {
|
||||
Ok(s == "enabled")
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
async fn run(
|
||||
self,
|
||||
launcher_paths: crate::state::LauncherPaths,
|
||||
@@ -219,6 +237,8 @@ trait SystemdManagerDbus {
|
||||
force: bool,
|
||||
) -> zbus::Result<(bool, Vec<(String, String, String)>)>;
|
||||
|
||||
fn get_unit_file_state(&self, file: String) -> zbus::Result<String>;
|
||||
|
||||
fn link_unit_files(
|
||||
&self,
|
||||
files: Vec<String>,
|
||||
|
||||
@@ -75,6 +75,11 @@ impl ServiceManager for LaunchdService {
|
||||
handle.run_service(self.log, launcher_paths).await
|
||||
}
|
||||
|
||||
async fn is_installed(&self) -> Result<bool, AnyError> {
|
||||
let cmd = capture_command_and_check_status("launchctl", &["list"]).await?;
|
||||
Ok(String::from_utf8_lossy(&cmd.stdout).contains(&get_service_label()))
|
||||
}
|
||||
|
||||
async fn unregister(&self) -> Result<(), crate::util::errors::AnyError> {
|
||||
let service_file = get_service_file_path()?;
|
||||
|
||||
|
||||
@@ -114,6 +114,11 @@ impl CliServiceManager for WindowsService {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn is_installed(&self) -> Result<bool, AnyError> {
|
||||
let key = WindowsService::open_key()?;
|
||||
Ok(key.get_raw_value(TUNNEL_ACTIVITY_NAME).is_ok())
|
||||
}
|
||||
|
||||
async fn unregister(&self) -> Result<(), AnyError> {
|
||||
let key = WindowsService::open_key()?;
|
||||
key.delete_value(TUNNEL_ACTIVITY_NAME)
|
||||
|
||||
+22
-1
@@ -442,6 +442,26 @@ macro_rules! makeAnyError {
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DbusConnectFailedError(pub String);
|
||||
|
||||
impl Display for DbusConnectFailedError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
let mut str = String::new();
|
||||
str.push_str("Error creating dbus session. This command uses systemd for managing services, you should check that systemd is installed and under your user.");
|
||||
|
||||
if std::env::var("WSL_DISTRO_NAME").is_ok() {
|
||||
str.push_str("\n\nTo enable systemd on WSL, check out: https://devblogs.microsoft.com/commandline/systemd-support-is-now-available-in-wsl/.\n\n");
|
||||
}
|
||||
|
||||
str.push_str("If running `systemctl status` works, systemd is ok, but your session dbus may not be. You might need to:\n\n- Install the `dbus-user-session` package, and reboot if it was not installed\n- Start the user dbus session with `systemctl --user enable dbus --now`.\n\nThe error encountered was: ");
|
||||
str.push_str(&self.0);
|
||||
str.push('\n');
|
||||
|
||||
write!(f, "{}", str)
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal errors in the VS Code CLI.
|
||||
/// Note: other error should be migrated to this type gradually
|
||||
#[derive(Error, Debug)]
|
||||
@@ -522,7 +542,8 @@ makeAnyError!(
|
||||
MissingHomeDirectory,
|
||||
OAuthError,
|
||||
InvalidRpcDataError,
|
||||
CodeError
|
||||
CodeError,
|
||||
DbusConnectFailedError
|
||||
);
|
||||
|
||||
impl From<reqwest::Error> for AnyError {
|
||||
|
||||
+2
-2
@@ -6,7 +6,7 @@ use crate::util::errors::{wrap, WrappedError};
|
||||
|
||||
use flate2::read::GzDecoder;
|
||||
use std::fs;
|
||||
use std::io::{Seek, SeekFrom};
|
||||
use std::io::Seek;
|
||||
use std::path::{Path, PathBuf};
|
||||
use tar::Archive;
|
||||
|
||||
@@ -65,7 +65,7 @@ where
|
||||
|
||||
// reset since skip logic read the tar already:
|
||||
tar_gz
|
||||
.seek(SeekFrom::Start(0))
|
||||
.rewind()
|
||||
.map_err(|e| wrap(e, "error resetting seek position"))?;
|
||||
|
||||
let tar = GzDecoder::new(tar_gz);
|
||||
|
||||
@@ -88,8 +88,13 @@ where
|
||||
use std::io::Read;
|
||||
use std::os::unix::ffi::OsStringExt;
|
||||
|
||||
if matches!(file.unix_mode(), Some(mode) if mode & (S_IFLNK as u32) == (S_IFLNK as u32))
|
||||
{
|
||||
#[cfg(target_os = "macos")]
|
||||
const S_IFLINK_32: u32 = S_IFLNK as u32;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
const S_IFLINK_32: u32 = S_IFLNK;
|
||||
|
||||
if matches!(file.unix_mode(), Some(mode) if mode & S_IFLINK_32 == S_IFLINK_32) {
|
||||
let mut link_to = Vec::new();
|
||||
file.read_to_end(&mut link_to).map_err(|e| {
|
||||
wrap(
|
||||
|
||||
+18
-23
@@ -2,19 +2,19 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@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==
|
||||
"@emmetio/abbreviation@^2.3.3":
|
||||
version "2.3.3"
|
||||
resolved "https://registry.yarnpkg.com/@emmetio/abbreviation/-/abbreviation-2.3.3.tgz#ed2b88fe37b972292d6026c7c540aaf887cecb6e"
|
||||
integrity sha512-mgv58UrU3rh4YgbE/TzgLQwJ3pFsHHhCLqY20aJq+9comytTXUDNGG/SMtSeMJdkpxgXSXunBGLD8Boka3JyVA==
|
||||
dependencies:
|
||||
"@emmetio/scanner" "^1.0.3"
|
||||
"@emmetio/scanner" "^1.0.4"
|
||||
|
||||
"@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==
|
||||
"@emmetio/css-abbreviation@^2.1.8":
|
||||
version "2.1.8"
|
||||
resolved "https://registry.yarnpkg.com/@emmetio/css-abbreviation/-/css-abbreviation-2.1.8.tgz#b785313486eba6cb7eb623ad39378c4e1063dc00"
|
||||
integrity sha512-s9yjhJ6saOO/uk1V74eifykk2CBYi01STTK3WlXWGOepyKa23ymJ053+DNQjpFcy1ingpaO7AxCcwLvHFY9tuw==
|
||||
dependencies:
|
||||
"@emmetio/scanner" "^1.0.3"
|
||||
"@emmetio/scanner" "^1.0.4"
|
||||
|
||||
"@emmetio/css-parser@ramya-rao-a/css-parser#vscode":
|
||||
version "0.4.0"
|
||||
@@ -38,11 +38,6 @@
|
||||
dependencies:
|
||||
"@emmetio/scanner" "^1.0.4"
|
||||
|
||||
"@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"
|
||||
@@ -64,9 +59,9 @@
|
||||
integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==
|
||||
|
||||
"@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==
|
||||
version "2.8.9"
|
||||
resolved "https://registry.yarnpkg.com/@vscode/emmet-helper/-/emmet-helper-2.8.9.tgz#536a2cba2f78b0dd25a874b85ab95f9b5424eb39"
|
||||
integrity sha512-ygpVStaePHt9aI9zk4NNJWI/NsRaeDSW1vQsZVmtpVRVCOdwYlsc3BfB/eppUu1OucT0x3OHDAzKcxnitjcSXQ==
|
||||
dependencies:
|
||||
emmet "^2.4.3"
|
||||
jsonc-parser "^2.3.0"
|
||||
@@ -75,12 +70,12 @@
|
||||
vscode-uri "^2.1.2"
|
||||
|
||||
emmet@^2.4.3:
|
||||
version "2.4.3"
|
||||
resolved "https://registry.yarnpkg.com/emmet/-/emmet-2.4.3.tgz#c99f19e572a270da27f456dd7f65dfda83dc0ec1"
|
||||
integrity sha512-Bq6zozVDVrLbBmKdosI9Q2DvrFh/ehwnNjgDRsvGVjPOEAhMKie9HwQnPuUi3NOZ2itVGyRwsLAdufnG9DVFwg==
|
||||
version "2.4.4"
|
||||
resolved "https://registry.yarnpkg.com/emmet/-/emmet-2.4.4.tgz#801aad64659dc76f3003130db767d77a78ac298e"
|
||||
integrity sha512-v8Mwpjym55CS3EjJgiCLWUB3J2HSR93jhzXW325720u8KvYxdI2voYLstW3pHBxFz54H6jFjayR9G4LfTG0q+g==
|
||||
dependencies:
|
||||
"@emmetio/abbreviation" "^2.3.2"
|
||||
"@emmetio/css-abbreviation" "^2.1.7"
|
||||
"@emmetio/abbreviation" "^2.3.3"
|
||||
"@emmetio/css-abbreviation" "^2.1.8"
|
||||
|
||||
image-size@~1.0.0:
|
||||
version "1.0.0"
|
||||
|
||||
@@ -314,7 +314,7 @@
|
||||
},
|
||||
{
|
||||
"match": "(?!when|and|or\\b)\\b([\\w0-9'`^._]+)",
|
||||
"comments": "Here we need the \\w modifier in order to check that the words isn't blacklisted",
|
||||
"comments": "Here we need the \\w modifier in order to check that the words are allowed",
|
||||
"captures": {
|
||||
"1": {
|
||||
"name": "entity.name.type.fsharp"
|
||||
@@ -677,7 +677,7 @@
|
||||
},
|
||||
{
|
||||
"match": "(?!with|get|set\\b)\\b([\\w0-9'`^._]+)",
|
||||
"comments": "Here we need the \\w modifier in order to check that the words isn't blacklisted",
|
||||
"comments": "Here we need the \\w modifier in order to check that the words are allowed",
|
||||
"captures": {
|
||||
"1": {
|
||||
"name": "entity.name.type.fsharp"
|
||||
@@ -1658,7 +1658,7 @@
|
||||
},
|
||||
{
|
||||
"match": "([\\w0-9'`^._]+)",
|
||||
"comments": "Here we need the \\w modifier in order to check that the words isn't blacklisted",
|
||||
"comments": "Here we need the \\w modifier in order to check that the words are allowed",
|
||||
"captures": {
|
||||
"1": {
|
||||
"name": "entity.name.type.fsharp"
|
||||
@@ -1832,4 +1832,4 @@
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { TextDecoder } from 'util';
|
||||
import { commands, env, ProgressLocation, Uri, window, workspace, QuickPickOptions, FileType, l10n } from 'vscode';
|
||||
import { commands, env, ProgressLocation, Uri, window, workspace, QuickPickOptions, FileType, l10n, Disposable, TextDocumentContentProvider } from 'vscode';
|
||||
import { getOctokit } from './auth';
|
||||
import { GitErrorCodes, PushErrorHandler, Remote, Repository } from './typings/git';
|
||||
import * as path from 'path';
|
||||
@@ -15,136 +15,6 @@ export function isInCodespaces(): boolean {
|
||||
return env.remoteName === 'codespaces';
|
||||
}
|
||||
|
||||
async function handlePushError(repository: Repository, remote: Remote, refspec: string, owner: string, repo: string): Promise<void> {
|
||||
const yes = l10n.t('Create Fork');
|
||||
const no = l10n.t('No');
|
||||
const askFork = l10n.t('You don\'t have permissions to push to "{0}/{1}" on GitHub. Would you like to create a fork and push to it instead?', owner, repo);
|
||||
|
||||
const answer = await window.showWarningMessage(askFork, { modal: true }, yes, no);
|
||||
if (answer !== yes) {
|
||||
return;
|
||||
}
|
||||
|
||||
const match = /^([^:]*):([^:]*)$/.exec(refspec);
|
||||
const localName = match ? match[1] : refspec;
|
||||
let remoteName = match ? match[2] : refspec;
|
||||
|
||||
const [octokit, ghRepository] = await window.withProgress({ location: ProgressLocation.Notification, cancellable: false, title: l10n.t('Create GitHub fork') }, async progress => {
|
||||
progress.report({ message: l10n.t('Forking "{0}/{1}"...', owner, repo), increment: 33 });
|
||||
|
||||
const octokit = await getOctokit();
|
||||
|
||||
type CreateForkResponseData = Awaited<ReturnType<typeof octokit.repos.createFork>>['data'];
|
||||
|
||||
// Issue: what if the repo already exists?
|
||||
let ghRepository: CreateForkResponseData;
|
||||
try {
|
||||
if (isInCodespaces()) {
|
||||
// Call into the codespaces extension to fork the repository
|
||||
const resp = await commands.executeCommand<{ repository: CreateForkResponseData; ref: string }>('github.codespaces.forkRepository');
|
||||
if (!resp) {
|
||||
throw new Error('Unable to fork respository');
|
||||
}
|
||||
|
||||
ghRepository = resp.repository;
|
||||
|
||||
if (resp.ref) {
|
||||
let ref = resp.ref;
|
||||
if (ref.startsWith('refs/heads/')) {
|
||||
ref = ref.substr(11);
|
||||
}
|
||||
|
||||
remoteName = ref;
|
||||
}
|
||||
} else {
|
||||
const resp = await octokit.repos.createFork({ owner, repo });
|
||||
ghRepository = resp.data;
|
||||
}
|
||||
} catch (ex) {
|
||||
console.error(ex);
|
||||
throw ex;
|
||||
}
|
||||
|
||||
progress.report({ message: l10n.t('Pushing changes...'), increment: 33 });
|
||||
|
||||
// Issue: what if there's already an `upstream` repo?
|
||||
await repository.renameRemote(remote.name, 'upstream');
|
||||
|
||||
// Issue: what if there's already another `origin` repo?
|
||||
const protocol = workspace.getConfiguration('github').get<'https' | 'ssh'>('gitProtocol');
|
||||
const remoteUrl = protocol === 'https' ? ghRepository.clone_url : ghRepository.ssh_url;
|
||||
await repository.addRemote('origin', remoteUrl);
|
||||
|
||||
try {
|
||||
await repository.fetch('origin', remoteName);
|
||||
await repository.setBranchUpstream(localName, `origin/${remoteName}`);
|
||||
} catch {
|
||||
// noop
|
||||
}
|
||||
|
||||
await repository.push('origin', localName, true);
|
||||
|
||||
return [octokit, ghRepository] as const;
|
||||
});
|
||||
|
||||
// yield
|
||||
(async () => {
|
||||
const openOnGitHub = l10n.t('Open on GitHub');
|
||||
const createPR = l10n.t('Create PR');
|
||||
const action = await window.showInformationMessage(l10n.t('The fork "{0}" was successfully created on GitHub.', ghRepository.full_name), openOnGitHub, createPR);
|
||||
|
||||
if (action === openOnGitHub) {
|
||||
await commands.executeCommand('vscode.open', Uri.parse(ghRepository.html_url));
|
||||
} else if (action === createPR) {
|
||||
const pr = await window.withProgress({ location: ProgressLocation.Notification, cancellable: false, title: l10n.t('Creating GitHub Pull Request...') }, async _ => {
|
||||
let title = `Update ${remoteName}`;
|
||||
const head = repository.state.HEAD?.name;
|
||||
|
||||
let body: string | undefined;
|
||||
|
||||
if (head) {
|
||||
const commit = await repository.getCommit(head);
|
||||
title = commit.message.split('\n')[0];
|
||||
body = commit.message.slice(title.length + 1).trim();
|
||||
}
|
||||
|
||||
const templates = await findPullRequestTemplates(repository.rootUri);
|
||||
if (templates.length > 0) {
|
||||
templates.sort((a, b) => a.path.localeCompare(b.path));
|
||||
|
||||
const template = await pickPullRequestTemplate(repository.rootUri, templates);
|
||||
|
||||
if (template) {
|
||||
body = new TextDecoder('utf-8').decode(await workspace.fs.readFile(template));
|
||||
}
|
||||
}
|
||||
|
||||
const { data: pr } = await octokit.pulls.create({
|
||||
owner,
|
||||
repo,
|
||||
title,
|
||||
body,
|
||||
head: `${ghRepository.owner.login}:${remoteName}`,
|
||||
base: ghRepository.default_branch
|
||||
});
|
||||
|
||||
await repository.setConfig(`branch.${localName}.remote`, 'upstream');
|
||||
await repository.setConfig(`branch.${localName}.merge`, `refs/heads/${remoteName}`);
|
||||
await repository.setConfig(`branch.${localName}.github-pr-owner-number`, `${owner}#${repo}#${pr.number}`);
|
||||
|
||||
return pr;
|
||||
});
|
||||
|
||||
const openPR = l10n.t('Open PR');
|
||||
const action = await window.showInformationMessage(l10n.t('The PR "{0}/{1}#{2}" was successfully created on GitHub.', owner, repo, pr.number), openPR);
|
||||
|
||||
if (action === openPR) {
|
||||
await commands.executeCommand('vscode.open', Uri.parse(pr.html_url));
|
||||
}
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
const PR_TEMPLATE_FILES = [
|
||||
{ dir: '.', files: ['pull_request_template.md', 'PULL_REQUEST_TEMPLATE.md'] },
|
||||
{ dir: 'docs', files: ['pull_request_template.md', 'PULL_REQUEST_TEMPLATE.md'] },
|
||||
@@ -207,10 +77,34 @@ export async function pickPullRequestTemplate(repositoryRootUri: Uri, templates:
|
||||
return pickedTemplate?.template;
|
||||
}
|
||||
|
||||
class CommandErrorOutputTextDocumentContentProvider implements TextDocumentContentProvider {
|
||||
|
||||
private items = new Map<string, string>();
|
||||
|
||||
set(uri: Uri, contents: string): void {
|
||||
this.items.set(uri.path, contents);
|
||||
}
|
||||
|
||||
delete(uri: Uri): void {
|
||||
this.items.delete(uri.path);
|
||||
}
|
||||
|
||||
provideTextDocumentContent(uri: Uri): string | undefined {
|
||||
return this.items.get(uri.path);
|
||||
}
|
||||
}
|
||||
|
||||
export class GithubPushErrorHandler implements PushErrorHandler {
|
||||
|
||||
async handlePushError(repository: Repository, remote: Remote, refspec: string, error: Error & { gitErrorCode: GitErrorCodes }): Promise<boolean> {
|
||||
if (error.gitErrorCode !== GitErrorCodes.PermissionDenied) {
|
||||
private disposables: Disposable[] = [];
|
||||
private commandErrors = new CommandErrorOutputTextDocumentContentProvider();
|
||||
|
||||
constructor() {
|
||||
this.disposables.push(workspace.registerTextDocumentContentProvider('github-output', this.commandErrors));
|
||||
}
|
||||
|
||||
async handlePushError(repository: Repository, remote: Remote, refspec: string, error: Error & { stderr: string; gitErrorCode: GitErrorCodes }): Promise<boolean> {
|
||||
if (error.gitErrorCode !== GitErrorCodes.PermissionDenied && error.gitErrorCode !== GitErrorCodes.PushRejected) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -229,8 +123,175 @@ export class GithubPushErrorHandler implements PushErrorHandler {
|
||||
}
|
||||
|
||||
const [, owner, repo] = match;
|
||||
await handlePushError(repository, remote, refspec, owner, repo);
|
||||
|
||||
return true;
|
||||
if (error.gitErrorCode === GitErrorCodes.PermissionDenied) {
|
||||
await this.handlePermissionDeniedError(repository, remote, refspec, owner, repo);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Push protection
|
||||
if (/GH009: Secrets detected!/i.test(error.stderr)) {
|
||||
await this.handlePushProtectionError(owner, repo, error.stderr);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private async handlePermissionDeniedError(repository: Repository, remote: Remote, refspec: string, owner: string, repo: string): Promise<void> {
|
||||
const yes = l10n.t('Create Fork');
|
||||
const no = l10n.t('No');
|
||||
const askFork = l10n.t('You don\'t have permissions to push to "{0}/{1}" on GitHub. Would you like to create a fork and push to it instead?', owner, repo);
|
||||
|
||||
const answer = await window.showWarningMessage(askFork, { modal: true }, yes, no);
|
||||
if (answer !== yes) {
|
||||
return;
|
||||
}
|
||||
|
||||
const match = /^([^:]*):([^:]*)$/.exec(refspec);
|
||||
const localName = match ? match[1] : refspec;
|
||||
let remoteName = match ? match[2] : refspec;
|
||||
|
||||
const [octokit, ghRepository] = await window.withProgress({ location: ProgressLocation.Notification, cancellable: false, title: l10n.t('Create GitHub fork') }, async progress => {
|
||||
progress.report({ message: l10n.t('Forking "{0}/{1}"...', owner, repo), increment: 33 });
|
||||
|
||||
const octokit = await getOctokit();
|
||||
|
||||
type CreateForkResponseData = Awaited<ReturnType<typeof octokit.repos.createFork>>['data'];
|
||||
|
||||
// Issue: what if the repo already exists?
|
||||
let ghRepository: CreateForkResponseData;
|
||||
try {
|
||||
if (isInCodespaces()) {
|
||||
// Call into the codespaces extension to fork the repository
|
||||
const resp = await commands.executeCommand<{ repository: CreateForkResponseData; ref: string }>('github.codespaces.forkRepository');
|
||||
if (!resp) {
|
||||
throw new Error('Unable to fork respository');
|
||||
}
|
||||
|
||||
ghRepository = resp.repository;
|
||||
|
||||
if (resp.ref) {
|
||||
let ref = resp.ref;
|
||||
if (ref.startsWith('refs/heads/')) {
|
||||
ref = ref.substr(11);
|
||||
}
|
||||
|
||||
remoteName = ref;
|
||||
}
|
||||
} else {
|
||||
const resp = await octokit.repos.createFork({ owner, repo });
|
||||
ghRepository = resp.data;
|
||||
}
|
||||
} catch (ex) {
|
||||
console.error(ex);
|
||||
throw ex;
|
||||
}
|
||||
|
||||
progress.report({ message: l10n.t('Pushing changes...'), increment: 33 });
|
||||
|
||||
// Issue: what if there's already an `upstream` repo?
|
||||
await repository.renameRemote(remote.name, 'upstream');
|
||||
|
||||
// Issue: what if there's already another `origin` repo?
|
||||
const protocol = workspace.getConfiguration('github').get<'https' | 'ssh'>('gitProtocol');
|
||||
const remoteUrl = protocol === 'https' ? ghRepository.clone_url : ghRepository.ssh_url;
|
||||
await repository.addRemote('origin', remoteUrl);
|
||||
|
||||
try {
|
||||
await repository.fetch('origin', remoteName);
|
||||
await repository.setBranchUpstream(localName, `origin/${remoteName}`);
|
||||
} catch {
|
||||
// noop
|
||||
}
|
||||
|
||||
await repository.push('origin', localName, true);
|
||||
|
||||
return [octokit, ghRepository] as const;
|
||||
});
|
||||
|
||||
// yield
|
||||
(async () => {
|
||||
const openOnGitHub = l10n.t('Open on GitHub');
|
||||
const createPR = l10n.t('Create PR');
|
||||
const action = await window.showInformationMessage(l10n.t('The fork "{0}" was successfully created on GitHub.', ghRepository.full_name), openOnGitHub, createPR);
|
||||
|
||||
if (action === openOnGitHub) {
|
||||
await commands.executeCommand('vscode.open', Uri.parse(ghRepository.html_url));
|
||||
} else if (action === createPR) {
|
||||
const pr = await window.withProgress({ location: ProgressLocation.Notification, cancellable: false, title: l10n.t('Creating GitHub Pull Request...') }, async _ => {
|
||||
let title = `Update ${remoteName}`;
|
||||
const head = repository.state.HEAD?.name;
|
||||
|
||||
let body: string | undefined;
|
||||
|
||||
if (head) {
|
||||
const commit = await repository.getCommit(head);
|
||||
title = commit.message.split('\n')[0];
|
||||
body = commit.message.slice(title.length + 1).trim();
|
||||
}
|
||||
|
||||
const templates = await findPullRequestTemplates(repository.rootUri);
|
||||
if (templates.length > 0) {
|
||||
templates.sort((a, b) => a.path.localeCompare(b.path));
|
||||
|
||||
const template = await pickPullRequestTemplate(repository.rootUri, templates);
|
||||
|
||||
if (template) {
|
||||
body = new TextDecoder('utf-8').decode(await workspace.fs.readFile(template));
|
||||
}
|
||||
}
|
||||
|
||||
const { data: pr } = await octokit.pulls.create({
|
||||
owner,
|
||||
repo,
|
||||
title,
|
||||
body,
|
||||
head: `${ghRepository.owner.login}:${remoteName}`,
|
||||
base: ghRepository.default_branch
|
||||
});
|
||||
|
||||
await repository.setConfig(`branch.${localName}.remote`, 'upstream');
|
||||
await repository.setConfig(`branch.${localName}.merge`, `refs/heads/${remoteName}`);
|
||||
await repository.setConfig(`branch.${localName}.github-pr-owner-number`, `${owner}#${repo}#${pr.number}`);
|
||||
|
||||
return pr;
|
||||
});
|
||||
|
||||
const openPR = l10n.t('Open PR');
|
||||
const action = await window.showInformationMessage(l10n.t('The PR "{0}/{1}#{2}" was successfully created on GitHub.', owner, repo, pr.number), openPR);
|
||||
|
||||
if (action === openPR) {
|
||||
await commands.executeCommand('vscode.open', Uri.parse(pr.html_url));
|
||||
}
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
private async handlePushProtectionError(owner: string, repo: string, stderr: string): Promise<void> {
|
||||
// Open command output in an editor
|
||||
const timestamp = new Date().getTime();
|
||||
const uri = Uri.parse(`github-output:/github-error-${timestamp}`);
|
||||
this.commandErrors.set(uri, stderr);
|
||||
|
||||
try {
|
||||
const doc = await workspace.openTextDocument(uri);
|
||||
await window.showTextDocument(doc);
|
||||
}
|
||||
finally {
|
||||
this.commandErrors.set(uri, stderr);
|
||||
}
|
||||
|
||||
// Show dialog
|
||||
const learnMore = l10n.t('Learn More');
|
||||
const message = l10n.t('Your push to "{0}/{1}" was rejected by GitHub because push protection is enabled and one or more secrets were detected.', owner, repo);
|
||||
const answer = await window.showWarningMessage(message, { modal: true }, learnMore);
|
||||
if (answer === learnMore) {
|
||||
commands.executeCommand('vscode.open', 'https://aka.ms/vscode-github-push-protection');
|
||||
}
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.disposables.forEach(d => d.dispose());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@
|
||||
"name": "punctuation.definition.arguments.begin.latex"
|
||||
}
|
||||
},
|
||||
"comment": "this works OK with all kinds of crazy stuff as long as section is one line",
|
||||
"comment": "this works OK with all kinds of stuff as long as the section is one line",
|
||||
"contentName": "entity.name.section.latex",
|
||||
"end": "\\}",
|
||||
"endCaptures": {
|
||||
@@ -2371,4 +2371,4 @@
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-9
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "code-oss-dev",
|
||||
"version": "1.80.0",
|
||||
"distro": "a44605dbeeed7ad5c41513260004329c4f502793",
|
||||
"distro": "cbc1aeab0c196a6c6314cf4df21228d9016c70d1",
|
||||
"author": {
|
||||
"name": "Microsoft Corporation"
|
||||
},
|
||||
@@ -92,14 +92,14 @@
|
||||
"vscode-oniguruma": "1.7.0",
|
||||
"vscode-regexpp": "^3.1.0",
|
||||
"vscode-textmate": "9.0.0",
|
||||
"xterm": "5.3.0-beta.1",
|
||||
"xterm-addon-canvas": "0.5.0-beta.1",
|
||||
"xterm": "5.3.0-beta.3",
|
||||
"xterm-addon-canvas": "0.5.0-beta.2",
|
||||
"xterm-addon-image": "0.4.1",
|
||||
"xterm-addon-search": "0.13.0-beta.1",
|
||||
"xterm-addon-serialize": "0.11.0-beta.1",
|
||||
"xterm-addon-search": "0.13.0-beta.2",
|
||||
"xterm-addon-serialize": "0.11.0-beta.2",
|
||||
"xterm-addon-unicode11": "0.5.0",
|
||||
"xterm-addon-webgl": "0.16.0-beta.1",
|
||||
"xterm-headless": "5.3.0-beta.1",
|
||||
"xterm-addon-webgl": "0.16.0-beta.2",
|
||||
"xterm-headless": "5.3.0-beta.3",
|
||||
"yauzl": "^2.9.2",
|
||||
"yazl": "^2.4.3"
|
||||
},
|
||||
@@ -132,7 +132,7 @@
|
||||
"@typescript-eslint/eslint-plugin": "^5.57.0",
|
||||
"@typescript-eslint/experimental-utils": "^5.57.0",
|
||||
"@typescript-eslint/parser": "^5.57.0",
|
||||
"@vscode/gulp-electron": "^1.34.0",
|
||||
"@vscode/gulp-electron": "^1.35.0",
|
||||
"@vscode/l10n-dev": "0.0.21",
|
||||
"@vscode/telemetry-extractor": "^1.9.9",
|
||||
"@vscode/test-web": "^0.0.41",
|
||||
@@ -147,7 +147,7 @@
|
||||
"cssnano": "^4.1.11",
|
||||
"debounce": "^1.0.0",
|
||||
"deemon": "^1.8.0",
|
||||
"electron": "22.3.10",
|
||||
"electron": "22.3.11",
|
||||
"eslint": "8.36.0",
|
||||
"eslint-plugin-header": "3.1.1",
|
||||
"eslint-plugin-jsdoc": "^39.3.2",
|
||||
|
||||
+2
-5
@@ -30,6 +30,7 @@
|
||||
"linuxIconName": "code-oss",
|
||||
"licenseFileName": "LICENSE.txt",
|
||||
"reportIssueUrl": "https://github.com/microsoft/vscode/issues/new",
|
||||
"nodejsRepository": "https://nodejs.org",
|
||||
"urlProtocol": "code-oss",
|
||||
"webviewContentExternalBaseUrlTemplate": "https://{{uuid}}.vscode-cdn.net/insider/ef65ac1ba57f57f2a3961bfe94aa20481caca4c6/out/vs/workbench/contrib/webview/browser/pre/",
|
||||
"builtInExtensions": [
|
||||
@@ -81,9 +82,5 @@
|
||||
"publisherDisplayName": "Microsoft"
|
||||
}
|
||||
}
|
||||
],
|
||||
"nodejs": {
|
||||
"repository": "https://nodejs.org",
|
||||
"version": "16.17.1"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
disturl "http://nodejs.org/dist"
|
||||
target "16.17.1"
|
||||
ms_build_id "218982"
|
||||
runtime "node"
|
||||
build_from_source "true"
|
||||
|
||||
+6
-6
@@ -26,14 +26,14 @@
|
||||
"vscode-oniguruma": "1.7.0",
|
||||
"vscode-regexpp": "^3.1.0",
|
||||
"vscode-textmate": "9.0.0",
|
||||
"xterm": "5.3.0-beta.1",
|
||||
"xterm-addon-canvas": "0.5.0-beta.1",
|
||||
"xterm": "5.3.0-beta.3",
|
||||
"xterm-addon-canvas": "0.5.0-beta.2",
|
||||
"xterm-addon-image": "0.4.1",
|
||||
"xterm-addon-search": "0.13.0-beta.1",
|
||||
"xterm-addon-serialize": "0.11.0-beta.1",
|
||||
"xterm-addon-search": "0.13.0-beta.2",
|
||||
"xterm-addon-serialize": "0.11.0-beta.2",
|
||||
"xterm-addon-unicode11": "0.5.0",
|
||||
"xterm-addon-webgl": "0.16.0-beta.1",
|
||||
"xterm-headless": "5.3.0-beta.1",
|
||||
"xterm-addon-webgl": "0.16.0-beta.2",
|
||||
"xterm-headless": "5.3.0-beta.3",
|
||||
"yauzl": "^2.9.2",
|
||||
"yazl": "^2.4.3"
|
||||
}
|
||||
|
||||
@@ -11,11 +11,11 @@
|
||||
"tas-client-umd": "0.1.8",
|
||||
"vscode-oniguruma": "1.7.0",
|
||||
"vscode-textmate": "9.0.0",
|
||||
"xterm": "5.3.0-beta.1",
|
||||
"xterm-addon-canvas": "0.5.0-beta.1",
|
||||
"xterm": "5.3.0-beta.3",
|
||||
"xterm-addon-canvas": "0.5.0-beta.2",
|
||||
"xterm-addon-image": "0.4.1",
|
||||
"xterm-addon-search": "0.13.0-beta.1",
|
||||
"xterm-addon-search": "0.13.0-beta.2",
|
||||
"xterm-addon-unicode11": "0.5.0",
|
||||
"xterm-addon-webgl": "0.16.0-beta.1"
|
||||
"xterm-addon-webgl": "0.16.0-beta.2"
|
||||
}
|
||||
}
|
||||
|
||||
+16
-16
@@ -68,32 +68,32 @@ vscode-textmate@9.0.0:
|
||||
resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-9.0.0.tgz#313c6c8792b0507aef35aeb81b6b370b37c44d6c"
|
||||
integrity sha512-Cl65diFGxz7gpwbav10HqiY/eVYTO1sjQpmRmV991Bj7wAoOAjGQ97PpQcXorDE2Uc4hnGWLY17xme+5t6MlSg==
|
||||
|
||||
xterm-addon-canvas@0.5.0-beta.1:
|
||||
version "0.5.0-beta.1"
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.5.0-beta.1.tgz#b5ae185741423715460a66029c944b4dededfab0"
|
||||
integrity sha512-A7yjIpyTcOh8ckPJw1YFDvwbTbQ+grM+kTtutOvu5LjLSV9EoCHX17kVoiT2V29ywF7KJMeLYwfBwyFE3uA3QQ==
|
||||
xterm-addon-canvas@0.5.0-beta.2:
|
||||
version "0.5.0-beta.2"
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.5.0-beta.2.tgz#1b83c2a9a306766c47a4f80b8c65cc9ee5f5a5c4"
|
||||
integrity sha512-oTb/2krdbHYGxH2X6yiBZzAB/1WB+apUu4nXHdhBnht20bl8E+YVWqg95D4o0Gl+QJI+XOfB3mqmWaBx1x531A==
|
||||
|
||||
xterm-addon-image@0.4.1:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.4.1.tgz#ec8f750af48005ad641c1128fa1f551ac198472a"
|
||||
integrity sha512-iJpYyvtbHg4oXSv+D6J73ZfCjnboZpbZ567MLplXDBlYSUknv3kvPTfVMPJATV7Zsx7+bDgyXboCh9vsDf/m/w==
|
||||
|
||||
xterm-addon-search@0.13.0-beta.1:
|
||||
version "0.13.0-beta.1"
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.13.0-beta.1.tgz#9fb6ede402d4c369d59d5d6faefe54a05b125bcf"
|
||||
integrity sha512-rdOIhwkfRASqTriUO8QP9UY0p6BosLMv1NXTZqhgq3/5xAXx4VZg6mlQjTRGnUz/GJIN1jU9e/Vp20SpocP/Hw==
|
||||
xterm-addon-search@0.13.0-beta.2:
|
||||
version "0.13.0-beta.2"
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.13.0-beta.2.tgz#c984a35312acad4ce768d17bc49adffa90eece61"
|
||||
integrity sha512-+VoPhIRmfiX2uh2t6xD/RJtBYjVjrkNa3dKQnOYEp4UbYzDjK57rZX652mnZ82TQfk/juxf7v+jV5aRdNLZVbA==
|
||||
|
||||
xterm-addon-unicode11@0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.5.0.tgz#41c0d96acc1e3bb6c6596eee64e163b6bca74be7"
|
||||
integrity sha512-Jm4/g4QiTxiKiTbYICQgC791ubhIZyoIwxAIgOW8z8HWFNY+lwk+dwaKEaEeGBfM48Vk8fklsUW9u/PlenYEBg==
|
||||
|
||||
xterm-addon-webgl@0.16.0-beta.1:
|
||||
version "0.16.0-beta.1"
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.1.tgz#e2b41c6b5f838724a5cb3cfa4231e2d1b8f3f130"
|
||||
integrity sha512-iJK+Uk+23Mh84BNa/44JqAdPESdNKN0ONfw6UztmDk2HTvsy47sU+d/lgF2kOcuI3ew2tRzK9YlupOUhVPwe9g==
|
||||
xterm-addon-webgl@0.16.0-beta.2:
|
||||
version "0.16.0-beta.2"
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.2.tgz#30489ef235405255ee54077002c90553531870d8"
|
||||
integrity sha512-DAt4E/QI1w34ToBhcDj0vaZOAHOO+ffwMt2HGDAB7amPXRcMb0LBIjLpyZhB9sD4tbIgsE0vuqZi1R9vKxZwbg==
|
||||
|
||||
xterm@5.3.0-beta.1:
|
||||
version "5.3.0-beta.1"
|
||||
resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.1.tgz#68e76f2818965592c60bb269360f1fc37219f4ae"
|
||||
integrity sha512-2v/Qmk1A0wO5oouRWUWZ3wxqtfFjxsQbZ1sWxPGJTQvoTSdkORLG44gxrU+Sk2jB5Ojz+3Kg42bnfFghsXVzlw==
|
||||
xterm@5.3.0-beta.3:
|
||||
version "5.3.0-beta.3"
|
||||
resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.3.tgz#1a1aaf9a57afe4dcf86e87d8dc85e80a41d68644"
|
||||
integrity sha512-NGxpV25U2W/KKk6M5V2OXuLgrKY+w05ABi66ZEYuCTi7ux1Qv0z+jm7bkgzk1pGGiTVLG+90OGr2nrhbFr5Y4w==
|
||||
|
||||
+24
-24
@@ -836,45 +836,45 @@ wrappy@1:
|
||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
|
||||
|
||||
xterm-addon-canvas@0.5.0-beta.1:
|
||||
version "0.5.0-beta.1"
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.5.0-beta.1.tgz#b5ae185741423715460a66029c944b4dededfab0"
|
||||
integrity sha512-A7yjIpyTcOh8ckPJw1YFDvwbTbQ+grM+kTtutOvu5LjLSV9EoCHX17kVoiT2V29ywF7KJMeLYwfBwyFE3uA3QQ==
|
||||
xterm-addon-canvas@0.5.0-beta.2:
|
||||
version "0.5.0-beta.2"
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.5.0-beta.2.tgz#1b83c2a9a306766c47a4f80b8c65cc9ee5f5a5c4"
|
||||
integrity sha512-oTb/2krdbHYGxH2X6yiBZzAB/1WB+apUu4nXHdhBnht20bl8E+YVWqg95D4o0Gl+QJI+XOfB3mqmWaBx1x531A==
|
||||
|
||||
xterm-addon-image@0.4.1:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.4.1.tgz#ec8f750af48005ad641c1128fa1f551ac198472a"
|
||||
integrity sha512-iJpYyvtbHg4oXSv+D6J73ZfCjnboZpbZ567MLplXDBlYSUknv3kvPTfVMPJATV7Zsx7+bDgyXboCh9vsDf/m/w==
|
||||
|
||||
xterm-addon-search@0.13.0-beta.1:
|
||||
version "0.13.0-beta.1"
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.13.0-beta.1.tgz#9fb6ede402d4c369d59d5d6faefe54a05b125bcf"
|
||||
integrity sha512-rdOIhwkfRASqTriUO8QP9UY0p6BosLMv1NXTZqhgq3/5xAXx4VZg6mlQjTRGnUz/GJIN1jU9e/Vp20SpocP/Hw==
|
||||
xterm-addon-search@0.13.0-beta.2:
|
||||
version "0.13.0-beta.2"
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.13.0-beta.2.tgz#c984a35312acad4ce768d17bc49adffa90eece61"
|
||||
integrity sha512-+VoPhIRmfiX2uh2t6xD/RJtBYjVjrkNa3dKQnOYEp4UbYzDjK57rZX652mnZ82TQfk/juxf7v+jV5aRdNLZVbA==
|
||||
|
||||
xterm-addon-serialize@0.11.0-beta.1:
|
||||
version "0.11.0-beta.1"
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.11.0-beta.1.tgz#a4bc1ef5d8b8db0180c07f071ce543536d806db1"
|
||||
integrity sha512-2I9Dq49nXUc6ymznwJp8SUsDq5owUdYviUy11HzLh35baDjzbG31CCu5Gs8KSlfUxpNRr3BxaV5/hx7MRPu7Qg==
|
||||
xterm-addon-serialize@0.11.0-beta.2:
|
||||
version "0.11.0-beta.2"
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.11.0-beta.2.tgz#fff924decfbf1bc08434317894f985fef7bb260b"
|
||||
integrity sha512-tN4IT2e+EIpsoFpMONUh1OAuoVAcV7AYOLsqMKgH6GNWB1D/LKGo3cwjpw1vwRZzDJJcCcLxYgxlUzhPbDbLxQ==
|
||||
|
||||
xterm-addon-unicode11@0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.5.0.tgz#41c0d96acc1e3bb6c6596eee64e163b6bca74be7"
|
||||
integrity sha512-Jm4/g4QiTxiKiTbYICQgC791ubhIZyoIwxAIgOW8z8HWFNY+lwk+dwaKEaEeGBfM48Vk8fklsUW9u/PlenYEBg==
|
||||
|
||||
xterm-addon-webgl@0.16.0-beta.1:
|
||||
version "0.16.0-beta.1"
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.1.tgz#e2b41c6b5f838724a5cb3cfa4231e2d1b8f3f130"
|
||||
integrity sha512-iJK+Uk+23Mh84BNa/44JqAdPESdNKN0ONfw6UztmDk2HTvsy47sU+d/lgF2kOcuI3ew2tRzK9YlupOUhVPwe9g==
|
||||
xterm-addon-webgl@0.16.0-beta.2:
|
||||
version "0.16.0-beta.2"
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.2.tgz#30489ef235405255ee54077002c90553531870d8"
|
||||
integrity sha512-DAt4E/QI1w34ToBhcDj0vaZOAHOO+ffwMt2HGDAB7amPXRcMb0LBIjLpyZhB9sD4tbIgsE0vuqZi1R9vKxZwbg==
|
||||
|
||||
xterm-headless@5.3.0-beta.1:
|
||||
version "5.3.0-beta.1"
|
||||
resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.3.0-beta.1.tgz#8c7db703b9f57496c2f052411721c00909b08e8b"
|
||||
integrity sha512-6rsv6l44hLL9Eg2UrfAbCiZcAucdHuPyIsovl2BEmluo4chwd4LD7VINRlPV/x8ML2HgD9SohFyNs5BQAc07Gg==
|
||||
xterm-headless@5.3.0-beta.3:
|
||||
version "5.3.0-beta.3"
|
||||
resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.3.0-beta.3.tgz#153cf330082f4b2aae64ff736ef0b62d93c30da8"
|
||||
integrity sha512-4i/bpFoAn4D4ZA4g8RKrJdhq2EcB1HN2E25yUg3omRbWCOZ2Gp9nAn+62LYzX5rvGqdNbpUTRJLX0lKwEFyLFw==
|
||||
|
||||
xterm@5.3.0-beta.1:
|
||||
version "5.3.0-beta.1"
|
||||
resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.1.tgz#68e76f2818965592c60bb269360f1fc37219f4ae"
|
||||
integrity sha512-2v/Qmk1A0wO5oouRWUWZ3wxqtfFjxsQbZ1sWxPGJTQvoTSdkORLG44gxrU+Sk2jB5Ojz+3Kg42bnfFghsXVzlw==
|
||||
xterm@5.3.0-beta.3:
|
||||
version "5.3.0-beta.3"
|
||||
resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.3.tgz#1a1aaf9a57afe4dcf86e87d8dc85e80a41d68644"
|
||||
integrity sha512-NGxpV25U2W/KKk6M5V2OXuLgrKY+w05ABi66ZEYuCTi7ux1Qv0z+jm7bkgzk1pGGiTVLG+90OGr2nrhbFr5Y4w==
|
||||
|
||||
yallist@^4.0.0:
|
||||
version "4.0.0"
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import 'vs/css!./aria';
|
||||
|
||||
// Use a max length since we are inserting the whole msg in the DOM and that can cause browsers to freeze for long messages #94233
|
||||
@@ -69,16 +68,12 @@ export function status(msg: string): void {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isMacintosh) {
|
||||
alert(msg); // VoiceOver does not seem to support status role
|
||||
if (statusContainer.textContent !== msg) {
|
||||
dom.clearNode(statusContainer2);
|
||||
insertMessage(statusContainer, msg);
|
||||
} else {
|
||||
if (statusContainer.textContent !== msg) {
|
||||
dom.clearNode(statusContainer2);
|
||||
insertMessage(statusContainer, msg);
|
||||
} else {
|
||||
dom.clearNode(statusContainer);
|
||||
insertMessage(statusContainer2, msg);
|
||||
}
|
||||
dom.clearNode(statusContainer);
|
||||
insertMessage(statusContainer2, msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,25 +25,6 @@
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.monaco-button {
|
||||
color: var(--vscode-button-foreground);
|
||||
background-color: var(--vscode-button-background);
|
||||
}
|
||||
|
||||
.monaco-button:hover {
|
||||
background-color: var(--vscode-button-hoverBackground);
|
||||
}
|
||||
|
||||
.monaco-button.secondary {
|
||||
color: var(--vscode-button-secondaryForeground);
|
||||
background-color: var(--vscode-button-secondaryBackground);
|
||||
}
|
||||
|
||||
.monaco-button.secondary:hover {
|
||||
background-color: var(--vscode-button-secondaryHoverBackground);
|
||||
}
|
||||
|
||||
|
||||
.monaco-button.disabled:focus,
|
||||
.monaco-button.disabled {
|
||||
opacity: 0.4 !important;
|
||||
@@ -109,19 +90,11 @@
|
||||
.monaco-button-dropdown .monaco-button-dropdown-separator {
|
||||
padding: 4px 0;
|
||||
cursor: default;
|
||||
background-color: var(--vscode-button-background);
|
||||
border-top: 1px solid var(--vscode-button-border);
|
||||
border-bottom: 1px solid var(--vscode-button-border);
|
||||
}
|
||||
|
||||
.monaco-button.secondary + .monaco-button-dropdown-separator {
|
||||
background-color: var(--vscode-button-secondaryBackground);
|
||||
}
|
||||
|
||||
.monaco-button-dropdown .monaco-button-dropdown-separator > div {
|
||||
height: 100%;
|
||||
width: 1px;
|
||||
background-color: var(--vscode-button-separator);
|
||||
}
|
||||
|
||||
.monaco-button-dropdown > .monaco-button.monaco-dropdown-button {
|
||||
@@ -159,3 +132,41 @@
|
||||
margin: 0 0.2em;
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
/* default color styles - based on CSS variables */
|
||||
|
||||
.monaco-button.default-colors,
|
||||
.monaco-button-dropdown.default-colors > .monaco-button{
|
||||
color: var(--vscode-button-foreground);
|
||||
background-color: var(--vscode-button-background);
|
||||
}
|
||||
|
||||
.monaco-button.default-colors:hover,
|
||||
.monaco-button-dropdown.default-colors > .monaco-button:hover {
|
||||
background-color: var(--vscode-button-hoverBackground);
|
||||
}
|
||||
|
||||
.monaco-button.default-colors.secondary,
|
||||
.monaco-button-dropdown.default-colors > .monaco-button.secondary {
|
||||
color: var(--vscode-button-secondaryForeground);
|
||||
background-color: var(--vscode-button-secondaryBackground);
|
||||
}
|
||||
|
||||
.monaco-button.default-colors.secondary:hover,
|
||||
.monaco-button-dropdown.default-colors > .monaco-button.secondary:hover {
|
||||
background-color: var(--vscode-button-secondaryHoverBackground);
|
||||
}
|
||||
|
||||
.monaco-button-dropdown.default-colors .monaco-button-dropdown-separator {
|
||||
background-color: var(--vscode-button-background);
|
||||
border-top: 1px solid var(--vscode-button-border);
|
||||
border-bottom: 1px solid var(--vscode-button-border);
|
||||
}
|
||||
|
||||
.monaco-button-dropdown.default-colors .monaco-button.secondary + .monaco-button-dropdown-separator {
|
||||
background-color: var(--vscode-button-secondaryBackground);
|
||||
}
|
||||
|
||||
.monaco-button-dropdown.default-colors .monaco-button-dropdown-separator > div {
|
||||
background-color: var(--vscode-button-separator);
|
||||
}
|
||||
|
||||
@@ -373,6 +373,9 @@ export abstract class AbstractScrollableElement extends Widget {
|
||||
}
|
||||
|
||||
private _onMouseWheel(e: StandardWheelEvent): void {
|
||||
if (e.browserEvent?.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
|
||||
const classifier = MouseWheelClassifier.INSTANCE;
|
||||
if (SCROLL_WHEEL_SMOOTH_SCROLL_ENABLED) {
|
||||
|
||||
@@ -755,7 +755,7 @@ export class EventProfiling {
|
||||
}
|
||||
|
||||
start(listenerCount: number): void {
|
||||
this._stopWatch = new StopWatch(true);
|
||||
this._stopWatch = new StopWatch();
|
||||
this.listenerCount = listenerCount;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,22 +3,24 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { globals } from 'vs/base/common/platform';
|
||||
// fake definition so that the valid layers check won't trip on this
|
||||
declare const globalThis: { performance?: { now(): number } };
|
||||
|
||||
const hasPerformanceNow = (globals.performance && typeof globals.performance.now === 'function');
|
||||
const hasPerformanceNow = (globalThis.performance && typeof globalThis.performance.now === 'function');
|
||||
|
||||
export class StopWatch {
|
||||
|
||||
private _highResolution: boolean;
|
||||
private _startTime: number;
|
||||
private _stopTime: number;
|
||||
|
||||
public static create(highResolution: boolean = true): StopWatch {
|
||||
private readonly _now: () => number;
|
||||
|
||||
public static create(highResolution?: boolean): StopWatch {
|
||||
return new StopWatch(highResolution);
|
||||
}
|
||||
|
||||
constructor(highResolution: boolean) {
|
||||
this._highResolution = hasPerformanceNow && highResolution;
|
||||
constructor(highResolution?: boolean) {
|
||||
this._now = hasPerformanceNow && highResolution === false ? Date.now : globalThis.performance!.now.bind(globalThis.performance);
|
||||
this._startTime = this._now();
|
||||
this._stopTime = -1;
|
||||
}
|
||||
@@ -38,8 +40,4 @@ export class StopWatch {
|
||||
}
|
||||
return this._now() - this._startTime;
|
||||
}
|
||||
|
||||
private _now(): number {
|
||||
return this._highResolution ? globals.performance.now() : Date.now();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { constants as FSConstants } from 'fs';
|
||||
import { open, FileHandle } from 'fs/promises';
|
||||
import { createInterface as readLines } from 'readline';
|
||||
import * as Platform from 'vs/base/common/platform';
|
||||
|
||||
type ReleaseInfo = {
|
||||
id: string;
|
||||
id_like?: string;
|
||||
version_id?: string;
|
||||
};
|
||||
|
||||
export async function getOSReleaseInfo(errorLogger: (error: any) => void): Promise<ReleaseInfo | undefined> {
|
||||
if (Platform.isMacintosh || Platform.isWindows) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract release information on linux based systems
|
||||
// using the identifiers specified in
|
||||
// https://www.freedesktop.org/software/systemd/man/os-release.html
|
||||
let handle: FileHandle | undefined;
|
||||
for (const filePath of ['/etc/os-release', '/usr/lib/os-release', '/etc/lsb-release']) {
|
||||
try {
|
||||
handle = await open(filePath, FSConstants.R_OK);
|
||||
break;
|
||||
} catch (err) { }
|
||||
}
|
||||
|
||||
if (!handle) {
|
||||
errorLogger('Unable to retrieve release information from known identifier paths.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const osReleaseKeys = new Set([
|
||||
'ID',
|
||||
'DISTRIB_ID',
|
||||
'ID_LIKE',
|
||||
'VERSION_ID',
|
||||
'DISTRIB_RELEASE',
|
||||
]);
|
||||
const releaseInfo: ReleaseInfo = {
|
||||
id: 'unknown'
|
||||
};
|
||||
|
||||
for await (const line of readLines({ input: handle.createReadStream(), crlfDelay: Infinity })) {
|
||||
if (!line.includes('=')) {
|
||||
continue;
|
||||
}
|
||||
const key = line.split('=')[0].toUpperCase().trim();
|
||||
if (osReleaseKeys.has(key)) {
|
||||
const value = line.split('=')[1].replace(/"/g, '').toLowerCase().trim();
|
||||
if (key === 'ID' || key === 'DISTRIB_ID') {
|
||||
releaseInfo.id = value;
|
||||
} else if (key === 'ID_LIKE') {
|
||||
releaseInfo.id_like = value;
|
||||
} else if (key === 'VERSION_ID' || key === 'DISTRIB_RELEASE') {
|
||||
releaseInfo.version_id = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return releaseInfo;
|
||||
} catch (err) {
|
||||
errorLogger(err);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -114,7 +114,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
process.enableUNCAccessChecks = false;
|
||||
process.restrictUNCAccess = false;
|
||||
}
|
||||
|
||||
function isUNCAccessRestrictionsDisabled() {
|
||||
@@ -122,7 +122,7 @@
|
||||
return true;
|
||||
}
|
||||
|
||||
return process.enableUNCAccessChecks === false;
|
||||
return process.restrictUNCAccess === false;
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -154,18 +154,18 @@ suite('SkipList', function () {
|
||||
|
||||
// init
|
||||
const list = new SkipList<number, boolean>(cmp, max);
|
||||
let sw = new StopWatch(true);
|
||||
let sw = new StopWatch();
|
||||
values.forEach(value => list.set(value, true));
|
||||
sw.stop();
|
||||
console.log(`[LIST] ${list.size} elements after ${sw.elapsed()}ms`);
|
||||
let array: number[] = [];
|
||||
sw = new StopWatch(true);
|
||||
sw = new StopWatch();
|
||||
values.forEach(value => array = insertArraySorted(array, value));
|
||||
sw.stop();
|
||||
console.log(`[ARRAY] ${array.length} elements after ${sw.elapsed()}ms`);
|
||||
|
||||
// get
|
||||
sw = new StopWatch(true);
|
||||
sw = new StopWatch();
|
||||
const someValues = [...values].slice(0, values.size / 4);
|
||||
someValues.forEach(key => {
|
||||
const value = list.get(key); // find
|
||||
@@ -174,7 +174,7 @@ suite('SkipList', function () {
|
||||
});
|
||||
sw.stop();
|
||||
console.log(`[LIST] retrieve ${sw.elapsed()}ms (${(sw.elapsed() / (someValues.length * 2)).toPrecision(4)}ms/op)`);
|
||||
sw = new StopWatch(true);
|
||||
sw = new StopWatch();
|
||||
someValues.forEach(key => {
|
||||
const idx = binarySearch(array, key, cmp); // find
|
||||
console.assert(idx >= 0, '[ARRAY] must have ' + key);
|
||||
@@ -185,13 +185,13 @@ suite('SkipList', function () {
|
||||
|
||||
|
||||
// insert
|
||||
sw = new StopWatch(true);
|
||||
sw = new StopWatch();
|
||||
someValues.forEach(key => {
|
||||
list.set(-key, false);
|
||||
});
|
||||
sw.stop();
|
||||
console.log(`[LIST] insert ${sw.elapsed()}ms (${(sw.elapsed() / someValues.length).toPrecision(4)}ms/op)`);
|
||||
sw = new StopWatch(true);
|
||||
sw = new StopWatch();
|
||||
someValues.forEach(key => {
|
||||
array = insertArraySorted(array, -key);
|
||||
});
|
||||
@@ -199,14 +199,14 @@ suite('SkipList', function () {
|
||||
console.log(`[ARRAY] insert ${sw.elapsed()}ms (${(sw.elapsed() / someValues.length).toPrecision(4)}ms/op)`);
|
||||
|
||||
// delete
|
||||
sw = new StopWatch(true);
|
||||
sw = new StopWatch();
|
||||
someValues.forEach(key => {
|
||||
list.delete(key); // find
|
||||
list.delete(-key); // miss
|
||||
});
|
||||
sw.stop();
|
||||
console.log(`[LIST] delete ${sw.elapsed()}ms (${(sw.elapsed() / (someValues.length * 2)).toPrecision(4)}ms/op)`);
|
||||
sw = new StopWatch(true);
|
||||
sw = new StopWatch();
|
||||
someValues.forEach(key => {
|
||||
array = delArraySorted(array, key); // find
|
||||
array = delArraySorted(array, -key); // miss
|
||||
|
||||
@@ -961,7 +961,7 @@ suite.skip('TST, perf', function () {
|
||||
function perfTest(name: string, callback: Function) {
|
||||
test(name, function () {
|
||||
if (_profile) { console.profile(name); }
|
||||
const sw = new StopWatch(true);
|
||||
const sw = new StopWatch();
|
||||
callback();
|
||||
console.log(name, sw.elapsed());
|
||||
if (_profile) { console.profileEnd(); }
|
||||
|
||||
@@ -12,7 +12,7 @@ import { IPosition, Position } from 'vs/editor/common/core/position';
|
||||
import { IRange, Range } from 'vs/editor/common/core/range';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import * as editorCommon from 'vs/editor/common/editorCommon';
|
||||
import { IIdentifiedSingleEditOperation, IModelDecoration, IModelDeltaDecoration, ITextModel, ICursorStateComputer, PositionAffinity } from 'vs/editor/common/model';
|
||||
import { IIdentifiedSingleEditOperation, IModelDecoration, IModelDeltaDecoration, ITextModel, ICursorStateComputer, PositionAffinity, GlyphMarginLane } from 'vs/editor/common/model';
|
||||
import { IWordAtPosition } from 'vs/editor/common/core/wordHelper';
|
||||
import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent, IModelTokensChangedEvent } from 'vs/editor/common/textModelEvents';
|
||||
import { OverviewRulerZone } from 'vs/editor/common/viewModel/overviewZoneManager';
|
||||
@@ -251,6 +251,43 @@ export interface IOverlayWidget {
|
||||
getPosition(): IOverlayWidgetPosition | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A glyph margin widget renders in the editor glyph margin.
|
||||
*/
|
||||
export interface IGlyphMarginWidget {
|
||||
/**
|
||||
* Get a unique identifier of the glyph widget.
|
||||
*/
|
||||
getId(): string;
|
||||
/**
|
||||
* Get the dom node of the glyph widget.
|
||||
*/
|
||||
getDomNode(): HTMLElement;
|
||||
/**
|
||||
* Get the placement of the glyph widget.
|
||||
*/
|
||||
getPosition(): IGlyphMarginWidgetPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* A position for rendering glyph margin widgets.
|
||||
*/
|
||||
export interface IGlyphMarginWidgetPosition {
|
||||
/**
|
||||
* The glyph margin lane where the widget should be shown.
|
||||
*/
|
||||
lane: GlyphMarginLane;
|
||||
/**
|
||||
* The priority order of the widget, used for determining which widget
|
||||
* to render when there are multiple.
|
||||
*/
|
||||
zIndex: number;
|
||||
/**
|
||||
* The editor range that this widget applies to.
|
||||
*/
|
||||
range: IRange;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type of hit element with the mouse in the editor.
|
||||
*/
|
||||
@@ -993,6 +1030,20 @@ export interface ICodeEditor extends editorCommon.IEditor {
|
||||
*/
|
||||
removeOverlayWidget(widget: IOverlayWidget): void;
|
||||
|
||||
/**
|
||||
* Add a glyph margin widget. Widgets must have unique ids, otherwise they will be overwritten.
|
||||
*/
|
||||
addGlyphMarginWidget(widget: IGlyphMarginWidget): void;
|
||||
/**
|
||||
* Layout/Reposition a glyph margin widget. This is a ping to the editor to call widget.getPosition()
|
||||
* and update appropriately.
|
||||
*/
|
||||
layoutGlyphMarginWidget(widget: IGlyphMarginWidget): void;
|
||||
/**
|
||||
* Remove a glyph margin widget.
|
||||
*/
|
||||
removeGlyphMarginWidget(widget: IGlyphMarginWidget): void;
|
||||
|
||||
/**
|
||||
* Change the view zones. View zones are lost when a new model is attached to the editor.
|
||||
*/
|
||||
|
||||
@@ -786,7 +786,7 @@ class DecorationCSSRules {
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the CSS for decorations styled via `glpyhMarginClassName`.
|
||||
* Build the CSS for decorations styled via `glyphMarginClassName`.
|
||||
*/
|
||||
private getCSSTextForModelDecorationGlyphMarginClassName(opts: IThemeDecorationRenderOptions | undefined): string {
|
||||
if (!opts) {
|
||||
|
||||
@@ -143,7 +143,7 @@ export class EditorWorkerService extends Disposable implements IEditorWorkerServ
|
||||
if (!canSyncModel(this._modelService, resource)) {
|
||||
return Promise.resolve(edits); // File too large
|
||||
}
|
||||
const sw = StopWatch.create(true);
|
||||
const sw = StopWatch.create();
|
||||
const result = this._workerManager.withWorker().then(client => client.computeMoreMinimalEdits(resource, edits, pretty));
|
||||
result.finally(() => this._logService.trace('FORMAT#computeMoreMinimalEdits', resource.toString(true), sw.elapsed()));
|
||||
return Promise.race([result, timeout(1000).then(() => edits)]);
|
||||
@@ -158,7 +158,7 @@ export class EditorWorkerService extends Disposable implements IEditorWorkerServ
|
||||
if (!canSyncModel(this._modelService, resource)) {
|
||||
return Promise.resolve(edits); // File too large
|
||||
}
|
||||
const sw = StopWatch.create(true);
|
||||
const sw = StopWatch.create();
|
||||
const result = this._workerManager.withWorker().then(client => client.computeHumanReadableDiff(resource, edits,
|
||||
{ ignoreTrimWhitespace: false, maxComputationTimeMs: 1000, computeMoves: false, })).catch((err) => {
|
||||
onUnexpectedError(err);
|
||||
|
||||
@@ -5,13 +5,14 @@
|
||||
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IPointerHandlerHelper } from 'vs/editor/browser/controller/mouseHandler';
|
||||
import { PointerHandler } from 'vs/editor/browser/controller/pointerHandler';
|
||||
import { IVisibleRangeProvider, TextAreaHandler } from 'vs/editor/browser/controller/textAreaHandler';
|
||||
import { IContentWidget, IContentWidgetPosition, IOverlayWidget, IOverlayWidgetPosition, IMouseTarget, IViewZoneChangeAccessor, IEditorAriaOptions } from 'vs/editor/browser/editorBrowser';
|
||||
import { IContentWidget, IContentWidgetPosition, IOverlayWidget, IOverlayWidgetPosition, IMouseTarget, IViewZoneChangeAccessor, IEditorAriaOptions, IGlyphMarginWidget, IGlyphMarginWidgetPosition } from 'vs/editor/browser/editorBrowser';
|
||||
import { ICommandDelegate, ViewController } from 'vs/editor/browser/view/viewController';
|
||||
import { ViewUserInputEvents } from 'vs/editor/browser/view/viewUserInputEvents';
|
||||
import { ContentViewOverlays, MarginViewOverlays } from 'vs/editor/browser/view/viewOverlays';
|
||||
@@ -20,7 +21,6 @@ import { ViewContentWidgets } from 'vs/editor/browser/viewParts/contentWidgets/c
|
||||
import { CurrentLineHighlightOverlay, CurrentLineMarginHighlightOverlay } from 'vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight';
|
||||
import { DecorationsOverlay } from 'vs/editor/browser/viewParts/decorations/decorations';
|
||||
import { EditorScrollbar } from 'vs/editor/browser/viewParts/editorScrollbar/editorScrollbar';
|
||||
import { GlyphMarginOverlay } from 'vs/editor/browser/viewParts/glyphMargin/glyphMargin';
|
||||
import { IndentGuidesOverlay } from 'vs/editor/browser/viewParts/indentGuides/indentGuides';
|
||||
import { LineNumbersOverlay } from 'vs/editor/browser/viewParts/lineNumbers/lineNumbers';
|
||||
import { ViewLines } from 'vs/editor/browser/viewParts/lines/viewLines';
|
||||
@@ -52,6 +52,8 @@ import { BlockDecorations } from 'vs/editor/browser/viewParts/blockDecorations/b
|
||||
import { inputLatency } from 'vs/base/browser/performance';
|
||||
import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { WhitespaceOverlay } from 'vs/editor/browser/viewParts/whitespace/whitespace';
|
||||
import { GlyphMarginWidgets } from 'vs/editor/browser/viewParts/glyphMargin/glyphMargin';
|
||||
import { GlyphMarginLane } from 'vs/editor/common/model';
|
||||
|
||||
|
||||
export interface IContentWidgetData {
|
||||
@@ -64,6 +66,11 @@ export interface IOverlayWidgetData {
|
||||
position: IOverlayWidgetPosition | null;
|
||||
}
|
||||
|
||||
export interface IGlyphMarginWidgetData {
|
||||
widget: IGlyphMarginWidget;
|
||||
position: IGlyphMarginWidgetPosition;
|
||||
}
|
||||
|
||||
export class View extends ViewEventHandler {
|
||||
|
||||
private readonly _scrollbar: EditorScrollbar;
|
||||
@@ -77,6 +84,7 @@ export class View extends ViewEventHandler {
|
||||
private readonly _viewZones: ViewZones;
|
||||
private readonly _contentWidgets: ViewContentWidgets;
|
||||
private readonly _overlayWidgets: ViewOverlayWidgets;
|
||||
private readonly _glyphMarginWidgets: GlyphMarginWidgets;
|
||||
private readonly _viewCursors: ViewCursors;
|
||||
private readonly _viewParts: ViewPart[];
|
||||
|
||||
@@ -89,6 +97,7 @@ export class View extends ViewEventHandler {
|
||||
private readonly _overflowGuardContainer: FastDomNode<HTMLElement>;
|
||||
|
||||
// Actual mutable state
|
||||
private _shouldRecomputeGlyphMarginLanes: boolean = false;
|
||||
private _renderAnimationFrame: IDisposable | null;
|
||||
|
||||
constructor(
|
||||
@@ -160,14 +169,18 @@ export class View extends ViewEventHandler {
|
||||
const marginViewOverlays = new MarginViewOverlays(this._context);
|
||||
this._viewParts.push(marginViewOverlays);
|
||||
marginViewOverlays.addDynamicOverlay(new CurrentLineMarginHighlightOverlay(this._context));
|
||||
marginViewOverlays.addDynamicOverlay(new GlyphMarginOverlay(this._context));
|
||||
marginViewOverlays.addDynamicOverlay(new MarginViewLineDecorationsOverlay(this._context));
|
||||
marginViewOverlays.addDynamicOverlay(new LinesDecorationsOverlay(this._context));
|
||||
marginViewOverlays.addDynamicOverlay(new LineNumbersOverlay(this._context));
|
||||
|
||||
// Glyph margin widgets
|
||||
this._glyphMarginWidgets = new GlyphMarginWidgets(this._context);
|
||||
this._viewParts.push(this._glyphMarginWidgets);
|
||||
|
||||
const margin = new Margin(this._context);
|
||||
margin.getDomNode().appendChild(this._viewZones.marginDomNode);
|
||||
margin.getDomNode().appendChild(marginViewOverlays.getDomNode());
|
||||
margin.getDomNode().appendChild(this._glyphMarginWidgets.domNode);
|
||||
this._viewParts.push(margin);
|
||||
|
||||
// Content widgets
|
||||
@@ -226,10 +239,70 @@ export class View extends ViewEventHandler {
|
||||
}
|
||||
|
||||
private _flushAccumulatedAndRenderNow(): void {
|
||||
if (this._shouldRecomputeGlyphMarginLanes) {
|
||||
this._shouldRecomputeGlyphMarginLanes = false;
|
||||
this._context.configuration.setGlyphMarginDecorationLaneCount(this._computeGlyphMarginLaneCount());
|
||||
}
|
||||
inputLatency.onRenderStart();
|
||||
this._renderNow();
|
||||
}
|
||||
|
||||
private _computeGlyphMarginLaneCount(): number {
|
||||
const model = this._context.viewModel.model;
|
||||
type Glyph = { range: Range; lane: GlyphMarginLane };
|
||||
let glyphs: Glyph[] = [];
|
||||
|
||||
// Add all margin decorations
|
||||
glyphs = glyphs.concat(model.getAllMarginDecorations().map((decoration) => {
|
||||
const lane = decoration.options.glyphMargin?.position ?? GlyphMarginLane.Left;
|
||||
return { range: decoration.range, lane };
|
||||
}));
|
||||
|
||||
// Add all glyph margin widgets
|
||||
glyphs = glyphs.concat(this._glyphMarginWidgets.getWidgets().map((widget) => {
|
||||
const range = model.validateRange(widget.preference.range);
|
||||
return { range, lane: widget.preference.lane };
|
||||
}));
|
||||
|
||||
// Sorted by their start position
|
||||
glyphs.sort((a, b) => Range.compareRangesUsingStarts(a.range, b.range));
|
||||
|
||||
let leftDecRange: Range | null = null;
|
||||
let rightDecRange: Range | null = null;
|
||||
for (const decoration of glyphs) {
|
||||
|
||||
if (decoration.lane === GlyphMarginLane.Left && (!leftDecRange || Range.compareRangesUsingEnds(leftDecRange, decoration.range) < 0)) {
|
||||
// assign only if the range of `decoration` ends after, which means it has a higher chance to overlap with the other lane
|
||||
leftDecRange = decoration.range;
|
||||
}
|
||||
|
||||
if (decoration.lane === GlyphMarginLane.Right && (!rightDecRange || Range.compareRangesUsingEnds(rightDecRange, decoration.range) < 0)) {
|
||||
// assign only if the range of `decoration` ends after, which means it has a higher chance to overlap with the other lane
|
||||
rightDecRange = decoration.range;
|
||||
}
|
||||
|
||||
if (leftDecRange && rightDecRange) {
|
||||
|
||||
if (leftDecRange.endLineNumber < rightDecRange.startLineNumber) {
|
||||
// there's no chance for `leftDecRange` to ever intersect something going further
|
||||
leftDecRange = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rightDecRange.endLineNumber < leftDecRange.startLineNumber) {
|
||||
// there's no chance for `rightDecRange` to ever intersect something going further
|
||||
rightDecRange = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
// leftDecRange and rightDecRange are intersecting or touching => we need two lanes
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private _createPointerHandlerHelper(): IPointerHandlerHelper {
|
||||
return {
|
||||
viewDomNode: this.domNode.domNode,
|
||||
@@ -317,6 +390,12 @@ export class View extends ViewEventHandler {
|
||||
this._selections = e.selections;
|
||||
return false;
|
||||
}
|
||||
public override onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean {
|
||||
if (e.affectsGlyphMargin) {
|
||||
this._shouldRecomputeGlyphMarginLanes = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public override onFocusChanged(e: viewEvents.ViewFocusChangedEvent): boolean {
|
||||
this.domNode.setClassName(this._getEditorClassName());
|
||||
return false;
|
||||
@@ -548,6 +627,27 @@ export class View extends ViewEventHandler {
|
||||
this._scheduleRender();
|
||||
}
|
||||
|
||||
public addGlyphMarginWidget(widgetData: IGlyphMarginWidgetData): void {
|
||||
this._glyphMarginWidgets.addWidget(widgetData.widget);
|
||||
this._shouldRecomputeGlyphMarginLanes = true;
|
||||
this._scheduleRender();
|
||||
}
|
||||
|
||||
public layoutGlyphMarginWidget(widgetData: IGlyphMarginWidgetData): void {
|
||||
const newPreference = widgetData.position;
|
||||
const shouldRender = this._glyphMarginWidgets.setWidgetPosition(widgetData.widget, newPreference);
|
||||
if (shouldRender) {
|
||||
this._shouldRecomputeGlyphMarginLanes = true;
|
||||
this._scheduleRender();
|
||||
}
|
||||
}
|
||||
|
||||
public removeGlyphMarginWidget(widgetData: IGlyphMarginWidgetData): void {
|
||||
this._glyphMarginWidgets.removeWidget(widgetData.widget);
|
||||
this._shouldRecomputeGlyphMarginLanes = true;
|
||||
this._scheduleRender();
|
||||
}
|
||||
|
||||
// --- END CodeEditor helpers
|
||||
|
||||
}
|
||||
|
||||
@@ -3,14 +3,22 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode';
|
||||
import { ArrayQueue } from 'vs/base/common/arrays';
|
||||
import 'vs/css!./glyphMargin';
|
||||
import { IGlyphMarginWidget, IGlyphMarginWidgetPosition } from 'vs/editor/browser/editorBrowser';
|
||||
import { DynamicViewOverlay } from 'vs/editor/browser/view/dynamicViewOverlay';
|
||||
import { RenderingContext } from 'vs/editor/browser/view/renderingContext';
|
||||
import { ViewContext } from 'vs/editor/common/viewModel/viewContext';
|
||||
import * as viewEvents from 'vs/editor/common/viewEvents';
|
||||
import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/browser/view/renderingContext';
|
||||
import { ViewPart } from 'vs/editor/browser/view/viewPart';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import * as viewEvents from 'vs/editor/common/viewEvents';
|
||||
import { ViewContext } from 'vs/editor/common/viewModel/viewContext';
|
||||
|
||||
|
||||
/**
|
||||
* Represents a decoration that should be shown along the lines from `startLineNumber` to `endLineNumber`.
|
||||
* This can end up producing multiple `LineDecorationToRender`.
|
||||
*/
|
||||
export class DecorationToRender {
|
||||
_decorationToRenderBrand: void = undefined;
|
||||
|
||||
@@ -18,66 +26,59 @@ export class DecorationToRender {
|
||||
public endLineNumber: number;
|
||||
public className: string;
|
||||
public readonly zIndex: number;
|
||||
public readonly decorationLane: number;
|
||||
|
||||
constructor(startLineNumber: number, endLineNumber: number, className: string, zIndex?: number, decorationLane?: number) {
|
||||
constructor(startLineNumber: number, endLineNumber: number, className: string, zIndex: number | undefined) {
|
||||
this.startLineNumber = +startLineNumber;
|
||||
this.endLineNumber = +endLineNumber;
|
||||
this.className = String(className);
|
||||
this.zIndex = zIndex ?? 0;
|
||||
this.decorationLane = decorationLane ?? 1;
|
||||
}
|
||||
}
|
||||
|
||||
export class RenderedDecoration {
|
||||
/**
|
||||
* A decoration that should be shown along a line.
|
||||
*/
|
||||
export class LineDecorationToRender {
|
||||
constructor(
|
||||
public readonly className: string,
|
||||
public readonly zIndex: number,
|
||||
) { }
|
||||
}
|
||||
|
||||
export class LineRenderedDecorations {
|
||||
/**
|
||||
* Decorations to render on a visible line.
|
||||
*/
|
||||
export class VisibleLineDecorationsToRender {
|
||||
|
||||
private readonly lanes: RenderedDecoration[][] = [];
|
||||
private readonly decorations: LineDecorationToRender[] = [];
|
||||
|
||||
public add(lane: number, decoration: RenderedDecoration) {
|
||||
while (lane >= this.lanes.length) {
|
||||
this.lanes.push([]);
|
||||
}
|
||||
this.lanes[lane].push(decoration);
|
||||
public add(decoration: LineDecorationToRender) {
|
||||
this.decorations.push(decoration);
|
||||
}
|
||||
|
||||
public getLaneDecorations(laneIndex: number): RenderedDecoration[] {
|
||||
if (laneIndex < this.lanes.length) {
|
||||
return this.lanes[laneIndex];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
public isEmpty(): boolean {
|
||||
for (const lane of this.lanes) {
|
||||
if (lane.length > 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
public getDecorations(): LineDecorationToRender[] {
|
||||
return this.decorations;
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class DedupOverlay extends DynamicViewOverlay {
|
||||
|
||||
protected _render(visibleStartLineNumber: number, visibleEndLineNumber: number, decorations: DecorationToRender[], decorationLaneCount: number): LineRenderedDecorations[] {
|
||||
/**
|
||||
* Returns an array with an element for each visible line number.
|
||||
*/
|
||||
protected _render(visibleStartLineNumber: number, visibleEndLineNumber: number, decorations: DecorationToRender[]): VisibleLineDecorationsToRender[] {
|
||||
|
||||
const output: LineRenderedDecorations[] = [];
|
||||
const output: VisibleLineDecorationsToRender[] = [];
|
||||
for (let lineNumber = visibleStartLineNumber; lineNumber <= visibleEndLineNumber; lineNumber++) {
|
||||
const lineIndex = lineNumber - visibleStartLineNumber;
|
||||
output[lineIndex] = new LineRenderedDecorations();
|
||||
output[lineIndex] = new VisibleLineDecorationsToRender();
|
||||
}
|
||||
|
||||
if (decorations.length === 0) {
|
||||
return output;
|
||||
}
|
||||
|
||||
// Sort decorations by className, then by startLineNumber and then by endLineNumber
|
||||
decorations.sort((a, b) => {
|
||||
if (a.className === b.className) {
|
||||
if (a.startLineNumber === b.startLineNumber) {
|
||||
@@ -96,9 +97,9 @@ export abstract class DedupOverlay extends DynamicViewOverlay {
|
||||
const zIndex = d.zIndex;
|
||||
let startLineIndex = Math.max(d.startLineNumber, visibleStartLineNumber) - visibleStartLineNumber;
|
||||
const endLineIndex = Math.min(d.endLineNumber, visibleEndLineNumber) - visibleStartLineNumber;
|
||||
const lane = Math.min(d.decorationLane, decorationLaneCount);
|
||||
|
||||
if (prevClassName === className) {
|
||||
// Here we avoid rendering the same className multiple times on the same line
|
||||
startLineIndex = Math.max(prevEndLineIndex + 1, startLineIndex);
|
||||
prevEndLineIndex = Math.max(prevEndLineIndex, endLineIndex);
|
||||
} else {
|
||||
@@ -107,7 +108,7 @@ export abstract class DedupOverlay extends DynamicViewOverlay {
|
||||
}
|
||||
|
||||
for (let i = startLineIndex; i <= prevEndLineIndex; i++) {
|
||||
output[i].add(lane, new RenderedDecoration(className, zIndex));
|
||||
output[i].add(new LineDecorationToRender(className, zIndex));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,40 +116,54 @@ export abstract class DedupOverlay extends DynamicViewOverlay {
|
||||
}
|
||||
}
|
||||
|
||||
export class GlyphMarginOverlay extends DedupOverlay {
|
||||
export class GlyphMarginWidgets extends ViewPart {
|
||||
|
||||
public domNode: FastDomNode<HTMLElement>;
|
||||
|
||||
private readonly _context: ViewContext;
|
||||
private _lineHeight: number;
|
||||
private _glyphMargin: boolean;
|
||||
private _glyphMarginLeft: number;
|
||||
private _glyphMarginWidth: number;
|
||||
private _glyphMarginDecorationLaneCount: number;
|
||||
private _renderResult: string[] | null;
|
||||
|
||||
private _managedDomNodes: FastDomNode<HTMLElement>[];
|
||||
private _decorationGlyphsToRender: DecorationBasedGlyph[];
|
||||
|
||||
private _widgets: { [key: string]: IWidgetData } = {};
|
||||
|
||||
constructor(context: ViewContext) {
|
||||
super();
|
||||
super(context);
|
||||
this._context = context;
|
||||
|
||||
const options = this._context.configuration.options;
|
||||
const layoutInfo = options.get(EditorOption.layoutInfo);
|
||||
|
||||
this.domNode = createFastDomNode(document.createElement('div'));
|
||||
this.domNode.setClassName('glyphMarginWidgets');
|
||||
this.domNode.setPosition('absolute');
|
||||
this.domNode.setTop(0);
|
||||
|
||||
this._lineHeight = options.get(EditorOption.lineHeight);
|
||||
this._glyphMargin = options.get(EditorOption.glyphMargin);
|
||||
this._glyphMarginLeft = layoutInfo.glyphMarginLeft;
|
||||
this._glyphMarginWidth = layoutInfo.glyphMarginWidth;
|
||||
this._glyphMarginDecorationLaneCount = layoutInfo.glyphMarginDecorationLaneCount;
|
||||
this._renderResult = null;
|
||||
this._context.addEventHandler(this);
|
||||
this._managedDomNodes = [];
|
||||
this._decorationGlyphsToRender = [];
|
||||
}
|
||||
|
||||
public override dispose(): void {
|
||||
this._context.removeEventHandler(this);
|
||||
this._renderResult = null;
|
||||
this._managedDomNodes = [];
|
||||
this._decorationGlyphsToRender = [];
|
||||
this._widgets = {};
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
// --- begin event handlers
|
||||
public getWidgets(): IWidgetData[] {
|
||||
return Object.values(this._widgets);
|
||||
}
|
||||
|
||||
// --- begin event handlers
|
||||
public override onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean {
|
||||
const options = this._context.configuration.options;
|
||||
const layoutInfo = options.get(EditorOption.layoutInfo);
|
||||
@@ -184,85 +199,302 @@ export class GlyphMarginOverlay extends DedupOverlay {
|
||||
|
||||
// --- end event handlers
|
||||
|
||||
protected _getDecorations(ctx: RenderingContext): DecorationToRender[] {
|
||||
const decorations = ctx.getDecorationsInViewport();
|
||||
const r: DecorationToRender[] = [];
|
||||
let rLen = 0;
|
||||
for (let i = 0, len = decorations.length; i < len; i++) {
|
||||
const d = decorations[i];
|
||||
const glyphMarginClassName = d.options.glyphMarginClassName;
|
||||
const zIndex = d.options.zIndex;
|
||||
const lane = d.options.glyphMargin?.position;
|
||||
if (glyphMarginClassName) {
|
||||
r[rLen++] = new DecorationToRender(d.range.startLineNumber, d.range.endLineNumber, glyphMarginClassName, zIndex, lane);
|
||||
}
|
||||
}
|
||||
return r;
|
||||
// --- begin widget management
|
||||
|
||||
public addWidget(widget: IGlyphMarginWidget): void {
|
||||
const domNode = createFastDomNode(widget.getDomNode());
|
||||
|
||||
this._widgets[widget.getId()] = {
|
||||
widget: widget,
|
||||
preference: widget.getPosition(),
|
||||
domNode: domNode,
|
||||
renderInfo: null
|
||||
};
|
||||
|
||||
domNode.setPosition('absolute');
|
||||
domNode.setDisplay('none');
|
||||
domNode.setAttribute('widgetId', widget.getId());
|
||||
this.domNode.appendChild(domNode);
|
||||
|
||||
this.setShouldRender();
|
||||
}
|
||||
|
||||
public setWidgetPosition(widget: IGlyphMarginWidget, preference: IGlyphMarginWidgetPosition): boolean {
|
||||
const myWidget = this._widgets[widget.getId()];
|
||||
if (myWidget.preference.lane === preference.lane
|
||||
&& myWidget.preference.zIndex === preference.zIndex
|
||||
&& Range.equalsRange(myWidget.preference.range, preference.range)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
myWidget.preference = preference;
|
||||
this.setShouldRender();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public removeWidget(widget: IGlyphMarginWidget): void {
|
||||
const widgetId = widget.getId();
|
||||
if (this._widgets[widgetId]) {
|
||||
const widgetData = this._widgets[widgetId];
|
||||
const domNode = widgetData.domNode.domNode;
|
||||
delete this._widgets[widgetId];
|
||||
|
||||
domNode.parentNode?.removeChild(domNode);
|
||||
this.setShouldRender();
|
||||
}
|
||||
}
|
||||
|
||||
// --- end widget management
|
||||
|
||||
private _collectDecorationBasedGlyphRenderRequest(ctx: RenderingContext, requests: GlyphRenderRequest[]): void {
|
||||
const visibleStartLineNumber = ctx.visibleRange.startLineNumber;
|
||||
const visibleEndLineNumber = ctx.visibleRange.endLineNumber;
|
||||
const decorations = ctx.getDecorationsInViewport();
|
||||
|
||||
for (const d of decorations) {
|
||||
const glyphMarginClassName = d.options.glyphMarginClassName;
|
||||
if (!glyphMarginClassName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const startLineNumber = Math.max(d.range.startLineNumber, visibleStartLineNumber);
|
||||
const endLineNumber = Math.min(d.range.endLineNumber, visibleEndLineNumber);
|
||||
const lane = Math.min(d.options.glyphMargin?.position ?? 1, this._glyphMarginDecorationLaneCount);
|
||||
const zIndex = d.options.zIndex ?? 0;
|
||||
|
||||
for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) {
|
||||
requests.push(new DecorationBasedGlyphRenderRequest(lineNumber, lane, zIndex, glyphMarginClassName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _collectWidgetBasedGlyphRenderRequest(ctx: RenderingContext, requests: GlyphRenderRequest[]): void {
|
||||
const visibleStartLineNumber = ctx.visibleRange.startLineNumber;
|
||||
const visibleEndLineNumber = ctx.visibleRange.endLineNumber;
|
||||
|
||||
for (const widget of Object.values(this._widgets)) {
|
||||
const range = widget.preference.range;
|
||||
if (range.endLineNumber < visibleStartLineNumber || range.startLineNumber > visibleEndLineNumber) {
|
||||
// The widget is not in the viewport
|
||||
continue;
|
||||
}
|
||||
|
||||
// The widget is in the viewport, find a good line for it
|
||||
const widgetLineNumber = Math.max(range.startLineNumber, visibleStartLineNumber);
|
||||
const lane = Math.min(widget.preference.lane, this._glyphMarginDecorationLaneCount);
|
||||
requests.push(new WidgetBasedGlyphRenderRequest(widgetLineNumber, lane, widget.preference.zIndex, widget));
|
||||
}
|
||||
}
|
||||
|
||||
private _collectSortedGlyphRenderRequests(ctx: RenderingContext): GlyphRenderRequest[] {
|
||||
|
||||
const requests: GlyphRenderRequest[] = [];
|
||||
|
||||
this._collectDecorationBasedGlyphRenderRequest(ctx, requests);
|
||||
this._collectWidgetBasedGlyphRenderRequest(ctx, requests);
|
||||
|
||||
// sort requests by lineNumber ASC, lane ASC, zIndex DESC, type DESC (widgets first), className ASC
|
||||
// don't change this sort unless you understand `prepareRender` below.
|
||||
requests.sort((a, b) => {
|
||||
if (a.lineNumber === b.lineNumber) {
|
||||
if (a.lane === b.lane) {
|
||||
if (a.zIndex === b.zIndex) {
|
||||
if (b.type === a.type) {
|
||||
if (a.type === GlyphRenderRequestType.Decoration && b.type === GlyphRenderRequestType.Decoration) {
|
||||
return (a.className < b.className ? -1 : 1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return b.type - a.type;
|
||||
}
|
||||
return b.zIndex - a.zIndex;
|
||||
}
|
||||
return a.lane - b.lane;
|
||||
}
|
||||
return a.lineNumber - b.lineNumber;
|
||||
});
|
||||
|
||||
return requests;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will store render information in each widget's renderInfo and in `_decorationGlyphsToRender`.
|
||||
*/
|
||||
public prepareRender(ctx: RenderingContext): void {
|
||||
if (!this._glyphMargin) {
|
||||
this._renderResult = null;
|
||||
this._decorationGlyphsToRender = [];
|
||||
return;
|
||||
}
|
||||
|
||||
const visibleStartLineNumber = ctx.visibleRange.startLineNumber;
|
||||
const visibleEndLineNumber = ctx.visibleRange.endLineNumber;
|
||||
const decorationsToRender = this._getDecorations(ctx);
|
||||
const toRender = this._render(visibleStartLineNumber, visibleEndLineNumber, decorationsToRender, this._glyphMarginDecorationLaneCount);
|
||||
for (const widget of Object.values(this._widgets)) {
|
||||
widget.renderInfo = null;
|
||||
}
|
||||
|
||||
const lineHeight = this._lineHeight.toString();
|
||||
const width = (Math.round(this._glyphMarginWidth / this._glyphMarginDecorationLaneCount)).toString();
|
||||
const common = '" style="width:' + width + 'px' + ';height:' + lineHeight + 'px;';
|
||||
const requests = new ArrayQueue<GlyphRenderRequest>(this._collectSortedGlyphRenderRequests(ctx));
|
||||
const decorationGlyphsToRender: DecorationBasedGlyph[] = [];
|
||||
while (requests.length > 0) {
|
||||
const first = requests.peek();
|
||||
if (!first) {
|
||||
// not possible
|
||||
break;
|
||||
}
|
||||
|
||||
const output: string[] = [];
|
||||
for (let lineNumber = visibleStartLineNumber; lineNumber <= visibleEndLineNumber; lineNumber++) {
|
||||
const lineIndex = lineNumber - visibleStartLineNumber;
|
||||
const renderInfo = toRender[lineIndex];
|
||||
// Requests are sorted by lineNumber and lane, so we read all requests for this particular location
|
||||
const requestsAtLocation = requests.takeWhile((el) => el.lineNumber === first.lineNumber && el.lane === first.lane);
|
||||
if (!requestsAtLocation || requestsAtLocation.length === 0) {
|
||||
// not possible
|
||||
break;
|
||||
}
|
||||
|
||||
if (renderInfo.isEmpty()) {
|
||||
output[lineIndex] = '';
|
||||
} else {
|
||||
let css = '';
|
||||
for (let lane = 1; lane <= this._glyphMarginDecorationLaneCount; lane += 1) {
|
||||
const decorations = renderInfo.getLaneDecorations(lane);
|
||||
if (decorations.length === 0) {
|
||||
continue;
|
||||
const winner = requestsAtLocation[0];
|
||||
if (winner.type === GlyphRenderRequestType.Decoration) {
|
||||
// combine all decorations with the same z-index
|
||||
|
||||
const classNames: string[] = [];
|
||||
// requests are sorted by zIndex, type, and className so we can dedup className by looking at the previous one
|
||||
for (const request of requestsAtLocation) {
|
||||
if (request.zIndex !== winner.zIndex || request.type !== winner.type) {
|
||||
break;
|
||||
}
|
||||
decorations.sort((a, b) => b.zIndex - a.zIndex);
|
||||
// Render winning decorations with the same zIndex together
|
||||
const winningDecoration: RenderedDecoration = decorations[0];
|
||||
const winningDecorationClassNames = [winningDecoration.className];
|
||||
for (let i = 1; i < decorations.length; i += 1) {
|
||||
const decoration = decorations[i];
|
||||
if (decoration.zIndex !== winningDecoration.zIndex) {
|
||||
break;
|
||||
}
|
||||
winningDecorationClassNames.push(decoration.className);
|
||||
if (classNames.length === 0 || classNames[classNames.length - 1] !== request.className) {
|
||||
classNames.push(request.className);
|
||||
}
|
||||
const left = (this._glyphMarginLeft + (lane - 1) * this._lineHeight).toString();
|
||||
css += (
|
||||
'<div class="cgmr codicon '
|
||||
+ winningDecorationClassNames.join(' ') // TODO@joyceerhl Implement overflow for remaining decorations
|
||||
+ common
|
||||
+ 'left:' + left + 'px;"></div>'
|
||||
);
|
||||
}
|
||||
output[lineIndex] = css;
|
||||
|
||||
decorationGlyphsToRender.push(winner.accept(classNames.join(' '))); // TODO@joyceerhl Implement overflow for remaining decorations
|
||||
} else {
|
||||
// widgets cannot be combined
|
||||
winner.widget.renderInfo = {
|
||||
lineNumber: winner.lineNumber,
|
||||
lane: winner.lane,
|
||||
};
|
||||
}
|
||||
}
|
||||
this._decorationGlyphsToRender = decorationGlyphsToRender;
|
||||
}
|
||||
|
||||
public render(ctx: RestrictedRenderingContext): void {
|
||||
if (!this._glyphMargin) {
|
||||
for (const widget of Object.values(this._widgets)) {
|
||||
widget.domNode.setDisplay('none');
|
||||
}
|
||||
while (this._managedDomNodes.length > 0) {
|
||||
const domNode = this._managedDomNodes.pop();
|
||||
domNode?.domNode.remove();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const width = (Math.round(this._glyphMarginWidth / this._glyphMarginDecorationLaneCount));
|
||||
|
||||
// Render widgets
|
||||
for (const widget of Object.values(this._widgets)) {
|
||||
if (!widget.renderInfo) {
|
||||
// this widget is not visible
|
||||
widget.domNode.setDisplay('none');
|
||||
} else {
|
||||
const top = ctx.viewportData.relativeVerticalOffset[widget.renderInfo.lineNumber - ctx.viewportData.startLineNumber];
|
||||
const left = this._glyphMarginLeft + (widget.renderInfo.lane - 1) * this._lineHeight;
|
||||
|
||||
widget.domNode.setDisplay('block');
|
||||
widget.domNode.setTop(top);
|
||||
widget.domNode.setLeft(left);
|
||||
widget.domNode.setWidth(width);
|
||||
widget.domNode.setHeight(this._lineHeight);
|
||||
}
|
||||
}
|
||||
|
||||
this._renderResult = output;
|
||||
}
|
||||
// Render decorations, reusing previous dom nodes as possible
|
||||
for (let i = 0; i < this._decorationGlyphsToRender.length; i++) {
|
||||
const dec = this._decorationGlyphsToRender[i];
|
||||
const top = ctx.viewportData.relativeVerticalOffset[dec.lineNumber - ctx.viewportData.startLineNumber];
|
||||
const left = this._glyphMarginLeft + (dec.lane - 1) * this._lineHeight;
|
||||
|
||||
public render(startLineNumber: number, lineNumber: number): string {
|
||||
if (!this._renderResult) {
|
||||
return '';
|
||||
let domNode: FastDomNode<HTMLElement>;
|
||||
if (i < this._managedDomNodes.length) {
|
||||
domNode = this._managedDomNodes[i];
|
||||
} else {
|
||||
domNode = createFastDomNode(document.createElement('div'));
|
||||
this._managedDomNodes.push(domNode);
|
||||
this.domNode.appendChild(domNode);
|
||||
}
|
||||
|
||||
domNode.setClassName(`cgmr codicon ` + dec.combinedClassName);
|
||||
domNode.setPosition(`absolute`);
|
||||
domNode.setTop(top);
|
||||
domNode.setLeft(left);
|
||||
domNode.setWidth(width);
|
||||
domNode.setHeight(this._lineHeight);
|
||||
}
|
||||
const lineIndex = lineNumber - startLineNumber;
|
||||
if (lineIndex < 0 || lineIndex >= this._renderResult.length) {
|
||||
return '';
|
||||
|
||||
// remove extra dom nodes
|
||||
while (this._managedDomNodes.length > this._decorationGlyphsToRender.length) {
|
||||
const domNode = this._managedDomNodes.pop();
|
||||
domNode?.domNode.remove();
|
||||
}
|
||||
return this._renderResult[lineIndex];
|
||||
}
|
||||
}
|
||||
|
||||
export interface IWidgetData {
|
||||
widget: IGlyphMarginWidget;
|
||||
preference: IGlyphMarginWidgetPosition;
|
||||
domNode: FastDomNode<HTMLElement>;
|
||||
/**
|
||||
* it will contain the location where to render the widget
|
||||
* or null if the widget is not visible
|
||||
*/
|
||||
renderInfo: IRenderInfo | null;
|
||||
}
|
||||
|
||||
export interface IRenderInfo {
|
||||
lineNumber: number;
|
||||
lane: number;
|
||||
}
|
||||
|
||||
const enum GlyphRenderRequestType {
|
||||
Decoration = 0,
|
||||
Widget = 1
|
||||
}
|
||||
|
||||
/**
|
||||
* A request to render a decoration in the glyph margin at a certain location.
|
||||
*/
|
||||
class DecorationBasedGlyphRenderRequest {
|
||||
public readonly type = GlyphRenderRequestType.Decoration;
|
||||
|
||||
constructor(
|
||||
public readonly lineNumber: number,
|
||||
public readonly lane: number,
|
||||
public readonly zIndex: number,
|
||||
public readonly className: string,
|
||||
) { }
|
||||
|
||||
accept(combinedClassName: string): DecorationBasedGlyph {
|
||||
return new DecorationBasedGlyph(this.lineNumber, this.lane, combinedClassName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A request to render a widget in the glyph margin at a certain location.
|
||||
*/
|
||||
class WidgetBasedGlyphRenderRequest {
|
||||
public readonly type = GlyphRenderRequestType.Widget;
|
||||
|
||||
constructor(
|
||||
public readonly lineNumber: number,
|
||||
public readonly lane: number,
|
||||
public readonly zIndex: number,
|
||||
public readonly widget: IWidgetData,
|
||||
) { }
|
||||
}
|
||||
|
||||
type GlyphRenderRequest = DecorationBasedGlyphRenderRequest | WidgetBasedGlyphRenderRequest;
|
||||
|
||||
class DecorationBasedGlyph {
|
||||
constructor(
|
||||
public readonly lineNumber: number,
|
||||
public readonly lane: number,
|
||||
public readonly combinedClassName: string
|
||||
) { }
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ export class LinesDecorationsOverlay extends DedupOverlay {
|
||||
public prepareRender(ctx: RenderingContext): void {
|
||||
const visibleStartLineNumber = ctx.visibleRange.startLineNumber;
|
||||
const visibleEndLineNumber = ctx.visibleRange.endLineNumber;
|
||||
const toRender = this._render(visibleStartLineNumber, visibleEndLineNumber, this._getDecorations(ctx), 1);
|
||||
const toRender = this._render(visibleStartLineNumber, visibleEndLineNumber, this._getDecorations(ctx));
|
||||
|
||||
const left = this._decorationsLeft.toString();
|
||||
const width = this._decorationsWidth.toString();
|
||||
@@ -100,7 +100,7 @@ export class LinesDecorationsOverlay extends DedupOverlay {
|
||||
const output: string[] = [];
|
||||
for (let lineNumber = visibleStartLineNumber; lineNumber <= visibleEndLineNumber; lineNumber++) {
|
||||
const lineIndex = lineNumber - visibleStartLineNumber;
|
||||
const decorations = toRender[lineIndex].getLaneDecorations(1); // there is only one lane, see _render call above
|
||||
const decorations = toRender[lineIndex].getDecorations();
|
||||
let lineOutput = '';
|
||||
for (const decoration of decorations) {
|
||||
lineOutput += '<div class="cldr ' + decoration.className + common;
|
||||
|
||||
@@ -73,12 +73,12 @@ export class MarginViewLineDecorationsOverlay extends DedupOverlay {
|
||||
public prepareRender(ctx: RenderingContext): void {
|
||||
const visibleStartLineNumber = ctx.visibleRange.startLineNumber;
|
||||
const visibleEndLineNumber = ctx.visibleRange.endLineNumber;
|
||||
const toRender = this._render(visibleStartLineNumber, visibleEndLineNumber, this._getDecorations(ctx), 1);
|
||||
const toRender = this._render(visibleStartLineNumber, visibleEndLineNumber, this._getDecorations(ctx));
|
||||
|
||||
const output: string[] = [];
|
||||
for (let lineNumber = visibleStartLineNumber; lineNumber <= visibleEndLineNumber; lineNumber++) {
|
||||
const lineIndex = lineNumber - visibleStartLineNumber;
|
||||
const decorations = toRender[lineIndex].getLaneDecorations(1); // there is only one lane, see _render call above
|
||||
const decorations = toRender[lineIndex].getDecorations();
|
||||
let lineOutput = '';
|
||||
for (const decoration of decorations) {
|
||||
lineOutput += '<div class="cmdr ' + decoration.className + '" style=""></div>';
|
||||
|
||||
@@ -21,7 +21,7 @@ import * as editorBrowser from 'vs/editor/browser/editorBrowser';
|
||||
import { EditorExtensionsRegistry, IEditorContributionDescription } from 'vs/editor/browser/editorExtensions';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { ICommandDelegate } from 'vs/editor/browser/view/viewController';
|
||||
import { IContentWidgetData, IOverlayWidgetData, View } from 'vs/editor/browser/view';
|
||||
import { IContentWidgetData, IGlyphMarginWidgetData, IOverlayWidgetData, View } from 'vs/editor/browser/view';
|
||||
import { ViewUserInputEvents } from 'vs/editor/browser/view/viewUserInputEvents';
|
||||
import { ConfigurationChangedEvent, EditorLayoutInfo, IEditorOptions, EditorOption, IComputedEditorOptions, FindComputedEditorOptionValueById, filterValidationDecorations } from 'vs/editor/common/config/editorOptions';
|
||||
import { CursorColumns } from 'vs/editor/common/core/cursorColumns';
|
||||
@@ -255,6 +255,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
|
||||
|
||||
private _contentWidgets: { [key: string]: IContentWidgetData };
|
||||
private _overlayWidgets: { [key: string]: IOverlayWidgetData };
|
||||
private _glyphMarginWidgets: { [key: string]: IGlyphMarginWidgetData };
|
||||
|
||||
/**
|
||||
* map from "parent" decoration type to live decoration ids.
|
||||
@@ -323,6 +324,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
|
||||
|
||||
this._contentWidgets = {};
|
||||
this._overlayWidgets = {};
|
||||
this._glyphMarginWidgets = {};
|
||||
|
||||
let contributions: IEditorContributionDescription[];
|
||||
if (Array.isArray(codeEditorWidgetOptions.contributions)) {
|
||||
@@ -1510,6 +1512,45 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
|
||||
}
|
||||
}
|
||||
|
||||
public addGlyphMarginWidget(widget: editorBrowser.IGlyphMarginWidget): void {
|
||||
const widgetData: IGlyphMarginWidgetData = {
|
||||
widget: widget,
|
||||
position: widget.getPosition()
|
||||
};
|
||||
|
||||
if (this._glyphMarginWidgets.hasOwnProperty(widget.getId())) {
|
||||
console.warn('Overwriting a glyph margin widget with the same id.');
|
||||
}
|
||||
|
||||
this._glyphMarginWidgets[widget.getId()] = widgetData;
|
||||
|
||||
if (this._modelData && this._modelData.hasRealView) {
|
||||
this._modelData.view.addGlyphMarginWidget(widgetData);
|
||||
}
|
||||
}
|
||||
|
||||
public layoutGlyphMarginWidget(widget: editorBrowser.IGlyphMarginWidget): void {
|
||||
const widgetId = widget.getId();
|
||||
if (this._glyphMarginWidgets.hasOwnProperty(widgetId)) {
|
||||
const widgetData = this._glyphMarginWidgets[widgetId];
|
||||
widgetData.position = widget.getPosition();
|
||||
if (this._modelData && this._modelData.hasRealView) {
|
||||
this._modelData.view.layoutGlyphMarginWidget(widgetData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public removeGlyphMarginWidget(widget: editorBrowser.IGlyphMarginWidget): void {
|
||||
const widgetId = widget.getId();
|
||||
if (this._glyphMarginWidgets.hasOwnProperty(widgetId)) {
|
||||
const widgetData = this._glyphMarginWidgets[widgetId];
|
||||
delete this._glyphMarginWidgets[widgetId];
|
||||
if (this._modelData && this._modelData.hasRealView) {
|
||||
this._modelData.view.removeGlyphMarginWidget(widgetData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public changeViewZones(callback: (accessor: editorBrowser.IViewZoneChangeAccessor) => void): void {
|
||||
if (!this._modelData || !this._modelData.hasRealView) {
|
||||
return;
|
||||
@@ -1718,6 +1759,12 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
|
||||
view.addOverlayWidget(this._overlayWidgets[widgetId]);
|
||||
}
|
||||
|
||||
keys = Object.keys(this._glyphMarginWidgets);
|
||||
for (let i = 0, len = keys.length; i < len; i++) {
|
||||
const widgetId = keys[i];
|
||||
view.addGlyphMarginWidget(this._glyphMarginWidgets[widgetId]);
|
||||
}
|
||||
|
||||
view.render(false, true);
|
||||
view.domNode.domNode.setAttribute('data-uri', model.uri.toString());
|
||||
}
|
||||
|
||||
@@ -55,9 +55,11 @@ export class DiffModel extends Disposable implements IDiffEditorViewModel {
|
||||
if (!diff) {
|
||||
return;
|
||||
}
|
||||
if (!this._showMoves.get()) {
|
||||
const textEdits = TextEditInfo.fromModelContentChanges(e.changes);
|
||||
this._lastDiff = applyModifiedEdits(this._lastDiff!, textEdits, model.original, model.modified);
|
||||
|
||||
const textEdits = TextEditInfo.fromModelContentChanges(e.changes);
|
||||
const result = applyModifiedEdits(this._lastDiff!, textEdits, model.original, model.modified);
|
||||
if (result) {
|
||||
this._lastDiff = result;
|
||||
this._diff.set(DiffState.fromDiffResult(this._lastDiff), undefined);
|
||||
const currentSyncedMovedText = this.syncedMovedTexts.get();
|
||||
this.syncedMovedTexts.set(currentSyncedMovedText ? this._lastDiff.moves.find(m => m.lineRangeMapping.modifiedRange.intersect(currentSyncedMovedText.lineRangeMapping.modifiedRange)) : undefined, undefined);
|
||||
@@ -70,13 +72,16 @@ export class DiffModel extends Disposable implements IDiffEditorViewModel {
|
||||
if (!diff) {
|
||||
return;
|
||||
}
|
||||
if (!this._showMoves.get()) {
|
||||
const textEdits = TextEditInfo.fromModelContentChanges(e.changes);
|
||||
this._lastDiff = applyOriginalEdits(this._lastDiff!, textEdits, model.original, model.modified);
|
||||
|
||||
const textEdits = TextEditInfo.fromModelContentChanges(e.changes);
|
||||
const result = applyModifiedEdits(this._lastDiff!, textEdits, model.original, model.modified);
|
||||
if (result) {
|
||||
this._lastDiff = result;
|
||||
this._diff.set(DiffState.fromDiffResult(this._lastDiff), undefined);
|
||||
const currentSyncedMovedText = this.syncedMovedTexts.get();
|
||||
this.syncedMovedTexts.set(currentSyncedMovedText ? this._lastDiff.moves.find(m => m.lineRangeMapping.modifiedRange.intersect(currentSyncedMovedText.lineRangeMapping.modifiedRange)) : undefined, undefined);
|
||||
}
|
||||
|
||||
debouncer.schedule();
|
||||
}));
|
||||
|
||||
@@ -107,8 +112,8 @@ export class DiffModel extends Disposable implements IDiffEditorViewModel {
|
||||
computeMoves: this._showMoves.read(reader),
|
||||
});
|
||||
|
||||
result = applyOriginalEdits(result, originalTextEditInfos, model.original, model.modified);
|
||||
result = applyModifiedEdits(result, modifiedTextEditInfos, model.original, model.modified);
|
||||
result = applyOriginalEdits(result, originalTextEditInfos, model.original, model.modified) ?? result;
|
||||
result = applyModifiedEdits(result, modifiedTextEditInfos, model.original, model.modified) ?? result;
|
||||
|
||||
const newUnchangedRegions = UnchangedRegion.fromDiffs(result.changes, model.original.getLineCount(), model.modified.getLineCount());
|
||||
|
||||
@@ -327,13 +332,16 @@ export class UnchangedRegion {
|
||||
}
|
||||
}
|
||||
|
||||
function applyOriginalEdits(diff: IDocumentDiff, textEdits: TextEditInfo[], originalTextModel: ITextModel, modifiedTextModel: ITextModel): IDocumentDiff {
|
||||
function applyOriginalEdits(diff: IDocumentDiff, textEdits: TextEditInfo[], originalTextModel: ITextModel, modifiedTextModel: ITextModel): IDocumentDiff | undefined {
|
||||
if (textEdits.length === 0) {
|
||||
return diff;
|
||||
}
|
||||
|
||||
const diff2 = flip(diff);
|
||||
const diff3 = applyModifiedEdits(diff2, textEdits, modifiedTextModel, originalTextModel);
|
||||
if (!diff3) {
|
||||
return undefined;
|
||||
}
|
||||
return flip(diff3);
|
||||
}
|
||||
|
||||
@@ -346,10 +354,14 @@ function flip(diff: IDocumentDiff): IDocumentDiff {
|
||||
};
|
||||
}
|
||||
|
||||
function applyModifiedEdits(diff: IDocumentDiff, textEdits: TextEditInfo[], originalTextModel: ITextModel, modifiedTextModel: ITextModel): IDocumentDiff {
|
||||
function applyModifiedEdits(diff: IDocumentDiff, textEdits: TextEditInfo[], originalTextModel: ITextModel, modifiedTextModel: ITextModel): IDocumentDiff | undefined {
|
||||
if (textEdits.length === 0) {
|
||||
return diff;
|
||||
}
|
||||
if (diff.changes.some(c => !c.innerChanges) || diff.moves.length > 0) {
|
||||
// TODO support these cases
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const changes = applyModifiedEditsToLineRangeMappings(diff.changes, textEdits, originalTextModel, modifiedTextModel);
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ export class WorkerBasedDocumentDiffProvider implements IDocumentDiffProvider, I
|
||||
return c.result;
|
||||
}
|
||||
|
||||
const sw = StopWatch.create(true);
|
||||
const sw = StopWatch.create();
|
||||
const result = await this.editorWorkerService.computeDiff(original.uri, modified.uri, options, this.diffAlgorithm);
|
||||
const timeMs = sw.elapsed();
|
||||
|
||||
|
||||
@@ -685,7 +685,7 @@ export class EditorSimpleWorker implements IRequestHandler, IDisposable {
|
||||
|
||||
public async textualSuggest(modelUrls: string[], leadingWord: string | undefined, wordDef: string, wordDefFlags: string): Promise<{ words: string[]; duration: number } | null> {
|
||||
|
||||
const sw = new StopWatch(true);
|
||||
const sw = new StopWatch();
|
||||
const wordDefRegExp = new RegExp(wordDef, wordDefFlags);
|
||||
const seen = new Set<string>();
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import { Range } from 'vs/editor/common/core/range';
|
||||
import { ISelection, Selection } from 'vs/editor/common/core/selection';
|
||||
import { ICommand, ICursorState, IViewState, ScrollType } from 'vs/editor/common/editorCommon';
|
||||
import { IEditorConfiguration } from 'vs/editor/common/config/editorConfiguration';
|
||||
import { EndOfLinePreference, GlyphMarginLane, IAttachedView, ICursorStateComputer, IIdentifiedSingleEditOperation, ITextModel, PositionAffinity, TrackedRangeStickiness } from 'vs/editor/common/model';
|
||||
import { EndOfLinePreference, IAttachedView, ICursorStateComputer, IIdentifiedSingleEditOperation, ITextModel, PositionAffinity, TrackedRangeStickiness } from 'vs/editor/common/model';
|
||||
import { IActiveIndentGuideInfo, BracketGuideOptions, IndentGuide } from 'vs/editor/common/textModelGuides';
|
||||
import { ModelDecorationMinimapOptions, ModelDecorationOptions, ModelDecorationOverviewRulerOptions } from 'vs/editor/common/model/textModel';
|
||||
import * as textModelEvents from 'vs/editor/common/textModelEvents';
|
||||
@@ -459,54 +459,6 @@ export class ViewModel extends Disposable implements IViewModel {
|
||||
|
||||
this._register(this.model.onDidChangeDecorations((e) => {
|
||||
this._decorations.onModelDecorationsChanged();
|
||||
|
||||
// Determine whether we need to resize the glyph margin
|
||||
if (e.affectsGlyphMargin) {
|
||||
const decorations = this.model.getAllMarginDecorations();
|
||||
|
||||
let hasTwoLanes = false;
|
||||
|
||||
// Decorations are already sorted by their start position, but protect against future changes
|
||||
decorations.sort((a, b) => Range.compareRangesUsingStarts(a.range, b.range));
|
||||
|
||||
let leftDecRange: Range | null = null;
|
||||
let rightDecRange: Range | null = null;
|
||||
for (const decoration of decorations) {
|
||||
const position = decoration.options.glyphMargin?.position ?? GlyphMarginLane.Left;
|
||||
|
||||
if (position === GlyphMarginLane.Left && (!leftDecRange || Range.compareRangesUsingEnds(leftDecRange, decoration.range) < 0)) {
|
||||
// assign only if the range of `decoration` ends after, which means it has a higher chance to overlap with the other lane
|
||||
leftDecRange = decoration.range;
|
||||
}
|
||||
|
||||
if (position === GlyphMarginLane.Right && (!rightDecRange || Range.compareRangesUsingEnds(rightDecRange, decoration.range) < 0)) {
|
||||
// assign only if the range of `decoration` ends after, which means it has a higher chance to overlap with the other lane
|
||||
rightDecRange = decoration.range;
|
||||
}
|
||||
|
||||
if (leftDecRange && rightDecRange) {
|
||||
|
||||
if (leftDecRange.endLineNumber < rightDecRange.startLineNumber) {
|
||||
// there's no chance for `leftDecRange` to ever intersect something going further
|
||||
leftDecRange = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rightDecRange.endLineNumber < leftDecRange.startLineNumber) {
|
||||
// there's no chance for `rightDecRange` to ever intersect something going further
|
||||
rightDecRange = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
// leftDecRange and rightDecRange are intersecting or touching => we need two lanes
|
||||
hasTwoLanes = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this._configuration.setGlyphMarginDecorationLaneCount(hasTwoLanes ? 2 : 1);
|
||||
}
|
||||
|
||||
this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewDecorationsChangedEvent(e));
|
||||
this._eventDispatcher.emitOutgoingEvent(new ModelDecorationsChangedEvent(e));
|
||||
}));
|
||||
|
||||
@@ -311,7 +311,7 @@ export class FoldingController extends Disposable implements IEditorContribution
|
||||
if (!foldingModel) { // null if editor has been disposed, or folding turned off
|
||||
return null;
|
||||
}
|
||||
const sw = new StopWatch(true);
|
||||
const sw = new StopWatch();
|
||||
const provider = this.getRangeProvider(foldingModel.textModel);
|
||||
const foldingRegionPromise = this.foldingRegionPromise = createCancelablePromise(token => provider.compute(token));
|
||||
return foldingRegionPromise.then(foldingRanges => {
|
||||
@@ -1269,4 +1269,3 @@ CommandsRegistry.registerCommand('_executeFoldingRangeProvider', async function
|
||||
rangeProvider.dispose();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -70,10 +70,13 @@ export class SingleTextEdit {
|
||||
const suggestionAddedIndentationLength = getLeadingWhitespace(edit.text).length;
|
||||
|
||||
const replacedIndentation = sourceLine.substring(edit.range.startColumn - 1, sourceIndentationLength);
|
||||
const rangeThatDoesNotReplaceIndentation = Range.fromPositions(
|
||||
edit.range.getStartPosition().delta(0, replacedIndentation.length),
|
||||
edit.range.getEndPosition()
|
||||
);
|
||||
|
||||
const [startPosition, endPosition] = [edit.range.getStartPosition(), edit.range.getEndPosition()];
|
||||
const newStartPosition =
|
||||
startPosition.column + replacedIndentation.length <= endPosition.column
|
||||
? startPosition.delta(0, replacedIndentation.length)
|
||||
: endPosition;
|
||||
const rangeThatDoesNotReplaceIndentation = Range.fromPositions(newStartPosition, endPosition);
|
||||
|
||||
const suggestionWithoutIndentationChange =
|
||||
edit.text.startsWith(replacedIndentation)
|
||||
|
||||
@@ -16,7 +16,7 @@ import { overviewRulerRangeHighlight } from 'vs/editor/common/core/editorColorRe
|
||||
import { IQuickAccessProvider } from 'vs/platform/quickinput/common/quickAccess';
|
||||
import { IKeyMods, IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { themeColorFromId } from 'vs/platform/theme/common/themeService';
|
||||
import { alert } from 'vs/base/browser/ui/aria/aria';
|
||||
import { status } from 'vs/base/browser/ui/aria/aria';
|
||||
|
||||
interface IEditorLineDecoration {
|
||||
readonly rangeHighlightId: string;
|
||||
@@ -149,7 +149,7 @@ export abstract class AbstractEditorNavigationQuickAccessProvider implements IQu
|
||||
}
|
||||
const model = editor.getModel();
|
||||
if (model && 'getLineContent' in model) {
|
||||
alert(`${model.getLineContent(options.range.startLineNumber)}`);
|
||||
status(`${model.getLineContent(options.range.startLineNumber)}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -211,7 +211,7 @@ suite('SmartSelect', () => {
|
||||
|
||||
async function assertRanges(provider: SelectionRangeProvider, value: string, ...expected: IRange[]): Promise<void> {
|
||||
const index = value.indexOf('|');
|
||||
value = value.replace('|', '');
|
||||
value = value.replace('|', ''); // CodeQL [SM02383] js/incomplete-sanitization this is purpose only the first | character
|
||||
|
||||
const model = modelService.createModel(value, new StaticLanguageSelector(languageId), URI.parse('fake:lang'));
|
||||
const pos = model.getPositionAt(index);
|
||||
|
||||
@@ -73,7 +73,7 @@ export class CompletionItem {
|
||||
readonly extensionId?: ExtensionIdentifier;
|
||||
|
||||
// resolving
|
||||
private _isResolved?: boolean;
|
||||
private _resolveDuration?: number;
|
||||
private _resolveCache?: Promise<void>;
|
||||
|
||||
constructor(
|
||||
@@ -122,32 +122,37 @@ export class CompletionItem {
|
||||
// create the suggestion resolver
|
||||
if (typeof provider.resolveCompletionItem !== 'function') {
|
||||
this._resolveCache = Promise.resolve();
|
||||
this._isResolved = true;
|
||||
this._resolveDuration = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ---- resolving
|
||||
|
||||
get isResolved(): boolean {
|
||||
return !!this._isResolved;
|
||||
return this._resolveDuration !== undefined;
|
||||
}
|
||||
|
||||
get resolveDuration(): number {
|
||||
return this._resolveDuration !== undefined ? this._resolveDuration : -1;
|
||||
}
|
||||
|
||||
async resolve(token: CancellationToken) {
|
||||
if (!this._resolveCache) {
|
||||
const sub = token.onCancellationRequested(() => {
|
||||
this._resolveCache = undefined;
|
||||
this._isResolved = false;
|
||||
this._resolveDuration = undefined;
|
||||
});
|
||||
const sw = new StopWatch(true);
|
||||
this._resolveCache = Promise.resolve(this.provider.resolveCompletionItem!(this.completion, token)).then(value => {
|
||||
Object.assign(this.completion, value);
|
||||
this._isResolved = true;
|
||||
this._resolveDuration = sw.elapsed();
|
||||
sub.dispose();
|
||||
}, err => {
|
||||
if (isCancellationError(err)) {
|
||||
// the IPC queue will reject the request with the
|
||||
// cancellation error -> reset cached
|
||||
this._resolveCache = undefined;
|
||||
this._isResolved = false;
|
||||
this._resolveDuration = undefined;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -213,7 +218,7 @@ export async function provideSuggestionItems(
|
||||
token: CancellationToken = CancellationToken.None
|
||||
): Promise<CompletionItemModel> {
|
||||
|
||||
const sw = new StopWatch(true);
|
||||
const sw = new StopWatch();
|
||||
position = position.clone();
|
||||
|
||||
const word = model.getWordAtPosition(position);
|
||||
@@ -275,7 +280,7 @@ export async function provideSuggestionItems(
|
||||
if (options.providerFilter.size > 0 && !options.providerFilter.has(_snippetSuggestSupport)) {
|
||||
return;
|
||||
}
|
||||
const sw = new StopWatch(true);
|
||||
const sw = new StopWatch();
|
||||
const list = await _snippetSuggestSupport.provideCompletionItems(model, position, context, token);
|
||||
onCompletionList(_snippetSuggestSupport, list, sw);
|
||||
})();
|
||||
@@ -300,7 +305,7 @@ export async function provideSuggestionItems(
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const sw = new StopWatch(true);
|
||||
const sw = new StopWatch();
|
||||
const list = await provider.provideCompletionItems(model, position, context, token);
|
||||
didAddResult = onCompletionList(provider, list, sw) || didAddResult;
|
||||
} catch (err) {
|
||||
|
||||
@@ -349,7 +349,7 @@ export class SuggestController implements IEditorContribution {
|
||||
|
||||
} else if (!isResolved) {
|
||||
// async additional edits
|
||||
const sw = new StopWatch(true);
|
||||
const sw = new StopWatch();
|
||||
let position: IPosition | undefined;
|
||||
|
||||
const docListener = model.onDidChangeContent(e => {
|
||||
@@ -482,20 +482,24 @@ export class SuggestController implements IEditorContribution {
|
||||
|
||||
// clear only now - after all tasks are done
|
||||
Promise.all(tasks).finally(() => {
|
||||
this._reportSuggestionAcceptedTelemetry(item, model, event, isResolved);
|
||||
this._reportSuggestionAcceptedTelemetry(item, model, isResolved);
|
||||
|
||||
this.model.clear();
|
||||
cts.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
private _telemetryGate: number = 0;
|
||||
private _reportSuggestionAcceptedTelemetry(item: CompletionItem, model: ITextModel, acceptedSuggestion: ISelectedSuggestion, itemResolved: boolean) {
|
||||
if (this._telemetryGate++ % 100 !== 0) {
|
||||
private _reportSuggestionAcceptedTelemetry(item: CompletionItem, model: ITextModel, itemResolved: boolean) {
|
||||
|
||||
if (Math.floor(Math.random() * 100) === 0) {
|
||||
// throttle telemetry event because accepting completions happens a lot
|
||||
return;
|
||||
}
|
||||
|
||||
type AcceptedSuggestion = { providerId: string; fileExtension: string; languageId: string; basenameHash: string; kind: number; itemResolved: boolean };
|
||||
type AcceptedSuggestion = {
|
||||
providerId: string; fileExtension: string; languageId: string; basenameHash: string; kind: number;
|
||||
resolveInfo: number; resolveDuration: number;
|
||||
};
|
||||
type AcceptedSuggestionClassification = {
|
||||
owner: 'jrieken';
|
||||
comment: 'Information accepting completion items';
|
||||
@@ -504,18 +508,18 @@ export class SuggestController implements IEditorContribution {
|
||||
fileExtension: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'File extension of the file into which the completion was inserted' };
|
||||
languageId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Language type of the file into which the completion was inserted' };
|
||||
kind: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'The completion item kind' };
|
||||
itemResolved: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'If the item was inserted before resolving was done' };
|
||||
resolveInfo: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'If the item was inserted before resolving was done' };
|
||||
resolveDuration: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'How long resolving took to finish' };
|
||||
};
|
||||
// _debugDisplayName looks like `vscode.css-language-features(/-:)`, where the last bit is the trigger chars
|
||||
// normalize it to just the extension ID and lowercase
|
||||
const providerId = item.extensionId ? item.extensionId.value : (acceptedSuggestion.item.provider._debugDisplayName ?? 'unknown').split('(', 1)[0].toLowerCase();
|
||||
|
||||
this._telemetryService.publicLog2<AcceptedSuggestion, AcceptedSuggestionClassification>('suggest.acceptedSuggestion', {
|
||||
providerId,
|
||||
providerId: item.extensionId?.value ?? 'unknown',
|
||||
kind: item.completion.kind,
|
||||
basenameHash: hash(basename(model.uri)).toString(16),
|
||||
languageId: model.getLanguageId(),
|
||||
fileExtension: extname(model.uri),
|
||||
itemResolved
|
||||
resolveInfo: !item.provider.resolveCompletionItem ? -1 : itemResolved ? 1 : 0,
|
||||
resolveDuration: item.resolveDuration
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ class ForceRetokenizeAction extends EditorAction {
|
||||
}
|
||||
const model = editor.getModel();
|
||||
model.tokenization.resetTokenization();
|
||||
const sw = new StopWatch(true);
|
||||
const sw = new StopWatch();
|
||||
model.tokenization.forceTokenization(model.getLineCount());
|
||||
sw.stop();
|
||||
console.log(`tokenization took ${sw.elapsed()}`);
|
||||
|
||||
@@ -9,7 +9,7 @@ import { CancelablePromise, createCancelablePromise, first, timeout } from 'vs/b
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { onUnexpectedError, onUnexpectedExternalError } from 'vs/base/common/errors';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IActiveCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { EditorAction, EditorContributionInstantiation, IActionOptions, registerEditorAction, registerEditorContribution, registerModelAndPositionCommand } from 'vs/editor/browser/editorExtensions';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
@@ -29,6 +29,7 @@ import { IWordAtPosition } from 'vs/editor/common/core/wordHelper';
|
||||
import { LanguageFeatureRegistry } from 'vs/editor/common/languageFeatureRegistry';
|
||||
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
|
||||
import { getHighlightDecorationOptions } from 'vs/editor/contrib/wordHighlighter/browser/highlightDecorations';
|
||||
import { Iterable } from 'vs/base/common/iterator';
|
||||
|
||||
const ctxHasWordHighlights = new RawContextKey<boolean>('hasWordHighlights', false);
|
||||
|
||||
@@ -192,9 +193,12 @@ class WordHighlighter {
|
||||
private readonly _hasWordHighlights: IContextKey<boolean>;
|
||||
private _ignorePositionChangeEvent: boolean;
|
||||
|
||||
constructor(editor: IActiveCodeEditor, providers: LanguageFeatureRegistry<DocumentHighlightProvider>, contextKeyService: IContextKeyService) {
|
||||
private readonly linkedHighlighters: () => Iterable<WordHighlighter | null>;
|
||||
|
||||
constructor(editor: IActiveCodeEditor, providers: LanguageFeatureRegistry<DocumentHighlightProvider>, linkedHighlighters: () => Iterable<WordHighlighter | null>, contextKeyService: IContextKeyService) {
|
||||
this.editor = editor;
|
||||
this.providers = providers;
|
||||
this.linkedHighlighters = linkedHighlighters;
|
||||
this._hasWordHighlights = ctxHasWordHighlights.bindTo(contextKeyService);
|
||||
this._ignorePositionChangeEvent = false;
|
||||
this.occurrencesHighlight = this.editor.getOption(EditorOption.occurrencesHighlight);
|
||||
@@ -453,6 +457,15 @@ class WordHighlighter {
|
||||
|
||||
this.decorations.set(decorations);
|
||||
this._hasWordHighlights.set(this.hasDecorations());
|
||||
|
||||
// update decorators of friends
|
||||
for (const other of this.linkedHighlighters()) {
|
||||
if (other?.editor.getModel() === this.editor.getModel()) {
|
||||
other._stopAll();
|
||||
other.decorations.set(decorations);
|
||||
other._hasWordHighlights.set(other.hasDecorations());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
@@ -470,13 +483,15 @@ export class WordHighlighterContribution extends Disposable implements IEditorCo
|
||||
}
|
||||
|
||||
private wordHighlighter: WordHighlighter | null;
|
||||
private linkedContributions: Set<WordHighlighterContribution>;
|
||||
|
||||
constructor(editor: ICodeEditor, @IContextKeyService contextKeyService: IContextKeyService, @ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService) {
|
||||
super();
|
||||
this.wordHighlighter = null;
|
||||
this.linkedContributions = new Set();
|
||||
const createWordHighlighterIfPossible = () => {
|
||||
if (editor.hasModel()) {
|
||||
this.wordHighlighter = new WordHighlighter(editor, languageFeaturesService.documentHighlightProvider, contextKeyService);
|
||||
this.wordHighlighter = new WordHighlighter(editor, languageFeaturesService.documentHighlightProvider, () => Iterable.map(this.linkedContributions, c => c.wordHighlighter), contextKeyService);
|
||||
}
|
||||
};
|
||||
this._register(editor.onDidChangeModel((e) => {
|
||||
@@ -514,6 +529,19 @@ export class WordHighlighterContribution extends Disposable implements IEditorCo
|
||||
this.wordHighlighter?.stop();
|
||||
}
|
||||
|
||||
public linkWordHighlighters(editor: ICodeEditor): IDisposable {
|
||||
const other = WordHighlighterContribution.get(editor);
|
||||
if (!other) {
|
||||
return Disposable.None;
|
||||
}
|
||||
this.linkedContributions.add(other);
|
||||
other.linkedContributions.add(this);
|
||||
return toDisposable(() => {
|
||||
this.linkedContributions.delete(other);
|
||||
other.linkedContributions.delete(this);
|
||||
});
|
||||
}
|
||||
|
||||
public override dispose(): void {
|
||||
if (this.wordHighlighter) {
|
||||
this.wordHighlighter.dispose();
|
||||
|
||||
@@ -87,7 +87,7 @@ import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { IStorageService, InMemoryStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { DefaultConfiguration } from 'vs/platform/configuration/common/configurations';
|
||||
import { WorkspaceEdit } from 'vs/editor/common/languages';
|
||||
import { AudioCue, IAudioCueService, Sound } from 'vs/platform/audioCues/browser/audioCueService';
|
||||
import { AudioCue, AudioCueGroupId, IAudioCueService, Sound } from 'vs/platform/audioCues/browser/audioCueService';
|
||||
import { LogService } from 'vs/platform/log/common/logService';
|
||||
import { getEditorFeatures } from 'vs/editor/common/editorFeatures';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
@@ -1055,6 +1055,11 @@ class StandaloneAudioService implements IAudioCueService {
|
||||
|
||||
async playSound(cue: Sound, allowManyInParallel?: boolean | undefined): Promise<void> {
|
||||
}
|
||||
playAudioCueLoop(cue: AudioCue): IDisposable {
|
||||
return toDisposable(() => { });
|
||||
}
|
||||
playRandomAudioCue(groupId: AudioCueGroupId, allowManyInParallel?: boolean): void {
|
||||
}
|
||||
}
|
||||
|
||||
export interface IEditorOverrideServices {
|
||||
|
||||
@@ -17,7 +17,7 @@ const MAX_INSERTS = 30;
|
||||
const MIN_CHANGE_CNT = 10;
|
||||
const MAX_CHANGE_CNT = 20;
|
||||
|
||||
suite('IntervalTree', () => {
|
||||
suite('IntervalTree 1', () => {
|
||||
|
||||
class Interval {
|
||||
_intervalBrand: void = undefined;
|
||||
@@ -554,7 +554,7 @@ suite('IntervalTree', () => {
|
||||
});
|
||||
});
|
||||
|
||||
suite('IntervalTree', () => {
|
||||
suite('IntervalTree 2', () => {
|
||||
function assertNodeAcceptEdit(msg: string, nodeStart: number, nodeEnd: number, nodeStickiness: TrackedRangeStickiness, start: number, end: number, textLength: number, forceMoveMarkers: boolean, expectedNodeStart: number, expectedNodeEnd: number): void {
|
||||
const node = new IntervalNode('', nodeStart, nodeEnd);
|
||||
setNodeStickiness(node, nodeStickiness);
|
||||
|
||||
Vendored
+50
@@ -5389,6 +5389,43 @@ declare namespace monaco.editor {
|
||||
getPosition(): IOverlayWidgetPosition | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A glyph margin widget renders in the editor glyph margin.
|
||||
*/
|
||||
export interface IGlyphMarginWidget {
|
||||
/**
|
||||
* Get a unique identifier of the glyph widget.
|
||||
*/
|
||||
getId(): string;
|
||||
/**
|
||||
* Get the dom node of the glyph widget.
|
||||
*/
|
||||
getDomNode(): HTMLElement;
|
||||
/**
|
||||
* Get the placement of the glyph widget.
|
||||
*/
|
||||
getPosition(): IGlyphMarginWidgetPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* A position for rendering glyph margin widgets.
|
||||
*/
|
||||
export interface IGlyphMarginWidgetPosition {
|
||||
/**
|
||||
* The glyph margin lane where the widget should be shown.
|
||||
*/
|
||||
lane: GlyphMarginLane;
|
||||
/**
|
||||
* The priority order of the widget, used for determining which widget
|
||||
* to render when there are multiple.
|
||||
*/
|
||||
zIndex: number;
|
||||
/**
|
||||
* The editor range that this widget applies to.
|
||||
*/
|
||||
range: IRange;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type of hit element with the mouse in the editor.
|
||||
*/
|
||||
@@ -5961,6 +5998,19 @@ declare namespace monaco.editor {
|
||||
* Remove an overlay widget.
|
||||
*/
|
||||
removeOverlayWidget(widget: IOverlayWidget): void;
|
||||
/**
|
||||
* Add a glyph margin widget. Widgets must have unique ids, otherwise they will be overwritten.
|
||||
*/
|
||||
addGlyphMarginWidget(widget: IGlyphMarginWidget): void;
|
||||
/**
|
||||
* Layout/Reposition a glyph margin widget. This is a ping to the editor to call widget.getPosition()
|
||||
* and update appropriately.
|
||||
*/
|
||||
layoutGlyphMarginWidget(widget: IGlyphMarginWidget): void;
|
||||
/**
|
||||
* Remove a glyph margin widget.
|
||||
*/
|
||||
removeGlyphMarginWidget(widget: IGlyphMarginWidget): void;
|
||||
/**
|
||||
* Change the view zones. View zones are lost when a new model is attached to the editor.
|
||||
*/
|
||||
|
||||
@@ -88,6 +88,7 @@ export class MenuWorkbenchButtonBar extends ButtonBar {
|
||||
}
|
||||
|
||||
btn.enabled = action.enabled;
|
||||
btn.element.classList.add('default-colors');
|
||||
if (conifgProvider(action)?.showLabel ?? true) {
|
||||
btn.label = action.label;
|
||||
} else {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { FileAccess } from 'vs/base/common/network';
|
||||
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
@@ -22,6 +22,8 @@ export interface IAudioCueService {
|
||||
onEnabledChanged(cue: AudioCue): Event<void>;
|
||||
|
||||
playSound(cue: Sound, allowManyInParallel?: boolean): Promise<void>;
|
||||
playAudioCueLoop(cue: AudioCue, milliseconds: number): IDisposable;
|
||||
playRandomAudioCue(groupId: AudioCueGroupId, allowManyInParallel?: boolean): void;
|
||||
}
|
||||
|
||||
export class AudioCueService extends Disposable implements IAudioCueService {
|
||||
@@ -51,6 +53,16 @@ export class AudioCueService extends Disposable implements IAudioCueService {
|
||||
await Promise.all(Array.from(sounds).map(sound => this.playSound(sound, true)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gaming and other apps often play a sound variant when the same event happens again
|
||||
* for an improved experience. This function plays a random sound from the given group to accomplish that.
|
||||
*/
|
||||
public playRandomAudioCue(groupId: AudioCueGroupId, allowManyInParallel?: boolean): void {
|
||||
const cues = AudioCue.allAudioCues.filter(cue => cue.groupId === groupId);
|
||||
const index = Math.floor(Math.random() * cues.length);
|
||||
this.playAudioCue(cues[index], allowManyInParallel);
|
||||
}
|
||||
|
||||
private getVolumeInPercent(): number {
|
||||
const volume = this.configurationService.getValue<number>('audioCues.volume');
|
||||
if (typeof volume !== 'number') {
|
||||
@@ -66,7 +78,6 @@ export class AudioCueService extends Disposable implements IAudioCueService {
|
||||
if (!allowManyInParallel && this.playingSounds.has(sound)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.playingSounds.add(sound);
|
||||
const url = FileAccess.asBrowserUri(`vs/platform/audioCues/browser/media/${sound.fileName}`).toString(true);
|
||||
|
||||
@@ -87,6 +98,23 @@ export class AudioCueService extends Disposable implements IAudioCueService {
|
||||
}
|
||||
}
|
||||
|
||||
public playAudioCueLoop(cue: AudioCue, milliseconds: number): IDisposable {
|
||||
let playing = true;
|
||||
const playSound = () => {
|
||||
if (playing) {
|
||||
this.playAudioCue(cue, true).finally(() => {
|
||||
setTimeout(() => {
|
||||
if (playing) {
|
||||
playSound();
|
||||
}
|
||||
}, milliseconds);
|
||||
});
|
||||
}
|
||||
};
|
||||
playSound();
|
||||
return toDisposable(() => playing = false);
|
||||
}
|
||||
|
||||
private readonly obsoleteAudioCuesEnabled = observableFromEvent(
|
||||
Event.filter(this.configurationService.onDidChangeConfiguration, (e) =>
|
||||
e.affectsConfiguration('audioCues.enabled')
|
||||
@@ -190,19 +218,30 @@ export class Sound {
|
||||
public static readonly diffLineInserted = Sound.register({ fileName: 'diffLineInserted.mp3' });
|
||||
public static readonly diffLineDeleted = Sound.register({ fileName: 'diffLineDeleted.mp3' });
|
||||
public static readonly diffLineModified = Sound.register({ fileName: 'diffLineModified.mp3' });
|
||||
public static readonly chatRequestSent = Sound.register({ fileName: 'chatRequestSent.mp3' });
|
||||
public static readonly chatResponsePending = Sound.register({ fileName: 'chatResponsePending.mp3' });
|
||||
public static readonly chatResponseReceived1 = Sound.register({ fileName: 'chatResponseReceived1.mp3' });
|
||||
public static readonly chatResponseReceived2 = Sound.register({ fileName: 'chatResponseReceived2.mp3' });
|
||||
public static readonly chatResponseReceived3 = Sound.register({ fileName: 'chatResponseReceived3.mp3' });
|
||||
public static readonly chatResponseReceived4 = Sound.register({ fileName: 'chatResponseReceived4.mp3' });
|
||||
public static readonly chatResponseReceived5 = Sound.register({ fileName: 'chatResponseReceived5.mp3' });
|
||||
|
||||
private constructor(public readonly fileName: string) { }
|
||||
}
|
||||
|
||||
export const enum AudioCueGroupId {
|
||||
chatResponseReceived = 'chatResponseReceived'
|
||||
}
|
||||
|
||||
export class AudioCue {
|
||||
private static _audioCues = new Set<AudioCue>();
|
||||
|
||||
private static register(options: {
|
||||
name: string;
|
||||
sound: Sound;
|
||||
settingsKey: string;
|
||||
groupId?: AudioCueGroupId;
|
||||
}): AudioCue {
|
||||
const audioCue = new AudioCue(options.sound, options.name, options.settingsKey);
|
||||
const audioCue = new AudioCue(options.sound, options.name, options.settingsKey, options.groupId);
|
||||
AudioCue._audioCues.add(audioCue);
|
||||
return audioCue;
|
||||
}
|
||||
@@ -309,9 +348,53 @@ export class AudioCue {
|
||||
settingsKey: 'audioCues.diffLineModified'
|
||||
});
|
||||
|
||||
public static readonly chatRequestSent = AudioCue.register({
|
||||
name: localize('audioCues.chatRequestSent', 'Chat Request Sent'),
|
||||
sound: Sound.chatRequestSent,
|
||||
settingsKey: 'audioCues.chatRequestSent'
|
||||
});
|
||||
|
||||
private static readonly chatResponseReceived = {
|
||||
name: localize('audioCues.chatResponseReceived', 'Chat Response Received'),
|
||||
settingsKey: 'audioCues.chatResponseReceived',
|
||||
groupId: AudioCueGroupId.chatResponseReceived
|
||||
};
|
||||
|
||||
public static readonly chatResponseReceived1 = AudioCue.register({
|
||||
sound: Sound.chatResponseReceived1,
|
||||
...this.chatResponseReceived
|
||||
});
|
||||
|
||||
public static readonly chatResponseReceived2 = AudioCue.register({
|
||||
sound: Sound.chatResponseReceived2,
|
||||
...this.chatResponseReceived
|
||||
});
|
||||
|
||||
public static readonly chatResponseReceived3 = AudioCue.register({
|
||||
sound: Sound.chatResponseReceived3,
|
||||
...this.chatResponseReceived
|
||||
});
|
||||
|
||||
public static readonly chatResponseReceived4 = AudioCue.register({
|
||||
sound: Sound.chatResponseReceived4,
|
||||
...this.chatResponseReceived
|
||||
});
|
||||
|
||||
public static readonly chatResponseReceived5 = AudioCue.register({
|
||||
sound: Sound.chatResponseReceived5,
|
||||
...this.chatResponseReceived
|
||||
});
|
||||
|
||||
public static readonly chatResponsePending = AudioCue.register({
|
||||
name: localize('audioCues.chatResponsePending', 'Chat Response Pending'),
|
||||
sound: Sound.chatResponsePending,
|
||||
settingsKey: 'audioCues.chatResponsePending'
|
||||
});
|
||||
|
||||
private constructor(
|
||||
public readonly sound: Sound,
|
||||
public readonly name: string,
|
||||
public readonly settingsKey: string,
|
||||
public readonly groupId?: string
|
||||
) { }
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -26,6 +26,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
export interface ICommandQuickPick extends IPickerQuickAccessItem {
|
||||
readonly commandId: string;
|
||||
readonly commandAlias?: string;
|
||||
readonly args?: any[];
|
||||
}
|
||||
|
||||
export interface ICommandsQuickAccessOptions extends IPickerQuickAccessProviderOptions<ICommandQuickPick> {
|
||||
@@ -211,7 +212,9 @@ export abstract class AbstractCommandsQuickAccessProvider extends PickerQuickAcc
|
||||
|
||||
// Run
|
||||
try {
|
||||
await this.commandService.executeCommand(commandPick.commandId);
|
||||
commandPick.args?.length
|
||||
? await this.commandService.executeCommand(commandPick.commandId, ...commandPick.args)
|
||||
: await this.commandService.executeCommand(commandPick.commandId);
|
||||
} catch (error) {
|
||||
if (!isCancellationError(error)) {
|
||||
this.dialogService.error(localize('canNotRun', "Command '{0}' resulted in an error", commandPick.label), toErrorMessage(error));
|
||||
|
||||
@@ -165,7 +165,7 @@ export class RemoteTunnelService extends Disposable implements IRemoteTunnelServ
|
||||
}
|
||||
|
||||
async startTunnel(session: IRemoteTunnelSession): Promise<TunnelStatus> {
|
||||
if (isSameSession(session, this._session)) {
|
||||
if (isSameSession(session, this._session) && this._tunnelStatus.type !== 'disconnected') {
|
||||
return this._tunnelStatus;
|
||||
}
|
||||
this.setSession(session);
|
||||
@@ -195,7 +195,7 @@ export class RemoteTunnelService extends Disposable implements IRemoteTunnelServ
|
||||
}
|
||||
};
|
||||
try {
|
||||
await this.runCodeTunneCommand('stop', ['kill'], onOutput);
|
||||
await this.runCodeTunnelCommand('stop', ['kill'], onOutput);
|
||||
} catch (e) {
|
||||
this._logger.error(e);
|
||||
}
|
||||
@@ -213,26 +213,35 @@ export class RemoteTunnelService extends Disposable implements IRemoteTunnelServ
|
||||
}
|
||||
|
||||
let isAttached = false;
|
||||
let output = '';
|
||||
|
||||
const onOutput = (a: string, isErr: boolean) => {
|
||||
if (isErr) {
|
||||
this._logger.error(a);
|
||||
} else {
|
||||
this._logger.info(a);
|
||||
output += a;
|
||||
}
|
||||
if (!this.environmentService.isBuilt && a.startsWith(' Compiling')) {
|
||||
this.setTunnelStatus(TunnelStates.connecting(localize('remoteTunnelService.building', 'Building CLI from sources')));
|
||||
}
|
||||
};
|
||||
|
||||
const statusProcess = this.runCodeTunneCommand('status', ['status'], onOutput);
|
||||
const statusProcess = this.runCodeTunnelCommand('status', ['status'], onOutput);
|
||||
this._tunnelProcess = statusProcess;
|
||||
try {
|
||||
const status = await statusProcess;
|
||||
await statusProcess;
|
||||
if (this._tunnelProcess !== statusProcess) {
|
||||
return;
|
||||
}
|
||||
isAttached = status === 0;
|
||||
|
||||
// split and find the line, since in dev builds additional noise is
|
||||
// added by cargo to the output.
|
||||
const status: {
|
||||
service_installed: boolean;
|
||||
tunnel: object | null;
|
||||
} = JSON.parse(output.trim().split('\n').find(l => l.startsWith('{'))!);
|
||||
|
||||
isAttached = !!status.tunnel;
|
||||
this._logger.info(isAttached ? 'Other tunnel running, attaching...' : 'No other tunnel running');
|
||||
if (!isAttached && !this._session) {
|
||||
this._tunnelProcess = undefined;
|
||||
@@ -255,7 +264,7 @@ export class RemoteTunnelService extends Disposable implements IRemoteTunnelServ
|
||||
a = a.replaceAll(token, '*'.repeat(4));
|
||||
onOutput(a, isErr);
|
||||
};
|
||||
const loginProcess = this.runCodeTunneCommand('login', ['user', 'login', '--provider', session.providerId, '--access-token', token, '--log', LogLevelToString(this._logger.getLevel())], onLoginOutput);
|
||||
const loginProcess = this.runCodeTunnelCommand('login', ['user', 'login', '--provider', session.providerId, '--access-token', token, '--log', LogLevelToString(this._logger.getLevel())], onLoginOutput);
|
||||
this._tunnelProcess = loginProcess;
|
||||
try {
|
||||
await loginProcess;
|
||||
@@ -286,7 +295,7 @@ export class RemoteTunnelService extends Disposable implements IRemoteTunnelServ
|
||||
if (this._preventSleep()) {
|
||||
args.push('--no-sleep');
|
||||
}
|
||||
const serveCommand = this.runCodeTunneCommand('tunnel', args, (message: string, isErr: boolean) => {
|
||||
const serveCommand = this.runCodeTunnelCommand('tunnel', args, (message: string, isErr: boolean) => {
|
||||
if (isErr) {
|
||||
this._logger.error(message);
|
||||
} else {
|
||||
@@ -315,7 +324,7 @@ export class RemoteTunnelService extends Disposable implements IRemoteTunnelServ
|
||||
});
|
||||
}
|
||||
|
||||
private runCodeTunneCommand(logLabel: string, commandArgs: string[], onOutput: (message: string, isError: boolean) => void = () => { }): CancelablePromise<number> {
|
||||
private runCodeTunnelCommand(logLabel: string, commandArgs: string[], onOutput: (message: string, isError: boolean) => void = () => { }): CancelablePromise<number> {
|
||||
return createCancelablePromise<number>(token => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (token.isCancellationRequested) {
|
||||
|
||||
@@ -5,10 +5,15 @@
|
||||
|
||||
import { SequencerByKey } from 'vs/base/common/async';
|
||||
import { IEncryptionService } from 'vs/platform/encryption/common/encryptionService';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IStorageService, InMemoryStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { Event, PauseableEmitter } from 'vs/base/common/event';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { isNative } from 'vs/base/common/platform';
|
||||
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { once } from 'vs/base/common/functional';
|
||||
|
||||
export const ISecretStorageService = createDecorator<ISecretStorageService>('secretStorageService');
|
||||
|
||||
@@ -40,7 +45,9 @@ export class SecretStorageService implements ISecretStorageService {
|
||||
constructor(
|
||||
@IStorageService private _storageService: IStorageService,
|
||||
@IEncryptionService private _encryptionService: IEncryptionService,
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@INotificationService private readonly _notificationService: INotificationService,
|
||||
@ILogService private readonly _logService: ILogService
|
||||
) {
|
||||
this._storageService.onDidChangeValue(e => this.onDidChangeValue(e.key));
|
||||
}
|
||||
@@ -89,6 +96,10 @@ export class SecretStorageService implements ISecretStorageService {
|
||||
return this._sequencer.queue(key, async () => {
|
||||
await this.initialized;
|
||||
|
||||
if (isNative && this.type !== 'persisted') {
|
||||
this.notifyNativeUserOnce();
|
||||
}
|
||||
|
||||
const encrypted = await this._encryptionService.encrypt(value);
|
||||
const fullKey = this.getKey(key);
|
||||
try {
|
||||
@@ -129,4 +140,20 @@ export class SecretStorageService implements ISecretStorageService {
|
||||
private getKey(key: string): string {
|
||||
return `${this._storagePrefix}${key}`;
|
||||
}
|
||||
|
||||
private notifyNativeUserOnce = once(() => this.notifyNativeUser());
|
||||
private notifyNativeUser(): void {
|
||||
this._notificationService.prompt(
|
||||
Severity.Warning,
|
||||
localize('notEncrypted', 'Secrets are not being stored on disk because encryption is not available in this environment.'),
|
||||
[{
|
||||
label: localize('openTroubleshooting', "Open Troubleshooting"),
|
||||
run: () => this._instantiationService.invokeFunction(accessor => {
|
||||
const openerService = accessor.get(IOpenerService);
|
||||
// Open troubleshooting docs page
|
||||
return openerService.open('https://go.microsoft.com/fwlink/?linkid=2239490');
|
||||
})
|
||||
}]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,14 +51,14 @@ export function traceRpc(_target: any, key: string, descriptor: any) {
|
||||
|
||||
descriptor[fnKey!] = async function (...args: any[]) {
|
||||
if (this.traceRpcArgs.logService.getLevel() === LogLevel.Trace) {
|
||||
this.traceRpcArgs.logService.trace(`[RPC Request] PtyService#${fnKey}(${args.map(e => JSON.stringify(e)).join(', ')})`);
|
||||
this.traceRpcArgs.logService.trace(`[RPC Request] PtyService#${fn!.name}(${args.map(e => JSON.stringify(e)).join(', ')})`);
|
||||
}
|
||||
if (this.traceRpcArgs.simulatedLatency) {
|
||||
await timeout(this.traceRpcArgs.simulatedLatency);
|
||||
}
|
||||
const result = await fn!.apply(this, args);
|
||||
if (this.traceRpcArgs.logService.getLevel() === LogLevel.Trace) {
|
||||
this.traceRpcArgs.logService.trace(`[RPC Response] PtyService#${fnKey}`, result);
|
||||
this.traceRpcArgs.logService.trace(`[RPC Response] PtyService#${fn!.name}`, result);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
@@ -22,6 +22,7 @@ import * as platform from 'vs/base/common/platform';
|
||||
import { createRegExp, escapeRegExpCharacters } from 'vs/base/common/strings';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { getOSReleaseInfo } from 'vs/base/node/osReleaseInfo';
|
||||
import { findFreePort } from 'vs/base/node/ports';
|
||||
import { addUNCHostToAllowlist, disableUNCAccessRestrictions } from 'vs/base/node/unc';
|
||||
import { PersistentProtocol } from 'vs/base/parts/ipc/common/ipc.net';
|
||||
@@ -788,7 +789,7 @@ export async function createServer(address: string | net.AddressInfo | null, arg
|
||||
const vscodeServerListenTime: number = (<any>global).vscodeServerListenTime;
|
||||
const vscodeServerCodeLoadedTime: number = (<any>global).vscodeServerCodeLoadedTime;
|
||||
|
||||
instantiationService.invokeFunction((accessor) => {
|
||||
instantiationService.invokeFunction(async (accessor) => {
|
||||
const telemetryService = accessor.get(ITelemetryService);
|
||||
|
||||
type ServerStartClassification = {
|
||||
@@ -811,6 +812,30 @@ export async function createServer(address: string | net.AddressInfo | null, arg
|
||||
codeLoadedTime: vscodeServerCodeLoadedTime,
|
||||
readyTime: currentTime
|
||||
});
|
||||
|
||||
if (platform.isLinux) {
|
||||
const logService = accessor.get(ILogService);
|
||||
const releaseInfo = await getOSReleaseInfo(logService.error.bind(logService));
|
||||
if (releaseInfo) {
|
||||
type ServerPlatformInfoClassification = {
|
||||
platformId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'A string identifying the operating system without any version information.' };
|
||||
platformVersionId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'A string identifying the operating system version excluding any name information or release code.' };
|
||||
platformIdLike: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'A string identifying the operating system the current OS derivate is closely related to.' };
|
||||
owner: 'deepak1556';
|
||||
comment: 'Provides insight into the distro information on Linux.';
|
||||
};
|
||||
type ServerPlatformInfoEvent = {
|
||||
platformId: string;
|
||||
platformVersionId: string | undefined;
|
||||
platformIdLike: string | undefined;
|
||||
};
|
||||
telemetryService.publicLog2<ServerPlatformInfoEvent, ServerPlatformInfoClassification>('serverPlatformInfo', {
|
||||
platformId: releaseInfo.id,
|
||||
platformVersionId: releaseInfo.version_id,
|
||||
platformIdLike: releaseInfo.id_like
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (args['print-startup-performance']) {
|
||||
|
||||
@@ -49,7 +49,7 @@ export class MainThreadNotebooks implements MainThreadNotebookShape {
|
||||
disposables.add(this._notebookService.registerNotebookSerializer(viewType, extension, {
|
||||
options,
|
||||
dataToNotebook: async (data: VSBuffer): Promise<NotebookData> => {
|
||||
const sw = new StopWatch(true);
|
||||
const sw = new StopWatch();
|
||||
const dto = await this._proxy.$dataToNotebook(handle, data, CancellationToken.None);
|
||||
const result = NotebookDto.fromNotebookDataDto(dto.value);
|
||||
this._logService.trace(`[NotebookSerializer] dataToNotebook DONE after ${sw.elapsed()}ms`, {
|
||||
@@ -59,7 +59,7 @@ export class MainThreadNotebooks implements MainThreadNotebookShape {
|
||||
return result;
|
||||
},
|
||||
notebookToData: (data: NotebookData): Promise<VSBuffer> => {
|
||||
const sw = new StopWatch(true);
|
||||
const sw = new StopWatch();
|
||||
const result = this._proxy.$notebookToData(handle, new SerializableObjectWithBuffers(NotebookDto.toNotebookDataDto(data)), CancellationToken.None);
|
||||
this._logService.trace(`[NotebookSerializer] notebookToData DONE after ${sw.elapsed()}`, {
|
||||
viewType,
|
||||
|
||||
@@ -391,7 +391,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
const COUNT = 2;
|
||||
let sum = 0;
|
||||
for (let i = 0; i < COUNT; i++) {
|
||||
const sw = StopWatch.create(true);
|
||||
const sw = StopWatch.create();
|
||||
await this._proxy.$acceptProcessRequestLatency(terminalId);
|
||||
sw.stop();
|
||||
sum += sw.elapsed();
|
||||
|
||||
@@ -982,7 +982,7 @@ class CompletionsAdapter {
|
||||
const replaceRange = doc.getWordRangeAtPosition(pos) || new Range(pos, pos);
|
||||
const insertRange = replaceRange.with({ end: pos });
|
||||
|
||||
const sw = new StopWatch(true);
|
||||
const sw = new StopWatch();
|
||||
const itemsOrList = await this._provider.provideCompletionItems(doc, pos, token, typeConvert.CompletionContext.to(context));
|
||||
|
||||
if (!itemsOrList) {
|
||||
|
||||
@@ -83,7 +83,7 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService {
|
||||
const fullSource = `${source}\n//# sourceURL=${sourceURL}`;
|
||||
let initFn: Function;
|
||||
try {
|
||||
initFn = new Function('module', 'exports', 'require', fullSource);
|
||||
initFn = new Function('module', 'exports', 'require', fullSource); // CodeQL [SM01632] js/eval-call there is no alternative until we move to ESM
|
||||
} catch (err) {
|
||||
if (extensionId) {
|
||||
console.error(`Loading code for extension ${extensionId} failed: ${err.message}`);
|
||||
|
||||
@@ -358,7 +358,7 @@ const registry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Con
|
||||
'type': 'boolean',
|
||||
tags: ['experimental'],
|
||||
'description': localize('useSemanticSimilarity', "Controls whether the command palette should include similar commands. You must have an extension installed that provides Semantic Similarity."),
|
||||
'default': false
|
||||
'default': true
|
||||
},
|
||||
'workbench.quickOpen.closeOnFocusLost': {
|
||||
'type': 'boolean',
|
||||
|
||||
@@ -60,7 +60,7 @@ class AccessibleView extends Disposable {
|
||||
contributions: EditorExtensionsRegistry.getSomeEditorContributions([LinkDetector.ID, SelectionClipboardContributionID, 'editor.contrib.selectionAnchorController'])
|
||||
};
|
||||
const editorOptions: IEditorConstructionOptions = {
|
||||
...getSimpleEditorOptions(),
|
||||
...getSimpleEditorOptions(this._configurationService),
|
||||
lineDecorationsWidth: 6,
|
||||
dragAndDrop: true,
|
||||
cursorWidth: 1,
|
||||
@@ -70,8 +70,6 @@ class AccessibleView extends Disposable {
|
||||
quickSuggestions: false,
|
||||
renderWhitespace: 'none',
|
||||
dropIntoEditor: { enabled: true },
|
||||
accessibilitySupport: this._configurationService.getValue<'auto' | 'off' | 'on'>('editor.accessibilitySupport'),
|
||||
cursorBlinking: this._configurationService.getValue('terminal.integrated.cursorBlinking'),
|
||||
readOnly: true
|
||||
};
|
||||
this._editorWidget = this._register(this._instantiationService.createInstance(CodeEditorWidget, this._editorContainer, editorOptions, codeEditorWidgetOptions));
|
||||
|
||||
@@ -117,6 +117,21 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).regis
|
||||
'description': localize('audioCues.notebookCellFailed', "Plays a sound when a notebook cell execution fails."),
|
||||
...audioCueFeatureBase,
|
||||
},
|
||||
'audioCues.chatRequestSent': {
|
||||
'description': localize('audioCues.chatRequestSent', "Plays a sound when a chat request is made."),
|
||||
...audioCueFeatureBase,
|
||||
default: 'off'
|
||||
},
|
||||
'audioCues.chatResponsePending': {
|
||||
'description': localize('audioCues.chatResponsePending', "Plays a sound on loop while the response is pending."),
|
||||
...audioCueFeatureBase,
|
||||
default: 'off'
|
||||
},
|
||||
'audioCues.chatResponseReceived': {
|
||||
'description': localize('audioCues.chatResponseReceived', "Plays a sound on loop while the response has been received."),
|
||||
...audioCueFeatureBase,
|
||||
default: 'off'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -24,9 +24,10 @@ import { IChatReplyFollowup, IChatService } from 'vs/workbench/contrib/chat/comm
|
||||
import { ChatViewModel } from 'vs/workbench/contrib/chat/common/chatViewModel';
|
||||
import { CHAT_CATEGORY } from 'vs/workbench/contrib/chat/browser/actions/chatActions';
|
||||
import { IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { CONTEXT_PROVIDER_EXISTS } from 'vs/workbench/contrib/chat/common/chatContextKeys';
|
||||
|
||||
export const ASK_QUICK_QUESTION_ACTION_ID = 'chat.action.askQuickQuestion';
|
||||
|
||||
export function registerChatQuickQuestionActions() {
|
||||
registerAction2(AskQuickQuestionAction);
|
||||
}
|
||||
@@ -41,15 +42,14 @@ class AskQuickQuestionAction extends Action2 {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: 'chat.action.askQuickQuestion',
|
||||
id: ASK_QUICK_QUESTION_ACTION_ID,
|
||||
title: { value: localize('askQuickQuestion', "Ask Quick Question"), original: 'Ask Quick Question' },
|
||||
precondition: CONTEXT_PROVIDER_EXISTS,
|
||||
f1: true,
|
||||
f1: false,
|
||||
category: CHAT_CATEGORY,
|
||||
keybinding: {
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyI,
|
||||
when: ContextKeyExpr.equals('config.chat.experimental.quickQuestion.enable', true),
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyI
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -22,11 +22,11 @@ import { registerChatExecuteActions } from 'vs/workbench/contrib/chat/browser/ac
|
||||
import { registerChatQuickQuestionActions } from 'vs/workbench/contrib/chat/browser/actions/chatQuickInputActions';
|
||||
import { registerChatTitleActions } from 'vs/workbench/contrib/chat/browser/actions/chatTitleActions';
|
||||
import { registerChatExportActions } from 'vs/workbench/contrib/chat/browser/actions/chatImportExport';
|
||||
import { IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat';
|
||||
import { IChatAccessibilityService, IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat';
|
||||
import { ChatContributionService } from 'vs/workbench/contrib/chat/browser/chatContributionServiceImpl';
|
||||
import { ChatEditor, IChatEditorOptions } from 'vs/workbench/contrib/chat/browser/chatEditor';
|
||||
import { ChatEditorInput, ChatEditorInputSerializer } from 'vs/workbench/contrib/chat/browser/chatEditorInput';
|
||||
import { ChatWidgetService } from 'vs/workbench/contrib/chat/browser/chatWidget';
|
||||
import { ChatAccessibilityService, ChatWidgetService } from 'vs/workbench/contrib/chat/browser/chatWidget';
|
||||
import 'vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib';
|
||||
import { IChatContributionService } from 'vs/workbench/contrib/chat/common/chatContributionService';
|
||||
import { IChatService } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
@@ -70,12 +70,6 @@ configurationRegistry.registerConfiguration({
|
||||
type: 'number',
|
||||
description: nls.localize('interactiveSession.editor.lineHeight', "Controls the line height in pixels in chat codeblocks. Use 0 to compute the line height from the font size."),
|
||||
default: 0
|
||||
},
|
||||
'chat.experimental.quickQuestion.enable': {
|
||||
type: 'boolean',
|
||||
description: nls.localize('interactiveSession.experimental.quickQuestion.enable', "Controls whether the quick question feature is enabled."),
|
||||
default: false,
|
||||
tags: ['experimental']
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -136,5 +130,6 @@ registerClearActions();
|
||||
registerSingleton(IChatService, ChatService, InstantiationType.Delayed);
|
||||
registerSingleton(IChatContributionService, ChatContributionService, InstantiationType.Delayed);
|
||||
registerSingleton(IChatWidgetService, ChatWidgetService, InstantiationType.Delayed);
|
||||
registerSingleton(IChatAccessibilityService, ChatAccessibilityService, InstantiationType.Delayed);
|
||||
registerSingleton(IChatWidgetHistoryService, ChatWidgetHistoryService, InstantiationType.Delayed);
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import { URI } from 'vs/base/common/uri';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
export const IChatWidgetService = createDecorator<IChatWidgetService>('chatWidgetService');
|
||||
export const IChatAccessibilityService = createDecorator<IChatAccessibilityService>('chatAccessibilityService');
|
||||
|
||||
export interface IChatWidgetService {
|
||||
|
||||
@@ -29,6 +30,13 @@ export interface IChatWidgetService {
|
||||
getWidgetByInputUri(uri: URI): IChatWidget | undefined;
|
||||
}
|
||||
|
||||
|
||||
export interface IChatAccessibilityService {
|
||||
readonly _serviceBrand: undefined;
|
||||
acceptRequest(): void;
|
||||
acceptResponse(response?: IChatResponseViewModel): void;
|
||||
}
|
||||
|
||||
export interface IChatCodeBlockInfo {
|
||||
codeBlockIndex: number;
|
||||
element: IChatResponseViewModel;
|
||||
|
||||
@@ -190,7 +190,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge
|
||||
historyNavigationForwardsEnablement.set(enabled);
|
||||
};
|
||||
|
||||
const options = getSimpleEditorOptions();
|
||||
const options = getSimpleEditorOptions(this.configurationService);
|
||||
options.readOnly = false;
|
||||
options.ariaLabel = this._getAriaLabel();
|
||||
options.fontFamily = DEFAULT_FONT_FAMILY;
|
||||
|
||||
@@ -396,7 +396,11 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
|
||||
const slashCommands = this.delegate.getSlashCommands();
|
||||
const usedSlashCommand = slashCommands.find(s => markdown.value.startsWith(`/${s.command} `));
|
||||
const toRender = usedSlashCommand ? markdown.value.slice(usedSlashCommand.command.length + 2) : markdown.value;
|
||||
markdown = new MarkdownString(toRender);
|
||||
markdown = new MarkdownString(toRender, {
|
||||
isTrusted: {
|
||||
enabledCommands: ['vscode.open']
|
||||
},
|
||||
});
|
||||
|
||||
const codeblocks: IChatCodeBlockInfo[] = [];
|
||||
const result = this.renderer.render(markdown, {
|
||||
@@ -612,7 +616,7 @@ class CodeBlockPart extends Disposable implements IChatResultCodeBlockPart {
|
||||
}));
|
||||
const editorElement = dom.append(this.element, $('.interactive-result-editor'));
|
||||
this.editor = this._register(scopedInstantiationService.createInstance(CodeEditorWidget, editorElement, {
|
||||
...getSimpleEditorOptions(),
|
||||
...getSimpleEditorOptions(this.configurationService),
|
||||
readOnly: true,
|
||||
lineNumbers: 'off',
|
||||
selectOnLineNumbers: true,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { alert } from 'vs/base/browser/ui/aria/aria';
|
||||
import { status } from 'vs/base/browser/ui/aria/aria';
|
||||
import { ITreeContextMenuEvent, ITreeElement } from 'vs/base/browser/ui/tree/tree';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
@@ -16,6 +16,7 @@ import 'vs/css!./media/chat';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { localize } from 'vs/nls';
|
||||
import { MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { AudioCue, AudioCueGroupId, IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService';
|
||||
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';
|
||||
@@ -23,7 +24,7 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle
|
||||
import { WorkbenchObjectTree } from 'vs/platform/list/browser/listService';
|
||||
import { IViewsService } from 'vs/workbench/common/views';
|
||||
import { clearChatSession } from 'vs/workbench/contrib/chat/browser/actions/chatClear';
|
||||
import { ChatTreeItem, IChatCodeBlockInfo, IChatWidget, IChatWidgetService, IChatWidgetViewContext } from 'vs/workbench/contrib/chat/browser/chat';
|
||||
import { ChatTreeItem, IChatAccessibilityService, IChatCodeBlockInfo, IChatWidget, IChatWidgetService, IChatWidgetViewContext } from 'vs/workbench/contrib/chat/browser/chat';
|
||||
import { ChatInputPart } from 'vs/workbench/contrib/chat/browser/chatInputPart';
|
||||
import { ChatAccessibilityProvider, ChatListDelegate, ChatListItemRenderer, IChatRendererDelegate } from 'vs/workbench/contrib/chat/browser/chatListRenderer';
|
||||
import { ChatEditorOptions } from 'vs/workbench/contrib/chat/browser/chatOptions';
|
||||
@@ -115,6 +116,7 @@ export class ChatWidget extends Disposable implements IChatWidget {
|
||||
@IChatService private readonly chatService: IChatService,
|
||||
@IChatWidgetService chatWidgetService: IChatWidgetService,
|
||||
@IContextMenuService private readonly contextMenuService: IContextMenuService,
|
||||
@IChatAccessibilityService private readonly _chatAccessibilityService: IChatAccessibilityService
|
||||
) {
|
||||
super();
|
||||
CONTEXT_IN_CHAT_SESSION.bindTo(contextKeyService).set(true);
|
||||
@@ -388,18 +390,17 @@ export class ChatWidget extends Disposable implements IChatWidget {
|
||||
this.instantiationService.invokeFunction(clearChatSession, this);
|
||||
return;
|
||||
}
|
||||
|
||||
this._chatAccessibilityService.acceptRequest();
|
||||
const input = query ?? editorValue;
|
||||
const result = await this.chatService.sendRequest(this.viewModel.sessionId, input);
|
||||
|
||||
if (result) {
|
||||
this.inputPart.acceptInput(query);
|
||||
result.responseCompletePromise.then(() => {
|
||||
result.responseCompletePromise.then(async () => {
|
||||
|
||||
const responses = this.viewModel?.getItems().filter(isResponseVM);
|
||||
const lastResponse = responses?.[responses.length - 1];
|
||||
if (lastResponse) {
|
||||
const errorDetails = lastResponse.errorDetails ? ` ${lastResponse.errorDetails.message}` : '';
|
||||
alert(lastResponse.response.value + errorDetails);
|
||||
}
|
||||
this._chatAccessibilityService.acceptResponse(lastResponse);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -429,7 +430,7 @@ export class ChatWidget extends Disposable implements IChatWidget {
|
||||
}
|
||||
|
||||
layout(height: number, width: number): void {
|
||||
width = Math.min(width, 600);
|
||||
width = Math.min(width, 850);
|
||||
this.bodyDimension = new dom.Dimension(width, height);
|
||||
|
||||
const inputPartHeight = this.inputPart.layout(height, width);
|
||||
@@ -505,3 +506,30 @@ export class ChatWidgetService implements IChatWidgetService {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const CHAT_RESPONSE_PENDING_AUDIO_CUE_LOOP_MS = 7000;
|
||||
export class ChatAccessibilityService extends Disposable implements IChatAccessibilityService {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
private _responsePendingAudioCue: IDisposable | undefined;
|
||||
|
||||
constructor(@IAudioCueService private readonly _audioCueService: IAudioCueService) {
|
||||
super();
|
||||
}
|
||||
acceptRequest(): void {
|
||||
this._audioCueService.playAudioCue(AudioCue.chatRequestSent, true);
|
||||
this._responsePendingAudioCue = this._audioCueService.playAudioCueLoop(AudioCue.chatResponsePending, CHAT_RESPONSE_PENDING_AUDIO_CUE_LOOP_MS);
|
||||
}
|
||||
acceptResponse(response?: IChatResponseViewModel): void {
|
||||
this._responsePendingAudioCue?.dispose();
|
||||
this._audioCueService.playRandomAudioCue(AudioCueGroupId.chatResponseReceived, true);
|
||||
if (!response) {
|
||||
return;
|
||||
}
|
||||
const errorDetails = response.errorDetails ? ` ${response.errorDetails.message}` : '';
|
||||
status(response.response.value + errorDetails);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.interactive-session {
|
||||
max-width: 600px;
|
||||
max-width: 850px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ import * as strings from 'vs/base/common/strings';
|
||||
import { TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { showHistoryKeybindingHint } from 'vs/platform/history/browser/historyWidgetKeybindingHint';
|
||||
import { alert as alertFn } from 'vs/base/browser/ui/aria/aria';
|
||||
import { status } from 'vs/base/browser/ui/aria/aria';
|
||||
import { defaultInputBoxStyles, defaultToggleStyles } from 'vs/platform/theme/browser/defaultStyles';
|
||||
|
||||
const NLS_FIND_INPUT_LABEL = nls.localize('label.find', "Find");
|
||||
@@ -374,7 +374,7 @@ export abstract class SimpleFindWidget extends Widget {
|
||||
} else {
|
||||
label = NLS_NO_RESULTS;
|
||||
}
|
||||
alertFn(this._announceSearchResults(label, this.inputValue));
|
||||
status(this._announceSearchResults(label, this.inputValue));
|
||||
this._matchesCount.appendChild(document.createTextNode(label));
|
||||
this._foundMatch = !!count && count.resultCount > 0;
|
||||
this.updateButtons(this._foundMatch);
|
||||
|
||||
@@ -12,8 +12,9 @@ import { MenuPreventer } from 'vs/workbench/contrib/codeEditor/browser/menuPreve
|
||||
import { SelectionClipboardContributionID } from 'vs/workbench/contrib/codeEditor/browser/selectionClipboard';
|
||||
import { TabCompletionController } from 'vs/workbench/contrib/snippets/browser/tabCompletion';
|
||||
import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
|
||||
export function getSimpleEditorOptions(): IEditorOptions {
|
||||
export function getSimpleEditorOptions(configurationService: IConfigurationService): IEditorOptions {
|
||||
return {
|
||||
wordWrap: 'on',
|
||||
overviewRulerLanes: 0,
|
||||
@@ -39,7 +40,9 @@ export function getSimpleEditorOptions(): IEditorOptions {
|
||||
},
|
||||
guides: {
|
||||
indentation: false
|
||||
}
|
||||
},
|
||||
accessibilitySupport: configurationService.getValue<'auto' | 'off' | 'on'>('editor.accessibilitySupport'),
|
||||
cursorBlinking: configurationService.getValue<'blink' | 'smooth' | 'phase' | 'expand' | 'solid'>('editor.cursorBlinking')
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+1
-3
@@ -155,11 +155,9 @@ export class SuggestEnabledInput extends Widget {
|
||||
this.placeholderText = append(this.stylingContainer, $('.suggest-input-placeholder', undefined, options.placeholderText || ''));
|
||||
|
||||
const editorOptions: IEditorConstructionOptions = mixin(
|
||||
getSimpleEditorOptions(),
|
||||
getSimpleEditorOptions(configurationService),
|
||||
getSuggestEnabledInputOptions(ariaLabel));
|
||||
editorOptions.overflowWidgetsDomNode = options.overflowWidgetsDomNode;
|
||||
editorOptions.accessibilitySupport = configurationService.getValue<'auto' | 'off' | 'on'>('editor.accessibilitySupport');
|
||||
editorOptions.cursorBlinking = configurationService.getValue<'blink' | 'smooth' | 'phase' | 'expand' | 'solid'>('editor.cursorBlinking');
|
||||
|
||||
const scopedContextKeyService = this.getScopedContextKeyService(contextKeyService);
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ import { SmoothScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollable
|
||||
import { DomEmitter } from 'vs/base/browser/event';
|
||||
import { CommentContextKeys } from 'vs/workbench/contrib/comments/common/commentContextKeys';
|
||||
import { FileAccess } from 'vs/base/common/network';
|
||||
import { COMMENTS_SECTION, ICommentsConfiguration } from 'vs/workbench/contrib/comments/common/commentsConfiguration';
|
||||
|
||||
class CommentsActionRunner extends ActionRunner {
|
||||
protected override async runAction(action: IAction, context: any[]): Promise<void> {
|
||||
@@ -131,6 +132,9 @@ export class CommentNode<T extends IRange | ICellRange> extends Disposable {
|
||||
this.createHeader(this._commentDetailsContainer);
|
||||
this._body = document.createElement(`div`);
|
||||
this._body.classList.add('comment-body', MOUSE_CURSOR_TEXT_CSS_CLASS_NAME);
|
||||
if (configurationService.getValue<ICommentsConfiguration | undefined>(COMMENTS_SECTION)?.maxHeight !== false) {
|
||||
this._body.classList.add('comment-body-max-height');
|
||||
}
|
||||
|
||||
this.createScroll(this._commentDetailsContainer, this._body);
|
||||
this.updateCommentBody(this.comment.body);
|
||||
|
||||
@@ -39,6 +39,11 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).regis
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: nls.localize('comments.visible', "Controls the visibility of the comments bar and comment threads in editors that have commenting ranges and comments. Comments are still accessible via the Comments view and will cause commenting to be toggled on in the same way running the command \"Comments: Toggle Editor Commenting\" toggles comments.")
|
||||
},
|
||||
'comments.maxHeight': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: nls.localize('comments.maxHeight', "Controls whether the comments widget scrolls or expands.")
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -136,6 +136,9 @@
|
||||
|
||||
.review-widget .body .review-comment .review-comment-contents .comment-body {
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.review-widget .body .review-comment .review-comment-contents .comment-body-max-height {
|
||||
max-height: 20em;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user