Merge branch 'main' into do-162262

This commit is contained in:
John Murray
2024-03-06 15:56:59 +00:00
committed by GitHub
305 changed files with 6447 additions and 3440 deletions
+2 -2
View File
@@ -1,5 +1,5 @@
disturl "https://electronjs.org/headers"
target "28.2.2"
ms_build_id "26836304"
target "28.2.5"
ms_build_id "27336930"
runtime "electron"
build_from_source "true"
+14 -14
View File
@@ -1,34 +1,34 @@
<!-- BEGIN MICROSOFT SECURITY.MD V0.0.5 BLOCK -->
<!-- BEGIN MICROSOFT SECURITY.MD V0.0.9 BLOCK -->
## Security
Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet) and [Xamarin](https://github.com/xamarin).
If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below.
If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/security.md/definition), please report it to us as described below.
## Reporting Security Issues
**Please do not report security vulnerabilities through public GitHub issues.**
Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report).
Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/security.md/msrc/create-report).
If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc).
If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/security.md/msrc/pgp).
You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc).
Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
* Full paths of source file(s) related to the manifestation of the issue
* The location of the affected source code (tag/branch/commit or direct URL)
* Any special configuration required to reproduce the issue
* Step-by-step instructions to reproduce the issue
* Proof-of-concept or exploit code (if possible)
* Impact of the issue, including how an attacker might exploit the issue
* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
* Full paths of source file(s) related to the manifestation of the issue
* The location of the affected source code (tag/branch/commit or direct URL)
* Any special configuration required to reproduce the issue
* Step-by-step instructions to reproduce the issue
* Proof-of-concept or exploit code (if possible)
* Impact of the issue, including how an attacker might exploit the issue
This information will help us triage your report more quickly.
If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs.
If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/security.md/msrc/bounty) page for more details about our active programs.
## Preferred Languages
@@ -36,6 +36,6 @@ We prefer all communications to be in English.
## Policy
Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd).
Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/security.md/cvd).
<!-- END MICROSOFT SECURITY.MD BLOCK -->
+75 -75
View File
@@ -1,75 +1,75 @@
b1478a79a80e453e9ee39ad4a0b0e0d871f3e369ec519e3ac5a1da20bb7bdbf3 *chromedriver-v28.2.2-darwin-arm64.zip
4e7c4651d610c70de883b9ceef633f1a2bf90e0f3a732eae7a6d7bcad11bb2df *chromedriver-v28.2.2-darwin-x64.zip
7cee31da7d90c2a24338a10046386517bb93c69c79bd44cfcc9372a551fc7d01 *chromedriver-v28.2.2-linux-arm64.zip
2056e41f713d1a6c83d1f0260c0f2b8addc3c49887ae85ca7e92267eb53951e8 *chromedriver-v28.2.2-linux-armv7l.zip
19503257092605e21bd3798f5ffd0049d8420a504ececef7b1e95d3733846874 *chromedriver-v28.2.2-linux-x64.zip
ec09eeb8a7040c7402a8a5f54491b33e5dc95ea0535b55381a3ec405014f08db *chromedriver-v28.2.2-mas-arm64.zip
1dd5cb2a113c74ae84f2ac98f6f40da2c367014381a547788fea9ae220e6fc9f *chromedriver-v28.2.2-mas-x64.zip
fd505e1f1c2f72266c48914690a48918fc7920877215a508ea5325cf0353f72c *chromedriver-v28.2.2-win32-arm64.zip
c0226c0fb260d6812185eeea718c8c0054d0fcac995bb1ccb333f852206372c8 *chromedriver-v28.2.2-win32-ia32.zip
b6daccad5bcd3046d0678c927f6b97ed91f2242f716deb0de95a0ee2303af818 *chromedriver-v28.2.2-win32-x64.zip
76a88da92b950c882d90c3dcb26e0c2ca5e5a52ad7a066ec0b3cbf9cc4d04563 *electron-api.json
6ad08d733c95de3c30560e8289d0e657ed5ee03bc8ba9d1f11d528851e5b7fba *electron-v28.2.2-darwin-arm64-dsym-snapshot.zip
8f0d450f3d2392cbe7a6cb274ec0f3bf63da66c98fa0baaa2355e69f1c93b151 *electron-v28.2.2-darwin-arm64-dsym.zip
262036eb86b767db0d199df022b8b432aa3714e451b9ac656af7ef031581b44a *electron-v28.2.2-darwin-arm64-symbols.zip
23119b333c47a5ea9e36e04cdc3b8c5955cfccfeb90994f1fecea4722bfb8dcc *electron-v28.2.2-darwin-arm64.zip
384015a3e49a6846ebefc78f9f01ce6d47c2ec109e6223907298aa6382b0d072 *electron-v28.2.2-darwin-x64-dsym-snapshot.zip
434838821de746ff71baafdf9e0df07cb3766dd73eb7fcd253aee0571bd0cd59 *electron-v28.2.2-darwin-x64-dsym.zip
470087b5d631dc0032611048d5fc23faed9a71ec2c36a528c5a50c2e357d1716 *electron-v28.2.2-darwin-x64-symbols.zip
48f3424b3cbdf602a13f451361ade2f7f2896a354a51f78da4239dbdf2d1218b *electron-v28.2.2-darwin-x64.zip
d5bf835ba4b2eaa4435946f97ad7ac3e7243564037423cfaadaf5cb03af4ddbc *electron-v28.2.2-linux-arm64-debug.zip
90550f29b1f032ebcf467dc81f4915c322f93855a4658cf74261f68a3ccdc21e *electron-v28.2.2-linux-arm64-symbols.zip
746284eb1d8029b0f6b02281543ab2ecf45f071da21407f45b2b32d1ff268310 *electron-v28.2.2-linux-arm64.zip
d5bf835ba4b2eaa4435946f97ad7ac3e7243564037423cfaadaf5cb03af4ddbc *electron-v28.2.2-linux-armv7l-debug.zip
80cc8d9333156caaee59c7ddf3bd77712be8379b51f4218063e6c176a4ec2c26 *electron-v28.2.2-linux-armv7l-symbols.zip
f4580e8877481c0526110feaa78372ed3045bfbf5a6ba4b14e8cd155b9965f5e *electron-v28.2.2-linux-armv7l.zip
da33d92871768e4cf95b143c6022830d97b0ec2d4120463ab71b48597f940f07 *electron-v28.2.2-linux-x64-debug.zip
18dce5283513abd94b79a1636d25e3453f5c33d335425a234b9967dd4e5ce942 *electron-v28.2.2-linux-x64-symbols.zip
1eeb6ebc3b0699cae1fb171bbf7c9105e716db833f6e73a90f4ca161f17ffb15 *electron-v28.2.2-linux-x64.zip
e74da15d90e52cddf0f0f14663f6313df585b486b002966f6016c1b148cdd70d *electron-v28.2.2-mas-arm64-dsym-snapshot.zip
498357eb2e784bff54c5ac59fd3eada814d130f12a5e77d47c468f2305377717 *electron-v28.2.2-mas-arm64-dsym.zip
849fa891d072d06b1e929eb1acfbe7ac83f0238483039f8e1102e01e5223c3f5 *electron-v28.2.2-mas-arm64-symbols.zip
621fd91d70cb33ec58543fc57762e692dfa0e272a53f3316fd215ffa88bd075b *electron-v28.2.2-mas-arm64.zip
0f9e2ab79bca99f44c1e9a140929fad6d2cd37def60303974f5a82ca95dd9a69 *electron-v28.2.2-mas-x64-dsym-snapshot.zip
51c29e047ba7d8669030cc9615f70ecaa5c9519cd04ab5e62822c0d4f21f5fbb *electron-v28.2.2-mas-x64-dsym.zip
25da93f45b095a3669475416832647a01f2a02a95dcc064dfabdf9c621045106 *electron-v28.2.2-mas-x64-symbols.zip
3b6931362f1b7f377624ea7c6ccf069f291e4e675a28f12a56e3e75355c13fbd *electron-v28.2.2-mas-x64.zip
cece93c232c65bf4e1b918b9645f5a2e247bd3f8bb2dd9e6e889a402060a103b *electron-v28.2.2-win32-arm64-pdb.zip
4a46e1ead0de7b6f757c1194add6467b3375a8dcfb02d903e481c0d8db5c7e5d *electron-v28.2.2-win32-arm64-symbols.zip
c9f31ae6408aa6936b5d683eda601773789185890375cd097e61e924d4fed77a *electron-v28.2.2-win32-arm64-toolchain-profile.zip
083f95abbce97cab70e77b86e39cff01ff1df121f36b9da581ead960ae329f69 *electron-v28.2.2-win32-arm64.zip
f9b4633bc03fe7c77db4b335121e7e3e05f6788c6752ccb3f68267e664d4323a *electron-v28.2.2-win32-ia32-pdb.zip
d20f70ea8dc86477f723d104d42fe78e2508577ef3b1eb6ec812366f18ad80d8 *electron-v28.2.2-win32-ia32-symbols.zip
c9f31ae6408aa6936b5d683eda601773789185890375cd097e61e924d4fed77a *electron-v28.2.2-win32-ia32-toolchain-profile.zip
c57691b73592632829cef136be6dd356a82331450920fd024ac3589654d23550 *electron-v28.2.2-win32-ia32.zip
b8a14fc75b9205a4b82aa008e23a020e9fac694399b47390a163c3100ac7946d *electron-v28.2.2-win32-x64-pdb.zip
780089dde95ce1ab5da176ad53d9c7cd122085809622852a3132b80b93faac9b *electron-v28.2.2-win32-x64-symbols.zip
c9f31ae6408aa6936b5d683eda601773789185890375cd097e61e924d4fed77a *electron-v28.2.2-win32-x64-toolchain-profile.zip
5fbc76585891b0d7b09c938f7be25b7ab36b3768491021b053dc99bc70a8aa29 *electron-v28.2.2-win32-x64.zip
225268475350fa71d9fdea966160fc91379ced2f2353a902addf65d5f9b0dbf1 *electron.d.ts
59a8d6b81d93bc99ecf099fac6492eb67ba601386cce07261a009a5b99e75479 *ffmpeg-v28.2.2-darwin-arm64.zip
15386f238dce9ba40714336265422cc41a1ef0608041f562a8fd42e3813ddc64 *ffmpeg-v28.2.2-darwin-x64.zip
8e108e533811febcc51f377ac8604d506663453e41c02dc818517e1ea9a4e8d5 *ffmpeg-v28.2.2-linux-arm64.zip
51ecd03435f56a2ced31b1c9dbf281955ba82a814ca0214a4292bdc711e5a45c *ffmpeg-v28.2.2-linux-armv7l.zip
acc9dc3765f68b7563045e2d0df11bbef6b41be0a1c34bbf9fa778f36eefb42f *ffmpeg-v28.2.2-linux-x64.zip
e71aac5c02f67bd5ba5d650160ff4edb122f697ab6bd8e686eae78426c439733 *ffmpeg-v28.2.2-mas-arm64.zip
3d0bb26cc9b751dad883750972fddec72aa936ecaa0d9bd198ba9b47203410e8 *ffmpeg-v28.2.2-mas-x64.zip
035b24a44f09587092e7db4e28400139901cec6378b3c828ce9f90a60f4f3a9a *ffmpeg-v28.2.2-win32-arm64.zip
38b25e225fd028f1f3f2c551f3b42d62d9e5c4ef388e0b0e019e9c8d93a85b07 *ffmpeg-v28.2.2-win32-ia32.zip
41849e779371dc0c35899341ae658b883ef0124296787ad96b7d5e4d9b69f1b9 *ffmpeg-v28.2.2-win32-x64.zip
8830364f8050164b1736246c30e96ae7ac876bcec5af1bf6344edbd66ed45353 *hunspell_dictionaries.zip
fc417873289fa9c947598ed73a27a28c4b5a07ce90ef998bb56550c4e10a034b *libcxx-objects-v28.2.2-linux-arm64.zip
06e9cdb2e8785a0835f66d34e9518c47ef220e32646e5b43e599339836e9e7b1 *libcxx-objects-v28.2.2-linux-armv7l.zip
ac098a006a8f84d0bb19088b2dec3ee3068b19208c5611194e831b1e5878fb2d *libcxx-objects-v28.2.2-linux-x64.zip
56414a1e809874949c1a1111b8e68b8d4f40d55cb481ad4869e920e47fe1b71b *libcxx_headers.zip
36e46cbed397cc1fe34d8dc477d3a87613acb9936f811535c1300e138e1a7008 *libcxxabi_headers.zip
94b01f4dd6bd56dec39a0be9ac14bb8c9a73db22cb579d6093f4f4c95a4a8896 *mksnapshot-v28.2.2-darwin-arm64.zip
ea768087b4fedf09c38eb093beb744c1a3b5b2a54025a83f1e2301ea03539500 *mksnapshot-v28.2.2-darwin-x64.zip
b9a01ba90abb69877838515d8273532e4aeea6d66c49b8aac3267e26546fc8b3 *mksnapshot-v28.2.2-linux-arm64-x64.zip
60005160b5e9db4a3847c63893f44e18ca86657a3ec97b6c13a90e43291bdb65 *mksnapshot-v28.2.2-linux-armv7l-x64.zip
81bf5ec59e7c33c642b79582fc5b775ec635ce0c52f3f5c30315cb45fdbffd12 *mksnapshot-v28.2.2-linux-x64.zip
7bfbe3cf02713b1a09aa19b75b876e158ed167b0d4345ec3b429061b53fc4b8f *mksnapshot-v28.2.2-mas-arm64.zip
91f7d34a05fa9c7cda4f36a44309f51e7defea2134d5bcc818a3eb4537979870 *mksnapshot-v28.2.2-mas-x64.zip
3f7163a34aae864cd44ebec086d4fab30132924680f20136cf19348811bace50 *mksnapshot-v28.2.2-win32-arm64-x64.zip
ac64fbfb78a1f6f389dac96ad7c655e2ea6fb2289e38a8fd516dbbda6bea42a3 *mksnapshot-v28.2.2-win32-ia32.zip
1bcd03747ce3eee6dd94b0608a0812268dacf77bac5541c581c22b92f700b303 *mksnapshot-v28.2.2-win32-x64.zip
23d9bca1abd1c64d0bd47b9528b8db1b1f28c31e81188ecbed4e9cd18eab3545 *chromedriver-v28.2.5-darwin-arm64.zip
9215cf2196988c5f0e0a01fe1bdd827ab25f3a0895b6e9ff96185fed45be24d9 *chromedriver-v28.2.5-darwin-x64.zip
a27c39a8a9f02a630f4ea1218954e768791e44319ce34e99bb524d45aa956376 *chromedriver-v28.2.5-linux-arm64.zip
658bef49300d3183a34609391f64f3df6c9b07eb55886fa1378249e1170ac68e *chromedriver-v28.2.5-linux-armv7l.zip
14a285843587f251455a3ac69be5bebca7e7c3e934151a69dc8c10c943aaac49 *chromedriver-v28.2.5-linux-x64.zip
173112b71f363f1c434eb4bfe8356a5a4592a0580d8c434c2141f3a04de7695b *chromedriver-v28.2.5-mas-arm64.zip
b72902d8f4d886fef3f945e4a9dd707e18d52201a57e421a555cc166689955a7 *chromedriver-v28.2.5-mas-x64.zip
723cc0db4299d23c6be611b723187c857102749de2f2294bae09047b0d99cfd2 *chromedriver-v28.2.5-win32-arm64.zip
1c549de92e2d784cc2a2618d129e368d74e8da6497df7f5bcabfd2f834981f5d *chromedriver-v28.2.5-win32-ia32.zip
2df3c811c3ed8f22f28e740ffe0abf7c6d0c29d1874efa5290b75575a23d292b *chromedriver-v28.2.5-win32-x64.zip
a6e536d48e399f0961cb5de1e9cb0d3e534c4686fdf6fc79080e66516fdd5b6e *electron-api.json
746c5867227538235cff139e174a7b85fa49230a69350414bed7d1e6ae664cba *electron-v28.2.5-darwin-arm64-dsym-snapshot.zip
b5d00927dead894355c26cc581443735c252a71a53a363f3909f02b39ba1a38f *electron-v28.2.5-darwin-arm64-dsym.zip
6bb1356b72b5d3f8c3d25ef3f42a9ab8574498ab79299af056d8ac93972de72d *electron-v28.2.5-darwin-arm64-symbols.zip
87b17c403d355ba2eee43ee3a955c02069571617ef081b951272c1337ed5a2bb *electron-v28.2.5-darwin-arm64.zip
ff8b7d3073bbc1f26d83a224a79def7cfa652318b98d513603ac3d6e3ea56905 *electron-v28.2.5-darwin-x64-dsym-snapshot.zip
26057699098b6a4173c3f2550ec9a08d3edf4ce3aab5b351ca41c056f8f5ea5f *electron-v28.2.5-darwin-x64-dsym.zip
a467b38526c2c6c677dfe71898eaa8f3c8fccd7675bddfbfce6b095ebd66ea62 *electron-v28.2.5-darwin-x64-symbols.zip
a1ed37b654c48afe0fa8411d09b8644e121fa448d9cafa2ee6a3d2f5d8d36a4d *electron-v28.2.5-darwin-x64.zip
e28c071288258dd55ce0ad6c8582ad1db894a7e981dc2f84534d942289ba0a8b *electron-v28.2.5-linux-arm64-debug.zip
cebd2961e8af1600ca307f530c8b89dafa934cfed356830f4b5c04c048ffc204 *electron-v28.2.5-linux-arm64-symbols.zip
2a24355c27b5d43a424caedfcfe3fc42aeea80e13977fd708537519d6850c372 *electron-v28.2.5-linux-arm64.zip
e28c071288258dd55ce0ad6c8582ad1db894a7e981dc2f84534d942289ba0a8b *electron-v28.2.5-linux-armv7l-debug.zip
de46cbbe2c3eb4cd7d761ec57e85cd2077592b88586e1d9f45606df69a1e5dab *electron-v28.2.5-linux-armv7l-symbols.zip
2707f9fb7b7c6ea8038f8c67054cdfee4fb0bbaec36c20f3ffcca05cd5bbcd6c *electron-v28.2.5-linux-armv7l.zip
2567949ac356f53a5f145e6efaef1e1bc07dabfafa549765ebca54b33b0c298b *electron-v28.2.5-linux-x64-debug.zip
b067d8dfd0345129172628575684c7bc3d739843662aecc33ce4221a96fb7d48 *electron-v28.2.5-linux-x64-symbols.zip
1c1e972fa7daa54e1e54b642b2828495020367df5dcf1e3a4d4fc170980a8d6e *electron-v28.2.5-linux-x64.zip
23068f0cad14769f715147d1778da27a1850cdeea20c1ff7c7b1eb729b4d87b0 *electron-v28.2.5-mas-arm64-dsym-snapshot.zip
04640b64a0132f4606d31b89f8fa063b12be8936f0a2c546e05a992f23d00260 *electron-v28.2.5-mas-arm64-dsym.zip
cf1787c50932ef5c5309a8d80d7647495ddac4c71b6e0feb8ea547299c114ed4 *electron-v28.2.5-mas-arm64-symbols.zip
7f2339a92defc1808714bcdd0561430898e6a9f0cb8788a91bf178c10228fb2a *electron-v28.2.5-mas-arm64.zip
26eca4afd370e422c911c68268cf668ca43c10617c4e9cd53eaa564e698efd50 *electron-v28.2.5-mas-x64-dsym-snapshot.zip
ab928fcd3851651d9ef62fe4b62b48471a6673c603b70ca7f4049e72ad3bc2c2 *electron-v28.2.5-mas-x64-dsym.zip
c64232513b56b5b82f76b637a04f68fcfb7231ea8076d6dc1375aac8dac4a02c *electron-v28.2.5-mas-x64-symbols.zip
8d3a0988e699a42482079676ffba2ac50fbd74f1097d40cee0029ff5bc189144 *electron-v28.2.5-mas-x64.zip
7fcc54dc77dedbf4cb9eeaba1678ebf265649479c478e344c6dff27b1ec63b0b *electron-v28.2.5-win32-arm64-pdb.zip
31a69a6ed4e71b4fb047d19522dc32f9ff0f4b617536dcb7e8b562c9c583adbf *electron-v28.2.5-win32-arm64-symbols.zip
c9f31ae6408aa6936b5d683eda601773789185890375cd097e61e924d4fed77a *electron-v28.2.5-win32-arm64-toolchain-profile.zip
68a6fc3411daaf604da0009df506a655587cb6e5cc19d6a1c47ce0b62fdb4ac6 *electron-v28.2.5-win32-arm64.zip
e74519411a678a9885bfb07acb5df85632f3de67d2fc54ccbd5ebd548edf84c8 *electron-v28.2.5-win32-ia32-pdb.zip
798261cfed077ec4afc965ab1a8e3fe4c976533ba86fa8f17cd69107bd1be3be *electron-v28.2.5-win32-ia32-symbols.zip
c9f31ae6408aa6936b5d683eda601773789185890375cd097e61e924d4fed77a *electron-v28.2.5-win32-ia32-toolchain-profile.zip
41f23f86bf7aa19f67025af7db221b727a38ffc0b1c5661b305be7250ecb7abc *electron-v28.2.5-win32-ia32.zip
ee882c550a2889dd18f58bf0f5c5ee9a1dd0eb6ed29c4f9a359b0db5314d7965 *electron-v28.2.5-win32-x64-pdb.zip
197b7ab2ba961ded3260ae91cf57502c43c2cdaca3fc18b90ec6f4d4e08ba9ac *electron-v28.2.5-win32-x64-symbols.zip
c9f31ae6408aa6936b5d683eda601773789185890375cd097e61e924d4fed77a *electron-v28.2.5-win32-x64-toolchain-profile.zip
9235e039183fd62a6d37f70ae39a0f3a3ddb4e00e1474e6258343d1ad955c995 *electron-v28.2.5-win32-x64.zip
981a6c4d1030af6949405c3818b7332a16a959bd30970f5660e4975ccdf31789 *electron.d.ts
90b6c39e1ba7bbf0bccc3e009bcdbd4d8a57821f669229ab7847fd3d56cc8a66 *ffmpeg-v28.2.5-darwin-arm64.zip
3a736fed82b232624aeba8a33c02e1ce589214db0faf5984e216f8a72cbf713a *ffmpeg-v28.2.5-darwin-x64.zip
1674cdc15b72fb421ae4fd5afb217ef8968fb879db391343519764e2e77edb41 *ffmpeg-v28.2.5-linux-arm64.zip
6db75c7fe794f2a2edf3ff9b0d8ad6157d132c89e36e42580594a26f56658ae5 *ffmpeg-v28.2.5-linux-armv7l.zip
7fed2646cf8cce5c6c1afe4214b2d1ea12c89ed379c4b2cdb06cdadb14ceb4f6 *ffmpeg-v28.2.5-linux-x64.zip
fb3649690c496f4c6f884ddf94a7ff518278f6140c2487dd652256f53ed2e3fa *ffmpeg-v28.2.5-mas-arm64.zip
c1fee5ef3a550a5e9a652e251e6ec3677d156610670b54489b5da0c6b4007179 *ffmpeg-v28.2.5-mas-x64.zip
6745a8816159bc980f1ccb4d28c2f02f70cb5e2faf6423e0924d890ca6353dd8 *ffmpeg-v28.2.5-win32-arm64.zip
e1046c0280a7833227963b43d468973d646bd38ae100a298bb28d6a229e723b2 *ffmpeg-v28.2.5-win32-ia32.zip
a189c9c2317f011735e6d1cb743a78536f45a41f18a16c81d5294ca933d9519a *ffmpeg-v28.2.5-win32-x64.zip
29a594a16cdf3585299e7c585bae2ea007e108a72a96b9e2a95d149e7dc381d6 *hunspell_dictionaries.zip
7c1263edb062c07c2fb589812788f70772629c1798abd1481205cf8cdb999120 *libcxx-objects-v28.2.5-linux-arm64.zip
fc75baa4308a58048fe846b9fe78bba362a34becc5bf32a776f15933f4beaedf *libcxx-objects-v28.2.5-linux-armv7l.zip
1bedd5f8f3c897b0ca3cb263620cc4a8fff7001fcd6318a12c2c4cd9e922b35f *libcxx-objects-v28.2.5-linux-x64.zip
f6f37ee5bf297959c4fdec9bb77637310e6c8a85c7defadcbd660507a9e63728 *libcxx_headers.zip
8849467a7e670355b9cab854d66a09d57e9d91e8881034b07eb71f9d9928eb18 *libcxxabi_headers.zip
ff875bb59ecc8bf01b618d61d4a8378e133ea2c2571653828c9ea08773f5d776 *mksnapshot-v28.2.5-darwin-arm64.zip
30c1f135220d783b08a70bc7992877431e320543837ce0d90102039a945023aa *mksnapshot-v28.2.5-darwin-x64.zip
289e6b5feabe9ea22c10fc0fd0afcde59670506df1af6f1e87dc4dab5cbada29 *mksnapshot-v28.2.5-linux-arm64-x64.zip
e857fe518df1063308514224f82d13ffc24bb661b22d9a8a10a915a69830037b *mksnapshot-v28.2.5-linux-armv7l-x64.zip
a14af21de32fbfdf5d40402b52e7ff4858682cf3958fd6898ea30df331164004 *mksnapshot-v28.2.5-linux-x64.zip
8531b3ed3b47ed0a34a317be2fd03a573ee38e719aeddeeb0a6b3d5c36268ffa *mksnapshot-v28.2.5-mas-arm64.zip
71c978fffa8cb4a3f13842a8eadcb29e0782a648492204ebba08edb23b1fa297 *mksnapshot-v28.2.5-mas-x64.zip
b1ce8db39866860a6853c9a8874224c757a2b3086a451b7b1c30511615457166 *mksnapshot-v28.2.5-win32-arm64-x64.zip
c9a7f82fcd320c52f111d18b7fe6aa9ec739f94a13a7e8e04d22e6706a889c4b *mksnapshot-v28.2.5-win32-ia32.zip
493d0eabacf33c9d51305cf40bf7590901eb4e38d53308a76ae05db5af0a8468 *mksnapshot-v28.2.5-win32-x64.zip
+4
View File
@@ -553,6 +553,10 @@
{
"name": "vs/workbench/contrib/accountEntitlements",
"project": "vscode-workbench"
},
{
"name": "vs/workbench/contrib/authentication",
"project": "vscode-workbench"
}
]
}
@@ -47,6 +47,7 @@
"--vscode-chat-requestBorder",
"--vscode-chat-slashCommandBackground",
"--vscode-chat-slashCommandForeground",
"--vscode-chat-list-background",
"--vscode-checkbox-background",
"--vscode-checkbox-border",
"--vscode-checkbox-foreground",
@@ -789,8 +790,6 @@
"--vscode-hover-maxWidth",
"--vscode-hover-sourceWhiteSpace",
"--vscode-hover-whiteSpace",
"--vscode-inline-chat-cropped",
"--vscode-inline-chat-expanded",
"--vscode-inline-chat-quick-voice-height",
"--vscode-inline-chat-quick-voice-width",
"--vscode-editor-dictation-widget-height",
@@ -827,4 +826,4 @@
"--zoom-factor",
"--test-bar-width"
]
}
}
+4 -4
View File
@@ -6,7 +6,7 @@
"git": {
"name": "chromium",
"repositoryUrl": "https://chromium.googlesource.com/chromium/src",
"commitHash": "01303e423c41f9fefe7ff777744a4c549c0c6d8c"
"commitHash": "14d11e5bb9b5b1cd51f7b19546e74a73cab42084"
}
},
"licenseDetail": [
@@ -40,7 +40,7 @@
"SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
],
"isOnlyProductionDependency": true,
"version": "120.0.6099.276"
"version": "120.0.6099.291"
},
{
"component": {
@@ -528,12 +528,12 @@
"git": {
"name": "electron",
"repositoryUrl": "https://github.com/electron/electron",
"commitHash": "16adf2a26358e3fc2297832e867c942b6df35844"
"commitHash": "6544cec6864be60f577c1fcd41fa646c4d0192aa"
}
},
"isOnlyProductionDependency": true,
"license": "MIT",
"version": "28.2.2"
"version": "28.2.5"
},
{
"component": {
+4 -1
View File
@@ -404,7 +404,10 @@ impl Auth {
let mut keyring_storage = KeyringStorage::default();
#[cfg(target_os = "linux")]
let mut keyring_storage = ThreadKeyringStorage::default();
let mut file_storage = FileStorage(PersistedState::new(self.file_storage_path.clone()));
let mut file_storage = FileStorage(PersistedState::new_with_mode(
self.file_storage_path.clone(),
0o600,
));
let native_storage_result = if std::env::var("VSCODE_CLI_USE_FILE_KEYCHAIN").is_ok()
|| self.file_storage_path.exists()
+30 -4
View File
@@ -6,7 +6,8 @@
extern crate dirs;
use std::{
fs::{create_dir_all, read_to_string, remove_dir_all, write},
fs::{self, create_dir_all, read_to_string, remove_dir_all},
io::Write,
path::{Path, PathBuf},
sync::{Arc, Mutex},
};
@@ -34,6 +35,8 @@ where
{
path: PathBuf,
state: Option<T>,
#[allow(dead_code)]
mode: u32,
}
impl<T> PersistedStateContainer<T>
@@ -58,13 +61,28 @@ where
fn save(&mut self, state: T) -> Result<(), WrappedError> {
let s = serde_json::to_string(&state).unwrap();
self.state = Some(state);
write(&self.path, s).map_err(|e| {
self.write_state(s).map_err(|e| {
wrap(
e,
format!("error saving launcher state into {}", self.path.display()),
)
})
}
fn write_state(&mut self, s: String) -> std::io::Result<()> {
#[cfg(not(windows))]
use std::os::unix::fs::OpenOptionsExt;
let mut f = fs::OpenOptions::new();
f.create(true);
f.write(true);
f.truncate(true);
#[cfg(not(windows))]
f.mode(self.mode);
let mut f = f.open(&self.path)?;
f.write_all(s.as_bytes())
}
}
/// Container that holds some state value that is persisted to disk.
@@ -82,8 +100,17 @@ where
{
/// Creates a new state container that persists to the given path.
pub fn new(path: PathBuf) -> PersistedState<T> {
Self::new_with_mode(path, 0o644)
}
/// Creates a new state container that persists to the given path.
pub fn new_with_mode(path: PathBuf, mode: u32) -> PersistedState<T> {
PersistedState {
container: Arc::new(Mutex::new(PersistedStateContainer { path, state: None })),
container: Arc::new(Mutex::new(PersistedStateContainer {
path,
state: None,
mode,
})),
}
}
@@ -217,5 +244,4 @@ impl LauncherPaths {
pub fn web_server_storage(&self) -> PathBuf {
self.root.join("serve-web")
}
}
+80 -85
View File
@@ -3,13 +3,13 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { window, workspace, Uri, Disposable, Event, EventEmitter, FileDecoration, FileDecorationProvider, ThemeColor } from 'vscode';
import { window, workspace, Uri, Disposable, Event, EventEmitter, FileDecoration, FileDecorationProvider, ThemeColor, l10n } from 'vscode';
import * as path from 'path';
import { Repository, GitResourceGroup } from './repository';
import { Model } from './model';
import { debounce } from './decorators';
import { filterEvent, dispose, anyEvent, fireEvent, PromiseSource, combinedDisposable } from './util';
import { GitErrorCodes, Status } from './api/git';
import { Change, GitErrorCodes, Status } from './api/git';
class GitIgnoreDecorationProvider implements FileDecorationProvider {
@@ -153,100 +153,95 @@ class GitDecorationProvider implements FileDecorationProvider {
}
}
// class GitIncomingChangesFileDecorationProvider implements FileDecorationProvider {
class GitIncomingChangesFileDecorationProvider implements FileDecorationProvider {
// private readonly _onDidChangeDecorations = new EventEmitter<Uri[]>();
// readonly onDidChangeFileDecorations: Event<Uri[]> = this._onDidChangeDecorations.event;
private readonly _onDidChangeDecorations = new EventEmitter<Uri[]>();
readonly onDidChangeFileDecorations: Event<Uri[]> = this._onDidChangeDecorations.event;
// private decorations = new Map<string, FileDecoration>();
// private readonly disposables: Disposable[] = [];
private decorations = new Map<string, FileDecoration>();
private readonly disposables: Disposable[] = [];
// constructor(private readonly repository: Repository) {
// this.disposables.push(window.registerFileDecorationProvider(this));
// repository.historyProvider.onDidChangeCurrentHistoryItemGroup(this.onDidChangeCurrentHistoryItemGroup, this, this.disposables);
// }
constructor(private readonly repository: Repository) {
this.disposables.push(window.registerFileDecorationProvider(this));
repository.historyProvider.onDidChangeCurrentHistoryItemGroup(this.onDidChangeCurrentHistoryItemGroup, this, this.disposables);
}
// private async onDidChangeCurrentHistoryItemGroup(): Promise<void> {
// const newDecorations = new Map<string, FileDecoration>();
// await this.collectIncomingChangesFileDecorations(newDecorations);
// const uris = new Set([...this.decorations.keys()].concat([...newDecorations.keys()]));
private async onDidChangeCurrentHistoryItemGroup(): Promise<void> {
const newDecorations = new Map<string, FileDecoration>();
await this.collectIncomingChangesFileDecorations(newDecorations);
const uris = new Set([...this.decorations.keys()].concat([...newDecorations.keys()]));
// this.decorations = newDecorations;
// this._onDidChangeDecorations.fire([...uris.values()].map(value => Uri.parse(value, true)));
// }
this.decorations = newDecorations;
this._onDidChangeDecorations.fire([...uris.values()].map(value => Uri.parse(value, true)));
}
// private async collectIncomingChangesFileDecorations(bucket: Map<string, FileDecoration>): Promise<void> {
// for (const change of await this.getIncomingChanges()) {
// switch (change.status) {
// case Status.INDEX_ADDED:
// bucket.set(change.uri.toString(), {
// badge: '↓A',
// color: new ThemeColor('gitDecoration.incomingAddedForegroundColor'),
// tooltip: l10n.t('Incoming Changes (added)'),
// });
// break;
// case Status.DELETED:
// bucket.set(change.uri.toString(), {
// badge: '↓D',
// color: new ThemeColor('gitDecoration.incomingDeletedForegroundColor'),
// tooltip: l10n.t('Incoming Changes (deleted)'),
// });
// break;
// case Status.INDEX_RENAMED:
// bucket.set(change.originalUri.toString(), {
// badge: '↓R',
// color: new ThemeColor('gitDecoration.incomingRenamedForegroundColor'),
// tooltip: l10n.t('Incoming Changes (renamed)'),
// });
// break;
// case Status.MODIFIED:
// bucket.set(change.uri.toString(), {
// badge: '↓M',
// color: new ThemeColor('gitDecoration.incomingModifiedForegroundColor'),
// tooltip: l10n.t('Incoming Changes (modified)'),
// });
// break;
// default: {
// bucket.set(change.uri.toString(), {
// badge: '↓~',
// color: new ThemeColor('gitDecoration.incomingModifiedForegroundColor'),
// tooltip: l10n.t('Incoming Changes'),
// });
// break;
// }
// }
// }
// }
private async collectIncomingChangesFileDecorations(bucket: Map<string, FileDecoration>): Promise<void> {
for (const change of await this.getIncomingChanges()) {
switch (change.status) {
case Status.INDEX_ADDED:
bucket.set(change.uri.toString(), {
badge: '↓A',
tooltip: l10n.t('Incoming Changes (added)'),
});
break;
case Status.DELETED:
bucket.set(change.uri.toString(), {
badge: '↓D',
tooltip: l10n.t('Incoming Changes (deleted)'),
});
break;
case Status.INDEX_RENAMED:
bucket.set(change.originalUri.toString(), {
badge: '↓R',
tooltip: l10n.t('Incoming Changes (renamed)'),
});
break;
case Status.MODIFIED:
bucket.set(change.uri.toString(), {
badge: '↓M',
tooltip: l10n.t('Incoming Changes (modified)'),
});
break;
default: {
bucket.set(change.uri.toString(), {
badge: '↓~',
tooltip: l10n.t('Incoming Changes'),
});
break;
}
}
}
}
// private async getIncomingChanges(): Promise<Change[]> {
// try {
// const historyProvider = this.repository.historyProvider;
// const currentHistoryItemGroup = historyProvider.currentHistoryItemGroup;
private async getIncomingChanges(): Promise<Change[]> {
try {
const historyProvider = this.repository.historyProvider;
const currentHistoryItemGroup = historyProvider.currentHistoryItemGroup;
// if (!currentHistoryItemGroup?.base) {
// return [];
// }
if (!currentHistoryItemGroup?.base) {
return [];
}
// const ancestor = await historyProvider.resolveHistoryItemGroupCommonAncestor(currentHistoryItemGroup.id, currentHistoryItemGroup.base.id);
// if (!ancestor) {
// return [];
// }
const ancestor = await historyProvider.resolveHistoryItemGroupCommonAncestor(currentHistoryItemGroup.id, currentHistoryItemGroup.base.id);
if (!ancestor) {
return [];
}
// const changes = await this.repository.diffBetween(ancestor.id, currentHistoryItemGroup.base.id);
// return changes;
// } catch (err) {
// return [];
// }
// }
const changes = await this.repository.diffBetween(ancestor.id, currentHistoryItemGroup.base.id);
return changes;
} catch (err) {
return [];
}
}
// provideFileDecoration(uri: Uri): FileDecoration | undefined {
// return this.decorations.get(uri.toString());
// }
provideFileDecoration(uri: Uri): FileDecoration | undefined {
return this.decorations.get(uri.toString());
}
// dispose(): void {
// dispose(this.disposables);
// }
// }
dispose(): void {
dispose(this.disposables);
}
}
export class GitDecorations {
@@ -287,7 +282,7 @@ export class GitDecorations {
private onDidOpenRepository(repository: Repository): void {
const providers = combinedDisposable([
new GitDecorationProvider(repository),
// new GitIncomingChangesFileDecorationProvider(repository)
new GitIncomingChangesFileDecorationProvider(repository)
]);
this.providers.set(repository, providers);
@@ -8,3 +8,7 @@ export const TIMED_OUT_ERROR = 'Timed out';
// These error messages are internal and should not be shown to the user in any way.
export const USER_CANCELLATION_ERROR = 'User Cancelled';
export const NETWORK_ERROR = 'network error';
// This is the error message that we throw if the login was cancelled for any reason. Extensions
// calling `getSession` can handle this error to know that the user cancelled the login.
export const CANCELLATION_ERROR = 'Cancelled';
+11 -2
View File
@@ -68,6 +68,7 @@ interface IFlowTriggerOptions {
callbackUri: Uri;
uriHandler: UriEventHandler;
enterpriseUri?: Uri;
existingLogin?: string;
}
interface IFlow {
@@ -149,7 +150,8 @@ const allFlows: IFlow[] = [
nonce,
callbackUri,
uriHandler,
enterpriseUri
enterpriseUri,
existingLogin
}: IFlowTriggerOptions): Promise<string> {
logger.info(`Trying without local server... (${scopes})`);
return await window.withProgress<string>({
@@ -169,6 +171,9 @@ const allFlows: IFlow[] = [
['scope', scopes],
['state', encodeURIComponent(callbackUri.toString(true))]
]);
if (existingLogin) {
searchParams.append('login', existingLogin);
}
// The extra toString, parse is apparently needed for env.openExternal
// to open the correct URL.
@@ -215,7 +220,8 @@ const allFlows: IFlow[] = [
baseUri,
redirectUri,
logger,
enterpriseUri
enterpriseUri,
existingLogin
}: IFlowTriggerOptions): Promise<string> {
logger.info(`Trying with local server... (${scopes})`);
return await window.withProgress<string>({
@@ -232,6 +238,9 @@ const allFlows: IFlow[] = [
['redirect_uri', redirectUri.toString(true)],
['scope', scopes],
]);
if (existingLogin) {
searchParams.append('login', existingLogin);
}
const loginUrl = baseUri.with({
path: '/login/oauth/authorize',
+34 -3
View File
@@ -11,7 +11,7 @@ import { PromiseAdapter, arrayEquals, promiseFromEvent } from './common/utils';
import { ExperimentationTelemetry } from './common/experimentationService';
import { Log } from './common/logger';
import { crypto } from './node/crypto';
import { TIMED_OUT_ERROR, USER_CANCELLATION_ERROR } from './common/errors';
import { CANCELLATION_ERROR, TIMED_OUT_ERROR, USER_CANCELLATION_ERROR } from './common/errors';
interface SessionData {
id: string;
@@ -296,13 +296,44 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid
scopes: JSON.stringify(scopes),
});
const sessions = await this._sessionsPromise;
const scopeString = sortedScopes.join(' ');
const token = await this._githubServer.login(scopeString);
const existingLogin = sessions[0]?.account.label;
const token = await this._githubServer.login(scopeString, existingLogin);
const session = await this.tokenToSession(token, scopes);
this.afterSessionLoad(session);
const sessions = await this._sessionsPromise;
if (sessions.some(s => s.id !== session.id)) {
const otherAccountsIndexes = new Array<number>();
const otherAccountsLabels = new Set<string>();
for (let i = 0; i < sessions.length; i++) {
if (sessions[i].id !== session.id) {
otherAccountsIndexes.push(i);
otherAccountsLabels.add(sessions[i].account.label);
}
}
const proceed = vscode.l10n.t("Continue");
const labelstr = [...otherAccountsLabels].join(', ');
const result = await vscode.window.showInformationMessage(
vscode.l10n.t({
message: "You are logged into another account already ({0}).\n\nDo you want to log out of that account and log in to '{1}' instead?",
comment: ['{0} is a comma-separated list of account names. {1} is the account name to log into.'],
args: [labelstr, session.account.label]
}),
{ modal: true },
proceed
);
if (result !== proceed) {
throw new Error(CANCELLATION_ERROR);
}
// Remove other accounts
for (const i of otherAccountsIndexes) {
sessions.splice(i, 1);
}
}
const sessionIndex = sessions.findIndex(s => s.id === session.id || arrayEquals([...s.scopes].sort(), sortedScopes));
if (sessionIndex > -1) {
sessions.splice(sessionIndex, 1, session);
@@ -11,19 +11,15 @@ import { isSupportedClient, isSupportedTarget } from './common/env';
import { crypto } from './node/crypto';
import { fetching } from './node/fetch';
import { ExtensionHost, GitHubTarget, getFlows } from './flows';
import { NETWORK_ERROR, USER_CANCELLATION_ERROR } from './common/errors';
import { CANCELLATION_ERROR, NETWORK_ERROR, USER_CANCELLATION_ERROR } from './common/errors';
import { Config } from './config';
import { base64Encode } from './node/buffer';
// This is the error message that we throw if the login was cancelled for any reason. Extensions
// calling `getSession` can handle this error to know that the user cancelled the login.
const CANCELLATION_ERROR = 'Cancelled';
const REDIRECT_URL_STABLE = 'https://vscode.dev/redirect';
const REDIRECT_URL_INSIDERS = 'https://insiders.vscode.dev/redirect';
export interface IGitHubServer {
login(scopes: string): Promise<string>;
login(scopes: string, existingLogin?: string): Promise<string>;
logout(session: vscode.AuthenticationSession): Promise<void>;
getUserInfo(token: string): Promise<{ id: string; accountName: string }>;
sendAdditionalTelemetryInfo(session: vscode.AuthenticationSession): Promise<void>;
@@ -91,7 +87,7 @@ export class GitHubServer implements IGitHubServer {
return this._isNoCorsEnvironment;
}
public async login(scopes: string): Promise<string> {
public async login(scopes: string, existingLogin?: string): Promise<string> {
this._logger.info(`Logging in for the following scopes: ${scopes}`);
// Used for showing a friendlier message to the user when the explicitly cancel a flow.
@@ -143,6 +139,7 @@ export class GitHubServer implements IGitHubServer {
uriHandler: this._uriHandler,
enterpriseUri: this._ghesUri,
redirectUri: vscode.Uri.parse(await this.getRedirectEndpoint()),
existingLogin
});
} catch (e) {
userCancelled = this.processLoginError(e);
@@ -3,9 +3,9 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable, ExtensionContext, Uri, l10n } from 'vscode';
import { Disposable, ExtensionContext, Uri, l10n, window } from 'vscode';
import { LanguageClientOptions } from 'vscode-languageclient';
import { startClient, LanguageClientConstructor, SchemaRequestService, AsyncDisposable } from '../jsonClient';
import { startClient, LanguageClientConstructor, SchemaRequestService, AsyncDisposable, languageServerDescription } from '../jsonClient';
import { LanguageClient } from 'vscode-languageclient/browser';
declare const Worker: {
@@ -43,7 +43,10 @@ export async function activate(context: ExtensionContext) {
}
};
client = await startClient(context, newLanguageClient, { schemaRequests, timer });
const logOutputChannel = window.createOutputChannel(languageServerDescription, { log: true });
context.subscriptions.push(logOutputChannel);
client = await startClient(context, newLanguageClient, { schemaRequests, timer, logOutputChannel });
} catch (e) {
console.log(e);
@@ -6,7 +6,7 @@
export type JSONLanguageStatus = { schemas: string[] };
import {
workspace, window, languages, commands, OutputChannel, ExtensionContext, extensions, Uri, ColorInformation,
workspace, window, languages, commands, LogOutputChannel, ExtensionContext, extensions, Uri, ColorInformation,
Diagnostic, StatusBarAlignment, TextEditor, TextDocument, FormattingOptions, CancellationToken, FoldingRange,
ProviderResult, TextEdit, Range, Position, Disposable, CompletionItem, CompletionList, CompletionContext, Hover, MarkdownString, FoldingContext, DocumentSymbol, SymbolInformation, l10n
} from 'vscode';
@@ -130,6 +130,7 @@ export interface Runtime {
readonly timer: {
setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): Disposable;
};
logOutputChannel: LogOutputChannel;
}
export interface SchemaRequestService {
@@ -150,12 +151,10 @@ export interface AsyncDisposable {
}
export async function startClient(context: ExtensionContext, newLanguageClient: LanguageClientConstructor, runtime: Runtime): Promise<AsyncDisposable> {
const outputChannel = window.createOutputChannel(languageServerDescription);
const languageParticipants = getLanguageParticipants();
context.subscriptions.push(languageParticipants);
let client: Disposable | undefined = await startClientWithParticipants(context, languageParticipants, newLanguageClient, outputChannel, runtime);
let client: Disposable | undefined = await startClientWithParticipants(context, languageParticipants, newLanguageClient, runtime);
let restartTrigger: Disposable | undefined;
languageParticipants.onDidChange(() => {
@@ -164,12 +163,12 @@ export async function startClient(context: ExtensionContext, newLanguageClient:
}
restartTrigger = runtime.timer.setTimeout(async () => {
if (client) {
outputChannel.appendLine('Extensions have changed, restarting JSON server...');
outputChannel.appendLine('');
runtime.logOutputChannel.info('Extensions have changed, restarting JSON server...');
runtime.logOutputChannel.info('');
const oldClient = client;
client = undefined;
await oldClient.dispose();
client = await startClientWithParticipants(context, languageParticipants, newLanguageClient, outputChannel, runtime);
client = await startClientWithParticipants(context, languageParticipants, newLanguageClient, runtime);
}
}, 2000);
});
@@ -178,12 +177,11 @@ export async function startClient(context: ExtensionContext, newLanguageClient:
dispose: async () => {
restartTrigger?.dispose();
await client?.dispose();
outputChannel.dispose();
}
};
}
async function startClientWithParticipants(context: ExtensionContext, languageParticipants: LanguageParticipants, newLanguageClient: LanguageClientConstructor, outputChannel: OutputChannel, runtime: Runtime): Promise<AsyncDisposable> {
async function startClientWithParticipants(context: ExtensionContext, languageParticipants: LanguageParticipants, newLanguageClient: LanguageClientConstructor, runtime: Runtime): Promise<AsyncDisposable> {
const toDispose: Disposable[] = [];
@@ -348,7 +346,7 @@ async function startClientWithParticipants(context: ExtensionContext, languagePa
}
};
clientOptions.outputChannel = outputChannel;
clientOptions.outputChannel = runtime.logOutputChannel;
// Create the language client and start the client.
const client = newLanguageClient('json', languageServerDescription, clientOptions);
client.registerProposedFeatures();
@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable, ExtensionContext, OutputChannel, window, workspace, l10n, env } from 'vscode';
import { Disposable, ExtensionContext, LogOutputChannel, window, l10n, env, LogLevel } from 'vscode';
import { startClient, LanguageClientConstructor, SchemaRequestService, languageServerDescription, AsyncDisposable } from '../jsonClient';
import { ServerOptions, TransportKind, LanguageClientOptions, LanguageClient } from 'vscode-languageclient/node';
@@ -14,15 +14,16 @@ import { xhr, XHRResponse, getErrorStatusDescription, Headers } from 'request-li
import TelemetryReporter from '@vscode/extension-telemetry';
import { JSONSchemaCache } from './schemaCache';
let telemetry: TelemetryReporter | undefined;
let client: AsyncDisposable | undefined;
// this method is called when vs code is activated
export async function activate(context: ExtensionContext) {
const clientPackageJSON = await getPackageInfo(context);
telemetry = new TelemetryReporter(clientPackageJSON.aiKey);
const telemetry = new TelemetryReporter(clientPackageJSON.aiKey);
context.subscriptions.push(telemetry);
const outputChannel = window.createOutputChannel(languageServerDescription);
const logOutputChannel = window.createOutputChannel(languageServerDescription, { log: true });
context.subscriptions.push(logOutputChannel);
const serverMain = `./server/${clientPackageJSON.main.indexOf('/dist/') !== -1 ? 'dist' : 'out'}/node/jsonServerMain`;
const serverModule = context.asAbsolutePath(serverMain);
@@ -38,11 +39,8 @@ export async function activate(context: ExtensionContext) {
};
const newLanguageClient: LanguageClientConstructor = (id: string, name: string, clientOptions: LanguageClientOptions) => {
clientOptions.outputChannel = outputChannel;
return new LanguageClient(id, name, serverOptions, clientOptions);
};
const log = getLog(outputChannel);
context.subscriptions.push(log);
const timer = {
setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): Disposable {
@@ -54,9 +52,9 @@ export async function activate(context: ExtensionContext) {
// pass the location of the localization bundle to the server
process.env['VSCODE_L10N_BUNDLE_LOCATION'] = l10n.uri?.toString() ?? '';
const schemaRequests = await getSchemaRequestService(context, log);
const schemaRequests = await getSchemaRequestService(context, logOutputChannel);
client = await startClient(context, newLanguageClient, { schemaRequests, telemetry, timer });
client = await startClient(context, newLanguageClient, { schemaRequests, telemetry, timer, logOutputChannel });
}
export async function deactivate(): Promise<any> {
@@ -64,7 +62,6 @@ export async function deactivate(): Promise<any> {
await client.dispose();
client = undefined;
}
telemetry?.dispose();
}
interface IPackageInfo {
@@ -84,36 +81,9 @@ async function getPackageInfo(context: ExtensionContext): Promise<IPackageInfo>
}
}
interface Log {
trace(message: string): void;
isTrace(): boolean;
dispose(): void;
}
const traceSetting = 'json.trace.server';
function getLog(outputChannel: OutputChannel): Log {
let trace = workspace.getConfiguration().get(traceSetting) === 'verbose';
const configListener = workspace.onDidChangeConfiguration(e => {
if (e.affectsConfiguration(traceSetting)) {
trace = workspace.getConfiguration().get(traceSetting) === 'verbose';
}
});
return {
trace(message: string) {
if (trace) {
outputChannel.appendLine(message);
}
},
isTrace() {
return trace;
},
dispose: () => configListener.dispose()
};
}
const retryTimeoutInHours = 2 * 24; // 2 days
async function getSchemaRequestService(context: ExtensionContext, log: Log): Promise<SchemaRequestService> {
async function getSchemaRequestService(context: ExtensionContext, log: LogOutputChannel): Promise<SchemaRequestService> {
let cache: JSONSchemaCache | undefined = undefined;
const globalStorage = context.globalStorageUri;
@@ -191,7 +161,7 @@ async function getSchemaRequestService(context: ExtensionContext, log: Log): Pro
if (cache && /^https?:\/\/json\.schemastore\.org\//.test(uri)) {
const content = await cache.getSchemaIfUpdatedSince(uri, retryTimeoutInHours);
if (content) {
if (log.isTrace()) {
if (log.logLevel === LogLevel.Trace) {
log.trace(`[json schema cache] Schema ${uri} from cache without request (last accessed ${cache.getLastUpdatedInHours(uri)} hours ago)`);
}
@@ -725,6 +725,13 @@ export default class BufferSyncSupport extends Disposable {
orderedFileSet.set(buffer.resource, undefined);
}
for (const { resource } of orderedFileSet.entries()) {
const buffer = this.syncedBuffers.get(resource);
if (buffer && !this.shouldValidate(buffer)) {
orderedFileSet.delete(resource);
}
}
if (orderedFileSet.size) {
const getErr = this.pendingGetErr = GetErrRequest.executeGetErrRequest(this.client, orderedFileSet, () => {
if (this.pendingGetErr === getErr) {
+13
View File
@@ -64,6 +64,19 @@
},
"icon": "media/icon.png",
"contributes": {
"chatParticipants": [
{
"name": "participant",
"description": "test",
"isDefault": true,
"commands": [
{
"name": "hello",
"description": "Hello"
}
]
}
],
"configuration": {
"type": "object",
"title": "Test Config",
@@ -5,7 +5,7 @@
import * as assert from 'assert';
import 'mocha';
import { CancellationToken, ChatContext, ChatRequest, ChatResult, ChatVariableLevel, Disposable, Event, EventEmitter, InteractiveSession, ProviderResult, chat, interactive } from 'vscode';
import { commands, CancellationToken, ChatContext, ChatRequest, ChatResult, ChatVariableLevel, Disposable, Event, EventEmitter, InteractiveSession, ProviderResult, chat, interactive } from 'vscode';
import { DeferredPromise, assertNoRpc, closeAllEditors, disposeAll } from '../utils';
suite('chat', () => {
@@ -45,18 +45,13 @@ suite('chat', () => {
return null;
});
participant.isDefault = true;
participant.commandProvider = {
provideCommands: (_token) => {
return [{ name: 'hello', description: 'Hello' }];
}
};
disposables.push(participant);
return emitter.event;
}
test('participant and slash command', async () => {
const onRequest = setupParticipant();
interactive.sendInteractiveRequestToProvider('provider', { message: '@participant /hello friend' });
commands.executeCommand('workbench.action.chat.open', { query: '@participant /hello friend' });
let i = 0;
onRequest(request => {
@@ -64,7 +59,7 @@ suite('chat', () => {
assert.deepStrictEqual(request.request.command, 'hello');
assert.strictEqual(request.request.prompt, 'friend');
i++;
interactive.sendInteractiveRequestToProvider('provider', { message: '@participant /hello friend' });
commands.executeCommand('workbench.action.chat.open', { query: '@participant /hello friend' });
} else {
assert.strictEqual(request.context.history.length, 1);
assert.strictEqual(request.context.history[0].participant.name, 'participant');
@@ -81,7 +76,7 @@ suite('chat', () => {
}));
const deferred = getDeferredForRequest();
interactive.sendInteractiveRequestToProvider('provider', { message: '@participant hi #myVar' });
commands.executeCommand('workbench.action.chat.open', { query: '@participant hi #myVar' });
const request = await deferred.p;
assert.strictEqual(request.prompt, 'hi #myVar');
assert.strictEqual(request.variables[0].values[0].value, 'myValue');
@@ -102,20 +97,15 @@ suite('chat', () => {
return { metadata: { key: 'value' } };
});
participant.isDefault = true;
participant.commandProvider = {
provideCommands: (_token) => {
return [{ name: 'hello', description: 'Hello' }];
}
};
participant.followupProvider = {
provideFollowups(result, _token) {
provideFollowups(result, _context, _token) {
deferred.complete(result);
return [];
},
};
disposables.push(participant);
interactive.sendInteractiveRequestToProvider('provider', { message: '@participant /hello friend' });
commands.executeCommand('workbench.action.chat.open', { query: '@participant /hello friend' });
const result = await deferred.p;
assert.deepStrictEqual(result.metadata, { key: 'value' });
});
+10 -10
View File
@@ -1,7 +1,7 @@
{
"name": "code-oss-dev",
"version": "1.88.0",
"distro": "a5b6daf94540aab9d17335c2c2533e629d750123",
"distro": "4623345215aabf2cde23e144a9d4d3ef7803360e",
"author": {
"name": "Microsoft Corporation"
},
@@ -80,14 +80,14 @@
"@vscode/windows-mutex": "^0.5.0",
"@vscode/windows-process-tree": "^0.6.0",
"@vscode/windows-registry": "^1.1.0",
"@xterm/addon-canvas": "0.6.0-beta.31",
"@xterm/addon-image": "0.7.0-beta.29",
"@xterm/addon-search": "0.14.0-beta.31",
"@xterm/addon-serialize": "0.12.0-beta.31",
"@xterm/addon-unicode11": "0.7.0-beta.31",
"@xterm/addon-webgl": "0.17.0-beta.31",
"@xterm/headless": "5.4.0-beta.31",
"@xterm/xterm": "5.4.0-beta.31",
"@xterm/addon-canvas": "0.7.0-beta.3",
"@xterm/addon-image": "0.8.0-beta.3",
"@xterm/addon-search": "0.15.0-beta.3",
"@xterm/addon-serialize": "0.13.0-beta.3",
"@xterm/addon-unicode11": "0.8.0-beta.3",
"@xterm/addon-webgl": "0.18.0-beta.3",
"@xterm/headless": "5.5.0-beta.3",
"@xterm/xterm": "5.5.0-beta.3",
"graceful-fs": "4.2.11",
"http-proxy-agent": "^7.0.0",
"https-proxy-agent": "^7.0.2",
@@ -149,7 +149,7 @@
"cssnano": "^6.0.3",
"debounce": "^1.0.0",
"deemon": "^1.8.0",
"electron": "28.2.2",
"electron": "28.2.5",
"eslint": "8.36.0",
"eslint-plugin-header": "3.1.1",
"eslint-plugin-jsdoc": "^46.5.0",
+8 -8
View File
@@ -13,14 +13,14 @@
"@vscode/vscode-languagedetection": "1.0.21",
"@vscode/windows-process-tree": "^0.6.0",
"@vscode/windows-registry": "^1.1.0",
"@xterm/addon-canvas": "0.6.0-beta.31",
"@xterm/addon-image": "0.7.0-beta.29",
"@xterm/addon-search": "0.14.0-beta.31",
"@xterm/addon-serialize": "0.12.0-beta.31",
"@xterm/addon-unicode11": "0.7.0-beta.31",
"@xterm/addon-webgl": "0.17.0-beta.31",
"@xterm/headless": "5.4.0-beta.31",
"@xterm/xterm": "5.4.0-beta.31",
"@xterm/addon-canvas": "0.7.0-beta.3",
"@xterm/addon-image": "0.8.0-beta.3",
"@xterm/addon-search": "0.15.0-beta.3",
"@xterm/addon-serialize": "0.13.0-beta.3",
"@xterm/addon-unicode11": "0.8.0-beta.3",
"@xterm/addon-webgl": "0.18.0-beta.3",
"@xterm/headless": "5.5.0-beta.3",
"@xterm/xterm": "5.5.0-beta.3",
"cookie": "^0.4.0",
"graceful-fs": "4.2.11",
"http-proxy-agent": "^7.0.0",
+7 -7
View File
@@ -7,13 +7,13 @@
"@microsoft/1ds-post-js": "^3.2.13",
"@vscode/iconv-lite-umd": "0.7.0",
"@vscode/vscode-languagedetection": "1.0.21",
"@xterm/addon-canvas": "0.6.0-beta.31",
"@xterm/addon-image": "0.7.0-beta.29",
"@xterm/addon-search": "0.14.0-beta.31",
"@xterm/addon-serialize": "0.12.0-beta.31",
"@xterm/addon-unicode11": "0.7.0-beta.31",
"@xterm/addon-webgl": "0.17.0-beta.31",
"@xterm/xterm": "5.4.0-beta.31",
"@xterm/addon-canvas": "0.7.0-beta.3",
"@xterm/addon-image": "0.8.0-beta.3",
"@xterm/addon-search": "0.15.0-beta.3",
"@xterm/addon-serialize": "0.13.0-beta.3",
"@xterm/addon-unicode11": "0.8.0-beta.3",
"@xterm/addon-webgl": "0.18.0-beta.3",
"@xterm/xterm": "5.5.0-beta.3",
"jschardet": "3.0.0",
"tas-client-umd": "0.1.8",
"vscode-oniguruma": "1.7.0",
+28 -28
View File
@@ -48,40 +48,40 @@
resolved "https://registry.yarnpkg.com/@vscode/vscode-languagedetection/-/vscode-languagedetection-1.0.21.tgz#89b48f293f6aa3341bb888c1118d16ff13b032d3"
integrity sha512-zSUH9HYCw5qsCtd7b31yqkpaCU6jhtkKLkvOOA8yTrIRfBSOFb8PPhgmMicD7B/m+t4PwOJXzU1XDtrM9Fd3/g==
"@xterm/addon-canvas@0.6.0-beta.31":
version "0.6.0-beta.31"
resolved "https://registry.yarnpkg.com/@xterm/addon-canvas/-/addon-canvas-0.6.0-beta.31.tgz#17cc7d9968ede411fb23db11813b495435c068a0"
integrity sha512-jm/7FWZOgnAGG7MXjr0W4SnuIzsag+oVpyf6wAD9UlCgq5HBuk/3kJ5mYGiGR7CpdTxqXmzyBk3OhQe8npZ1aQ==
"@xterm/addon-canvas@0.7.0-beta.3":
version "0.7.0-beta.3"
resolved "https://registry.yarnpkg.com/@xterm/addon-canvas/-/addon-canvas-0.7.0-beta.3.tgz#271054deee3828b38d4ac8abfa5802c19295aaeb"
integrity sha512-pvq1h45Xhi0wAHGlXmy1tK4x/kxDmkSRtHwoCu81fplHgxa2vgIrGSwSKzRWhD3ro6ccDQhFDhpdJUDNVP4Y+w==
"@xterm/addon-image@0.7.0-beta.29":
version "0.7.0-beta.29"
resolved "https://registry.yarnpkg.com/@xterm/addon-image/-/addon-image-0.7.0-beta.29.tgz#276b56007c9009e7a59605dc3809c280e7d637ed"
integrity sha512-Z5JCuhl0AcwQA+DE/kQMeSSHZbfwJVLUUBodDeujVItQrcpc9vA8mxf/qIwS3XTA/tPbFihfc/CE9zL7OFdbaw==
"@xterm/addon-image@0.8.0-beta.3":
version "0.8.0-beta.3"
resolved "https://registry.yarnpkg.com/@xterm/addon-image/-/addon-image-0.8.0-beta.3.tgz#1fe6f872a88f6cf04596f5e8a0166da94c429ef6"
integrity sha512-5FZRF4avxTedw5f41RQ9Z7A31H0YB33tjV5aQAzSlOiwcQr5m5Q8YYWHdj/vdjfW/dbECJJlckLY3VwyNMPQuQ==
"@xterm/addon-search@0.14.0-beta.31":
version "0.14.0-beta.31"
resolved "https://registry.yarnpkg.com/@xterm/addon-search/-/addon-search-0.14.0-beta.31.tgz#e6edcd257f5a66bca7e92e62684132b604fb817d"
integrity sha512-SS4CdgciLT98Uc4Dq0IjJegHcGIjGaASTcMtVkNBx9dOat9xt6lCXmtgUUj5w0KlB8nUfKrcy5T6fHgzrOzvrw==
"@xterm/addon-search@0.15.0-beta.3":
version "0.15.0-beta.3"
resolved "https://registry.yarnpkg.com/@xterm/addon-search/-/addon-search-0.15.0-beta.3.tgz#f7701e0374805e1abfce167e696f9321020e198a"
integrity sha512-2otjNh5hkSvMvwZ6m9uEijhAmW+XE/xfDawteLLoM0GV8Pmt8C1EUa3/aZF7axKv7U1WmYy0Oh+TJ5mQwcBHHA==
"@xterm/addon-serialize@0.12.0-beta.31":
version "0.12.0-beta.31"
resolved "https://registry.yarnpkg.com/@xterm/addon-serialize/-/addon-serialize-0.12.0-beta.31.tgz#daec32b94d45afcd662351d7689cb1b19eb24db7"
integrity sha512-MZ24pw33qOJrHdA6tlvwE4dSSpmIp/H9ZKtbiWZvuxVsY/hfYYPOluBQiCsOiYT7bZ8gQub2OOBX3jyMoZVxnQ==
"@xterm/addon-serialize@0.13.0-beta.3":
version "0.13.0-beta.3"
resolved "https://registry.yarnpkg.com/@xterm/addon-serialize/-/addon-serialize-0.13.0-beta.3.tgz#665adc0830a3c2cede399c660121650924907da9"
integrity sha512-88putapu36cKM0DBZpJ0k4Hk09JVF1B3kKtj9utXlOWNsriX5WeUH/yEWr+T8iqsnYcUsROOuC12rtoW92+uvg==
"@xterm/addon-unicode11@0.7.0-beta.31":
version "0.7.0-beta.31"
resolved "https://registry.yarnpkg.com/@xterm/addon-unicode11/-/addon-unicode11-0.7.0-beta.31.tgz#e1a6e965638ee6cb59b8b0777387037c42582d4b"
integrity sha512-wrZLt2s6Yjmpe4nh0Sp6DKji0EoHod7V6ABfWBf8krjmEGSleE+GSb+ZwDOMsNzLJLmxoq1e6glHcVixG1z7WQ==
"@xterm/addon-unicode11@0.8.0-beta.3":
version "0.8.0-beta.3"
resolved "https://registry.yarnpkg.com/@xterm/addon-unicode11/-/addon-unicode11-0.8.0-beta.3.tgz#083092a40a7cad8ed03a41f67ad21f33048b6398"
integrity sha512-zPg5ItGawDTSayuxxIxGcLeNYPEq8bpY999/cVjckt02KxD2TJ097URWAnS0Hr7OYO9OxR4NPOOjbSNSw29OFg==
"@xterm/addon-webgl@0.17.0-beta.31":
version "0.17.0-beta.31"
resolved "https://registry.yarnpkg.com/@xterm/addon-webgl/-/addon-webgl-0.17.0-beta.31.tgz#15dfea4583ff9b65f1a442e5cdba1d1638adb05f"
integrity sha512-wqbBDDppwQ4R8o0YgnyFL8Pai2mVZqHb3E097vkFLB5Fw2hNx2dys3MgiXriSGXaUABKM3usVdZyouL6QgWdxQ==
"@xterm/addon-webgl@0.18.0-beta.3":
version "0.18.0-beta.3"
resolved "https://registry.yarnpkg.com/@xterm/addon-webgl/-/addon-webgl-0.18.0-beta.3.tgz#c592be94c2230a03cb0c0501a4aafbbeac49e691"
integrity sha512-M36K2QhZl/HKVNRXftxJbn7YMaqWVqWwgW1lxyHefn2uZx1+jfSXM8EQo+PpntPuGJaUWZ3zoLv8TGz9rNJEFg==
"@xterm/xterm@5.4.0-beta.31":
version "5.4.0-beta.31"
resolved "https://registry.yarnpkg.com/@xterm/xterm/-/xterm-5.4.0-beta.31.tgz#ff0bb3af9b00b0dfc73e84075f4218440c9886be"
integrity sha512-EpCtaYqMhJSyZrGY2sJVZeRCIRrANKtv1GGTj+IQPvk6hTiJHGrFHLM0tZ0dj0l3z65tLoOdj6EzJnjzX3Pqjw==
"@xterm/xterm@5.5.0-beta.3":
version "5.5.0-beta.3"
resolved "https://registry.yarnpkg.com/@xterm/xterm/-/xterm-5.5.0-beta.3.tgz#40b9017cbac37f7f55f227a10e37b3519ed3f39f"
integrity sha512-ukbnGJxJTFVCI6voThi04ePPtJ3NLEQSTRDskxTwgjIxfUw1s/LwGhAG2SZnQcgqtDLXjIXAslrgVRiVBQ3yXg==
jschardet@3.0.0:
version "3.0.0"
+32 -32
View File
@@ -114,45 +114,45 @@
resolved "https://registry.yarnpkg.com/@vscode/windows-registry/-/windows-registry-1.1.0.tgz#03dace7c29c46f658588b9885b9580e453ad21f9"
integrity sha512-5AZzuWJpGscyiMOed0IuyEwt6iKmV5Us7zuwCDCFYMIq7tsvooO9BUiciywsvuthGz6UG4LSpeDeCxvgMVhnIw==
"@xterm/addon-canvas@0.6.0-beta.31":
version "0.6.0-beta.31"
resolved "https://registry.yarnpkg.com/@xterm/addon-canvas/-/addon-canvas-0.6.0-beta.31.tgz#17cc7d9968ede411fb23db11813b495435c068a0"
integrity sha512-jm/7FWZOgnAGG7MXjr0W4SnuIzsag+oVpyf6wAD9UlCgq5HBuk/3kJ5mYGiGR7CpdTxqXmzyBk3OhQe8npZ1aQ==
"@xterm/addon-canvas@0.7.0-beta.3":
version "0.7.0-beta.3"
resolved "https://registry.yarnpkg.com/@xterm/addon-canvas/-/addon-canvas-0.7.0-beta.3.tgz#271054deee3828b38d4ac8abfa5802c19295aaeb"
integrity sha512-pvq1h45Xhi0wAHGlXmy1tK4x/kxDmkSRtHwoCu81fplHgxa2vgIrGSwSKzRWhD3ro6ccDQhFDhpdJUDNVP4Y+w==
"@xterm/addon-image@0.7.0-beta.29":
version "0.7.0-beta.29"
resolved "https://registry.yarnpkg.com/@xterm/addon-image/-/addon-image-0.7.0-beta.29.tgz#276b56007c9009e7a59605dc3809c280e7d637ed"
integrity sha512-Z5JCuhl0AcwQA+DE/kQMeSSHZbfwJVLUUBodDeujVItQrcpc9vA8mxf/qIwS3XTA/tPbFihfc/CE9zL7OFdbaw==
"@xterm/addon-image@0.8.0-beta.3":
version "0.8.0-beta.3"
resolved "https://registry.yarnpkg.com/@xterm/addon-image/-/addon-image-0.8.0-beta.3.tgz#1fe6f872a88f6cf04596f5e8a0166da94c429ef6"
integrity sha512-5FZRF4avxTedw5f41RQ9Z7A31H0YB33tjV5aQAzSlOiwcQr5m5Q8YYWHdj/vdjfW/dbECJJlckLY3VwyNMPQuQ==
"@xterm/addon-search@0.14.0-beta.31":
version "0.14.0-beta.31"
resolved "https://registry.yarnpkg.com/@xterm/addon-search/-/addon-search-0.14.0-beta.31.tgz#e6edcd257f5a66bca7e92e62684132b604fb817d"
integrity sha512-SS4CdgciLT98Uc4Dq0IjJegHcGIjGaASTcMtVkNBx9dOat9xt6lCXmtgUUj5w0KlB8nUfKrcy5T6fHgzrOzvrw==
"@xterm/addon-search@0.15.0-beta.3":
version "0.15.0-beta.3"
resolved "https://registry.yarnpkg.com/@xterm/addon-search/-/addon-search-0.15.0-beta.3.tgz#f7701e0374805e1abfce167e696f9321020e198a"
integrity sha512-2otjNh5hkSvMvwZ6m9uEijhAmW+XE/xfDawteLLoM0GV8Pmt8C1EUa3/aZF7axKv7U1WmYy0Oh+TJ5mQwcBHHA==
"@xterm/addon-serialize@0.12.0-beta.31":
version "0.12.0-beta.31"
resolved "https://registry.yarnpkg.com/@xterm/addon-serialize/-/addon-serialize-0.12.0-beta.31.tgz#daec32b94d45afcd662351d7689cb1b19eb24db7"
integrity sha512-MZ24pw33qOJrHdA6tlvwE4dSSpmIp/H9ZKtbiWZvuxVsY/hfYYPOluBQiCsOiYT7bZ8gQub2OOBX3jyMoZVxnQ==
"@xterm/addon-serialize@0.13.0-beta.3":
version "0.13.0-beta.3"
resolved "https://registry.yarnpkg.com/@xterm/addon-serialize/-/addon-serialize-0.13.0-beta.3.tgz#665adc0830a3c2cede399c660121650924907da9"
integrity sha512-88putapu36cKM0DBZpJ0k4Hk09JVF1B3kKtj9utXlOWNsriX5WeUH/yEWr+T8iqsnYcUsROOuC12rtoW92+uvg==
"@xterm/addon-unicode11@0.7.0-beta.31":
version "0.7.0-beta.31"
resolved "https://registry.yarnpkg.com/@xterm/addon-unicode11/-/addon-unicode11-0.7.0-beta.31.tgz#e1a6e965638ee6cb59b8b0777387037c42582d4b"
integrity sha512-wrZLt2s6Yjmpe4nh0Sp6DKji0EoHod7V6ABfWBf8krjmEGSleE+GSb+ZwDOMsNzLJLmxoq1e6glHcVixG1z7WQ==
"@xterm/addon-unicode11@0.8.0-beta.3":
version "0.8.0-beta.3"
resolved "https://registry.yarnpkg.com/@xterm/addon-unicode11/-/addon-unicode11-0.8.0-beta.3.tgz#083092a40a7cad8ed03a41f67ad21f33048b6398"
integrity sha512-zPg5ItGawDTSayuxxIxGcLeNYPEq8bpY999/cVjckt02KxD2TJ097URWAnS0Hr7OYO9OxR4NPOOjbSNSw29OFg==
"@xterm/addon-webgl@0.17.0-beta.31":
version "0.17.0-beta.31"
resolved "https://registry.yarnpkg.com/@xterm/addon-webgl/-/addon-webgl-0.17.0-beta.31.tgz#15dfea4583ff9b65f1a442e5cdba1d1638adb05f"
integrity sha512-wqbBDDppwQ4R8o0YgnyFL8Pai2mVZqHb3E097vkFLB5Fw2hNx2dys3MgiXriSGXaUABKM3usVdZyouL6QgWdxQ==
"@xterm/addon-webgl@0.18.0-beta.3":
version "0.18.0-beta.3"
resolved "https://registry.yarnpkg.com/@xterm/addon-webgl/-/addon-webgl-0.18.0-beta.3.tgz#c592be94c2230a03cb0c0501a4aafbbeac49e691"
integrity sha512-M36K2QhZl/HKVNRXftxJbn7YMaqWVqWwgW1lxyHefn2uZx1+jfSXM8EQo+PpntPuGJaUWZ3zoLv8TGz9rNJEFg==
"@xterm/headless@5.4.0-beta.31":
version "5.4.0-beta.31"
resolved "https://registry.yarnpkg.com/@xterm/headless/-/headless-5.4.0-beta.31.tgz#7727c5c79d3b1b8e59526cf51c75148e13f61694"
integrity sha512-AIMP0ZZozxtvilVTKqquNPYDE5RuKINTsYjOcWzYvjpg7sS75/Tn/RBx20KfZN8Z2oCCwVgj+1mudrV0W4JmMw==
"@xterm/headless@5.5.0-beta.3":
version "5.5.0-beta.3"
resolved "https://registry.yarnpkg.com/@xterm/headless/-/headless-5.5.0-beta.3.tgz#d58d07b5d5e08987cc0cd5888f28f4750627c286"
integrity sha512-F5AdR4VPBmCQGcc57zGTHTT5JZyAUWpBxxY+vclrH/AVxnf9/5uRcSdCmXc8Y558FtdVynG31k48c6fd9n1vVw==
"@xterm/xterm@5.4.0-beta.31":
version "5.4.0-beta.31"
resolved "https://registry.yarnpkg.com/@xterm/xterm/-/xterm-5.4.0-beta.31.tgz#ff0bb3af9b00b0dfc73e84075f4218440c9886be"
integrity sha512-EpCtaYqMhJSyZrGY2sJVZeRCIRrANKtv1GGTj+IQPvk6hTiJHGrFHLM0tZ0dj0l3z65tLoOdj6EzJnjzX3Pqjw==
"@xterm/xterm@5.5.0-beta.3":
version "5.5.0-beta.3"
resolved "https://registry.yarnpkg.com/@xterm/xterm/-/xterm-5.5.0-beta.3.tgz#40b9017cbac37f7f55f227a10e37b3519ed3f39f"
integrity sha512-ukbnGJxJTFVCI6voThi04ePPtJ3NLEQSTRDskxTwgjIxfUw1s/LwGhAG2SZnQcgqtDLXjIXAslrgVRiVBQ3yXg==
agent-base@^7.0.1, agent-base@^7.0.2, agent-base@^7.1.0:
version "7.1.0"
-7
View File
@@ -921,13 +921,6 @@ export function getActiveWindow(): CodeWindow {
return (document.defaultView?.window ?? mainWindow) as CodeWindow;
}
export function focusWindow(element: Node): void {
const window = getWindow(element);
if (!window.document.hasFocus()) {
window.focus();
}
}
const globalStylesheets = new Map<HTMLStyleElement /* main stylesheet */, Set<HTMLStyleElement /* aux window clones that track the main stylesheet */>>();
export function isGlobalStylesheet(node: Node): boolean {
@@ -270,7 +270,14 @@ export function setupCustomHover(hoverDelegate: IHoverDelegate, htmlElement: HTM
toDispose.add(triggerShowHover(hoverDelegate.delay, false, target));
hoverPreparation = toDispose;
};
const focusDomEmitter = dom.addDisposableListener(htmlElement, dom.EventType.FOCUS, onFocus, true);
// Do not show hover when focusing an input or textarea
let focusDomEmitter: undefined | IDisposable;
const tagName = htmlElement.tagName.toLowerCase();
if (tagName !== 'input' && tagName !== 'textarea') {
focusDomEmitter = dom.addDisposableListener(htmlElement, dom.EventType.FOCUS, onFocus, true);
}
const hover: ICustomHover = {
show: focus => {
hideHover(false, true); // terminate a ongoing mouse over preparation
@@ -288,7 +295,7 @@ export function setupCustomHover(hoverDelegate: IHoverDelegate, htmlElement: HTM
mouseLeaveEmitter.dispose();
mouseDownEmitter.dispose();
mouseUpEmitter.dispose();
focusDomEmitter.dispose();
focusDomEmitter?.dispose();
hideHover(true, true);
}
};
+8 -1
View File
@@ -11,6 +11,8 @@ import { MarkdownRenderOptions } from 'vs/base/browser/markdownRenderer';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import * as aria from 'vs/base/browser/ui/aria/aria';
import { AnchorAlignment, IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory';
import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget';
import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { Widget } from 'vs/base/browser/ui/widget';
import { IAction } from 'vs/base/common/actions';
@@ -111,6 +113,7 @@ export class InputBox extends Widget {
private cachedContentHeight: number | undefined;
private maxHeight: number = Number.POSITIVE_INFINITY;
private scrollableElement: ScrollableElement | undefined;
private hover: ICustomHover | undefined;
private _onDidChange = this._register(new Emitter<string>());
public readonly onDidChange: Event<string> = this._onDidChange.event;
@@ -230,7 +233,11 @@ export class InputBox extends Widget {
public setTooltip(tooltip: string): void {
this.tooltip = tooltip;
this.input.title = tooltip;
if (!this.hover) {
this.hover = this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), this.input, tooltip));
} else {
this.hover.update(tooltip);
}
}
public setAriaLabel(label: string): void {
+11 -1
View File
@@ -36,7 +36,17 @@ export const codiconsDerived = {
scrollbarButtonUp: register('scrollbar-button-up', 'triangle-up'),
scrollbarButtonDown: register('scrollbar-button-down', 'triangle-down'),
toolBarMore: register('toolbar-more', 'more'),
quickInputBack: register('quick-input-back', 'arrow-left')
quickInputBack: register('quick-input-back', 'arrow-left'),
dropDownButton: register('drop-down-button', 0xeab4),
symbolCustomColor: register('symbol-customcolor', 0xeb5c),
exportIcon: register('export', 0xebac),
workspaceUnspecified: register('workspace-unspecified', 0xebc3),
newLine: register('newline', 0xebea),
thumbsDownFilled: register('thumbsdown-filled', 0xec13),
thumbsUpFilled: register('thumbsup-filled', 0xec14),
gitFetch: register('git-fetch', 0xec1d),
lightbulbSparkleAutofix: register('lightbulb-sparkle-autofix', 0xec1f),
debugBreakpointPending: register('debug-breakpoint-pending', 0xebd9),
} as const;
+28 -24
View File
@@ -4,10 +4,9 @@
*--------------------------------------------------------------------------------------------*/
import { register } from 'vs/base/common/codiconsUtil';
// This list is automatically generated by the vscode-codicons repo.
// This file is automatically generated by (microsoft/vscode-codicon)/scripts/export-to-ts.js
// Please don't edit it, as your changes will be overwritten.
// If you want to create a mapping, add it to the codiconsDerived list in codicons.ts.
// Instead, add mappings to codiconsDerived in codicons.ts.
export const codiconsLibrary = {
add: register('add', 0xea60),
plus: register('plus', 0xea60),
@@ -24,9 +23,9 @@ export const codiconsLibrary = {
recordKeys: register('record-keys', 0xea65),
keyboard: register('keyboard', 0xea65),
tag: register('tag', 0xea66),
gitPullRequestLabel: register('git-pull-request-label', 0xea66),
tagAdd: register('tag-add', 0xea66),
tagRemove: register('tag-remove', 0xea66),
gitPullRequestLabel: register('git-pull-request-label', 0xea66),
person: register('person', 0xea67),
personFollow: register('person-follow', 0xea67),
personOutline: register('person-outline', 0xea67),
@@ -59,8 +58,8 @@ export const codiconsLibrary = {
closeDirty: register('close-dirty', 0xea71),
debugBreakpoint: register('debug-breakpoint', 0xea71),
debugBreakpointDisabled: register('debug-breakpoint-disabled', 0xea71),
debugBreakpointPending: register('debug-breakpoint-pending', 0xebd9),
debugHint: register('debug-hint', 0xea71),
terminalDecorationSuccess: register('terminal-decoration-success', 0xea71),
primitiveSquare: register('primitive-square', 0xea72),
edit: register('edit', 0xea73),
pencil: register('pencil', 0xea73),
@@ -172,7 +171,6 @@ export const codiconsLibrary = {
check: register('check', 0xeab2),
checklist: register('checklist', 0xeab3),
chevronDown: register('chevron-down', 0xeab4),
dropDownButton: register('drop-down-button', 0xeab4),
chevronLeft: register('chevron-left', 0xeab5),
chevronRight: register('chevron-right', 0xeab6),
chevronUp: register('chevron-up', 0xeab7),
@@ -180,9 +178,10 @@ export const codiconsLibrary = {
chromeMaximize: register('chrome-maximize', 0xeab9),
chromeMinimize: register('chrome-minimize', 0xeaba),
chromeRestore: register('chrome-restore', 0xeabb),
circle: register('circle', 0xeabc),
circleOutline: register('circle-outline', 0xeabc),
circle: register('circle', 0xeabc),
debugBreakpointUnverified: register('debug-breakpoint-unverified', 0xeabc),
terminalDecorationIncomplete: register('terminal-decoration-incomplete', 0xeabc),
circleSlash: register('circle-slash', 0xeabd),
circuitBoard: register('circuit-board', 0xeabe),
clearAll: register('clear-all', 0xeabf),
@@ -194,7 +193,6 @@ export const codiconsLibrary = {
collapseAll: register('collapse-all', 0xeac5),
colorMode: register('color-mode', 0xeac6),
commentDiscussion: register('comment-discussion', 0xeac7),
compareChanges: register('compare-changes', 0xeafd),
creditCard: register('credit-card', 0xeac9),
dash: register('dash', 0xeacc),
dashboard: register('dashboard', 0xeacd),
@@ -218,6 +216,7 @@ export const codiconsLibrary = {
diffRemoved: register('diff-removed', 0xeadf),
diffRenamed: register('diff-renamed', 0xeae0),
diff: register('diff', 0xeae1),
diffSidebyside: register('diff-sidebyside', 0xeae1),
discard: register('discard', 0xeae2),
editorLayout: register('editor-layout', 0xeae3),
emptyWindow: register('empty-window', 0xeae4),
@@ -246,6 +245,7 @@ export const codiconsLibrary = {
gist: register('gist', 0xeafb),
gitCommit: register('git-commit', 0xeafc),
gitCompare: register('git-compare', 0xeafd),
compareChanges: register('compare-changes', 0xeafd),
gitMerge: register('git-merge', 0xeafe),
githubAction: register('github-action', 0xeaff),
githubAlt: register('github-alt', 0xeb00),
@@ -258,13 +258,11 @@ export const codiconsLibrary = {
horizontalRule: register('horizontal-rule', 0xeb07),
hubot: register('hubot', 0xeb08),
inbox: register('inbox', 0xeb09),
issueClosed: register('issue-closed', 0xeba4),
issueReopened: register('issue-reopened', 0xeb0b),
issues: register('issues', 0xeb0c),
italic: register('italic', 0xeb0d),
jersey: register('jersey', 0xeb0e),
json: register('json', 0xeb0f),
bracket: register('bracket', 0xeb0f),
kebabVertical: register('kebab-vertical', 0xeb10),
key: register('key', 0xeb11),
law: register('law', 0xeb12),
@@ -343,7 +341,6 @@ export const codiconsLibrary = {
starHalf: register('star-half', 0xeb5a),
symbolClass: register('symbol-class', 0xeb5b),
symbolColor: register('symbol-color', 0xeb5c),
symbolCustomColor: register('symbol-customcolor', 0xeb5c),
symbolConstant: register('symbol-constant', 0xeb5d),
symbolEnumMember: register('symbol-enum-member', 0xeb5e),
symbolField: register('symbol-field', 0xeb5f),
@@ -395,6 +392,7 @@ export const codiconsLibrary = {
debugStackframeActive: register('debug-stackframe-active', 0xeb89),
circleSmallFilled: register('circle-small-filled', 0xeb8a),
debugStackframeDot: register('debug-stackframe-dot', 0xeb8a),
terminalDecorationMark: register('terminal-decoration-mark', 0xeb8a),
debugStackframe: register('debug-stackframe', 0xeb8b),
debugStackframeFocused: register('debug-stackframe-focused', 0xeb8b),
debugBreakpointUnsupported: register('debug-breakpoint-unsupported', 0xeb8c),
@@ -402,6 +400,7 @@ export const codiconsLibrary = {
debugReverseContinue: register('debug-reverse-continue', 0xeb8e),
debugStepBack: register('debug-step-back', 0xeb8f),
debugRestartFrame: register('debug-restart-frame', 0xeb90),
debugAlt: register('debug-alt', 0xeb91),
callIncoming: register('call-incoming', 0xeb92),
callOutgoing: register('call-outgoing', 0xeb93),
menu: register('menu', 0xeb94),
@@ -420,10 +419,10 @@ export const codiconsLibrary = {
syncIgnored: register('sync-ignored', 0xeb9f),
pinned: register('pinned', 0xeba0),
githubInverted: register('github-inverted', 0xeba1),
debugAlt: register('debug-alt', 0xeb91),
serverProcess: register('server-process', 0xeba2),
serverEnvironment: register('server-environment', 0xeba3),
pass: register('pass', 0xeba4),
issueClosed: register('issue-closed', 0xeba4),
stopCircle: register('stop-circle', 0xeba5),
playCircle: register('play-circle', 0xeba6),
record: register('record', 0xeba7),
@@ -431,7 +430,7 @@ export const codiconsLibrary = {
vmConnect: register('vm-connect', 0xeba9),
cloud: register('cloud', 0xebaa),
merge: register('merge', 0xebab),
exportIcon: register('export', 0xebac),
export: register('export', 0xebac),
graphLeft: register('graph-left', 0xebad),
magnet: register('magnet', 0xebae),
notebook: register('notebook', 0xebaf),
@@ -456,7 +455,7 @@ export const codiconsLibrary = {
debugRerun: register('debug-rerun', 0xebc0),
workspaceTrusted: register('workspace-trusted', 0xebc1),
workspaceUntrusted: register('workspace-untrusted', 0xebc2),
workspaceUnspecified: register('workspace-unspecified', 0xebc3),
workspaceUnknown: register('workspace-unknown', 0xebc3),
terminalCmd: register('terminal-cmd', 0xebc4),
terminalDebian: register('terminal-debian', 0xebc5),
terminalLinux: register('terminal-linux', 0xebc6),
@@ -490,12 +489,13 @@ export const codiconsLibrary = {
graphLine: register('graph-line', 0xebe2),
graphScatter: register('graph-scatter', 0xebe3),
pieChart: register('pie-chart', 0xebe4),
bracket: register('bracket', 0xeb0f),
bracketDot: register('bracket-dot', 0xebe5),
bracketError: register('bracket-error', 0xebe6),
lockSmall: register('lock-small', 0xebe7),
azureDevops: register('azure-devops', 0xebe8),
verifiedFilled: register('verified-filled', 0xebe9),
newLine: register('newline', 0xebea),
newline: register('newline', 0xebea),
layout: register('layout', 0xebeb),
layoutActivitybarLeft: register('layout-activitybar-left', 0xebec),
layoutActivitybarRight: register('layout-activitybar-right', 0xebed),
@@ -509,17 +509,19 @@ export const codiconsLibrary = {
layoutStatusbar: register('layout-statusbar', 0xebf5),
layoutMenubar: register('layout-menubar', 0xebf6),
layoutCentered: register('layout-centered', 0xebf7),
layoutSidebarRightOff: register('layout-sidebar-right-off', 0xec00),
layoutPanelOff: register('layout-panel-off', 0xec01),
layoutSidebarLeftOff: register('layout-sidebar-left-off', 0xec02),
target: register('target', 0xebf8),
indent: register('indent', 0xebf9),
recordSmall: register('record-small', 0xebfa),
errorSmall: register('error-small', 0xebfb),
terminalDecorationError: register('terminal-decoration-error', 0xebfb),
arrowCircleDown: register('arrow-circle-down', 0xebfc),
arrowCircleLeft: register('arrow-circle-left', 0xebfd),
arrowCircleRight: register('arrow-circle-right', 0xebfe),
arrowCircleUp: register('arrow-circle-up', 0xebff),
layoutSidebarRightOff: register('layout-sidebar-right-off', 0xec00),
layoutPanelOff: register('layout-panel-off', 0xec01),
layoutSidebarLeftOff: register('layout-sidebar-left-off', 0xec02),
blank: register('blank', 0xec03),
heartFilled: register('heart-filled', 0xec04),
map: register('map', 0xec05),
mapFilled: register('map-filled', 0xec06),
@@ -535,8 +537,8 @@ export const codiconsLibrary = {
sparkle: register('sparkle', 0xec10),
insert: register('insert', 0xec11),
mic: register('mic', 0xec12),
thumbsDownFilled: register('thumbsdown-filled', 0xec13),
thumbsUpFilled: register('thumbsup-filled', 0xec14),
thumbsdownFilled: register('thumbsdown-filled', 0xec13),
thumbsupFilled: register('thumbsup-filled', 0xec14),
coffee: register('coffee', 0xec15),
snake: register('snake', 0xec16),
game: register('game', 0xec17),
@@ -545,21 +547,23 @@ export const codiconsLibrary = {
piano: register('piano', 0xec1a),
music: register('music', 0xec1b),
micFilled: register('mic-filled', 0xec1c),
gitFetch: register('git-fetch', 0xec1d),
repoFetch: register('repo-fetch', 0xec1d),
copilot: register('copilot', 0xec1e),
lightbulbSparkle: register('lightbulb-sparkle', 0xec1f),
lightbulbSparkleAutofix: register('lightbulb-sparkle-autofix', 0xec1f),
robot: register('robot', 0xec20),
sparkleFilled: register('sparkle-filled', 0xec21),
diffSingle: register('diff-single', 0xec22),
diffMultiple: register('diff-multiple', 0xec23),
surroundWith: register('surround-with', 0xec24),
share: register('share', 0xec25),
gitStash: register('git-stash', 0xec26),
gitStashApply: register('git-stash-apply', 0xec27),
gitStashPop: register('git-stash-pop', 0xec28),
vscode: register('vscode', 0xec29),
vscodeInsiders: register('vscode-insiders', 0xec2a),
codeOss: register('code-oss', 0xec2b),
runCoverage: register('run-coverage', 0xec2c),
runAllCoverage: register('run-all-coverage', 0xec2d),
runCoverage: register('run-all-coverage', 0xec2c),
coverage: register('coverage', 0xec2e),
githubProject: register('github-project', 0xec2f),
} as const;
+14 -2
View File
@@ -71,6 +71,7 @@ import { LogService } from 'vs/platform/log/common/logService';
import { massageMessageBoxOptions } from 'vs/platform/dialogs/common/dialogs';
import { SaveStrategy, StateService } from 'vs/platform/state/node/stateService';
import { FileUserDataProvider } from 'vs/platform/userData/common/fileUserDataProvider';
import { addUNCHostToAllowlist, getUNCHost } from 'vs/base/node/unc';
/**
* The main VS Code entry point.
@@ -249,8 +250,8 @@ class CodeMain {
// Environment service (paths)
Promise.all<string | undefined>([
environmentMainService.extensionsPath,
environmentMainService.codeCachePath,
this.allowWindowsUNCPath(environmentMainService.extensionsPath), // enable extension paths on UNC drives...
environmentMainService.codeCachePath, // ...other user-data-derived paths should already be enlisted from `main.js`
environmentMainService.logsHome.with({ scheme: Schemas.file }).fsPath,
userDataProfilesMainService.defaultProfile.globalStorageHome.with({ scheme: Schemas.file }).fsPath,
environmentMainService.workspaceStorageHome.with({ scheme: Schemas.file }).fsPath,
@@ -269,6 +270,17 @@ class CodeMain {
userDataProfilesMainService.init();
}
private allowWindowsUNCPath(path: string): string {
if (isWindows) {
const host = getUNCHost(path);
if (host) {
addUNCHostToAllowlist(host);
}
}
return path;
}
private async claimInstance(logService: ILogService, environmentMainService: IEnvironmentMainService, lifecycleMainService: ILifecycleMainService, instantiationService: IInstantiationService, productService: IProductService, retry: boolean): Promise<NodeIPCServer> {
// Try to setup a server for running. If that succeeds it means
@@ -78,6 +78,10 @@ export class IssueReporter extends Disposable {
const issueReporterElement = this.getElementById('issue-reporter');
if (issueReporterElement) {
this.previewButton = new Button(issueReporterElement, unthemedButtonStyles);
const issueRepoName = document.createElement('a');
issueReporterElement.appendChild(issueRepoName);
issueRepoName.id = 'show-repo-name';
issueRepoName.classList.add('hidden');
this.updatePreviewButtonState();
}
@@ -501,6 +505,31 @@ export class IssueReporter extends Disposable {
this.previewButton.enabled = false;
this.previewButton.label = localize('loadingData', "Loading data...");
}
const issueRepoName = this.getElementById('show-repo-name')! as HTMLAnchorElement;
const selectedExtension = this.issueReporterModel.getData().selectedExtension;
if (selectedExtension && selectedExtension.uri) {
const urlString = URI.revive(selectedExtension.uri).toString();
issueRepoName.href = urlString;
issueRepoName.addEventListener('click', (e) => this.openLink(e));
issueRepoName.addEventListener('auxclick', (e) => this.openLink(<MouseEvent>e));
const gitHubInfo = this.parseGitHubUrl(urlString);
issueRepoName.textContent = gitHubInfo ? gitHubInfo.owner + '/' + gitHubInfo.repositoryName : urlString;
Object.assign(issueRepoName.style, {
alignSelf: 'flex-end',
display: 'block',
fontSize: '13px',
marginBottom: '10px',
padding: '4px 0px',
textDecoration: 'none',
width: 'auto'
});
show(issueRepoName);
} else {
// clear styles
issueRepoName.removeAttribute('style');
hide(issueRepoName);
}
}
private isPreviewEnabled() {
+14 -2
View File
@@ -63,6 +63,7 @@ import { LogService } from 'vs/platform/log/common/logService';
import { LoggerService } from 'vs/platform/log/node/loggerService';
import { localize } from 'vs/nls';
import { FileUserDataProvider } from 'vs/platform/userData/common/fileUserDataProvider';
import { addUNCHostToAllowlist, getUNCHost } from 'vs/base/node/unc';
class CliMain extends Disposable {
@@ -121,8 +122,8 @@ class CliMain extends Disposable {
// Init folders
await Promise.all([
environmentService.appSettingsHome.with({ scheme: Schemas.file }).fsPath,
environmentService.extensionsPath
this.allowWindowsUNCPath(environmentService.appSettingsHome.with({ scheme: Schemas.file }).fsPath),
this.allowWindowsUNCPath(environmentService.extensionsPath)
].map(path => path ? Promises.mkdir(path, { recursive: true }) : undefined));
// Logger
@@ -233,6 +234,17 @@ class CliMain extends Disposable {
return [new InstantiationService(services), appenders];
}
private allowWindowsUNCPath(path: string): string {
if (isWindows) {
const host = getUNCHost(path);
if (host) {
addUNCHostToAllowlist(host);
}
}
return path;
}
private registerErrorHandler(logService: ILogService): void {
// Install handler for unexpected errors
@@ -519,15 +519,24 @@ export async function main(configuration: ISharedProcessConfiguration): Promise<
// create shared process and signal back to main that we are
// ready to accept message ports as client connections
const sharedProcess = new SharedProcessMain(configuration);
process.parentPort.postMessage(SharedProcessLifecycle.ipcReady);
try {
const sharedProcess = new SharedProcessMain(configuration);
process.parentPort.postMessage(SharedProcessLifecycle.ipcReady);
// await initialization and signal this back to electron-main
await sharedProcess.init();
// await initialization and signal this back to electron-main
await sharedProcess.init();
process.parentPort.postMessage(SharedProcessLifecycle.initDone);
process.parentPort.postMessage(SharedProcessLifecycle.initDone);
} catch (error) {
process.parentPort.postMessage({ error: error.toString() });
}
}
const handle = setTimeout(() => {
process.parentPort.postMessage({ warning: '[SharedProcess] did not receive configuration within 30s...' });
}, 30000);
process.parentPort.once('message', (e: Electron.MessageEvent) => {
clearTimeout(handle);
main(e.data as ISharedProcessConfiguration);
});
@@ -30,7 +30,7 @@ import { IDiffEditorOptions } from 'vs/editor/common/config/editorOptions';
import { IDimension } from 'vs/editor/common/core/dimension';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { CursorChangeReason } from 'vs/editor/common/cursorEvents';
import { CursorChangeReason, ICursorPositionChangedEvent } from 'vs/editor/common/cursorEvents';
import { IDiffComputationResult, ILineChange } from 'vs/editor/common/diff/legacyLinesDiffComputer';
import { LineRangeMapping, RangeMapping } from 'vs/editor/common/diff/rangeMapping';
import { EditorType, IDiffEditorModel, IDiffEditorViewModel, IDiffEditorViewState } from 'vs/editor/common/editorCommon';
@@ -267,18 +267,9 @@ export class DiffEditorWidget extends DelegatingEditor implements IDiffEditor {
),
}));
this._register(Event.runAndSubscribe(this._editors.modified.onDidChangeCursorPosition, (e) => {
if (e?.reason === CursorChangeReason.Explicit) {
const diff = this._diffModel.get()?.diff.get()?.mappings.find(m => m.lineRangeMapping.modified.contains(e.position.lineNumber));
if (diff?.lineRangeMapping.modified.isEmpty) {
this._accessibilitySignalService.playSignal(AccessibilitySignal.diffLineDeleted, { source: 'diffEditor.cursorPositionChanged' });
} else if (diff?.lineRangeMapping.original.isEmpty) {
this._accessibilitySignalService.playSignal(AccessibilitySignal.diffLineInserted, { source: 'diffEditor.cursorPositionChanged' });
} else if (diff) {
this._accessibilitySignalService.playSignal(AccessibilitySignal.diffLineModified, { source: 'diffEditor.cursorPositionChanged' });
}
}
}));
this._register(Event.runAndSubscribe(this._editors.modified.onDidChangeCursorPosition, e => this._handleCursorPositionChange(e, true)));
this._register(Event.runAndSubscribe(this._editors.original.onDidChangeCursorPosition, e => this._handleCursorPositionChange(e, false)));
const isInitializingDiff = this._diffModel.map(this, (m, reader) => {
/** @isInitializingDiff isDiffUpToDate */
@@ -608,6 +599,19 @@ export class DiffEditorWidget extends DelegatingEditor implements IDiffEditor {
}
});
}
private _handleCursorPositionChange(e: ICursorPositionChangedEvent | undefined, isModifiedEditor: boolean): void {
if (e?.reason === CursorChangeReason.Explicit) {
const diff = this._diffModel.get()?.diff.get()?.mappings.find(m => isModifiedEditor ? m.lineRangeMapping.modified.contains(e.position.lineNumber) : m.lineRangeMapping.original.contains(e.position.lineNumber));
if (diff?.lineRangeMapping.modified.isEmpty) {
this._accessibilitySignalService.playSignal(AccessibilitySignal.diffLineDeleted, { source: 'diffEditor.cursorPositionChanged' });
} else if (diff?.lineRangeMapping.original.isEmpty) {
this._accessibilitySignalService.playSignal(AccessibilitySignal.diffLineInserted, { source: 'diffEditor.cursorPositionChanged' });
} else if (diff) {
this._accessibilitySignalService.playSignal(AccessibilitySignal.diffLineModified, { source: 'diffEditor.cursorPositionChanged' });
}
}
}
}
function toLineChanges(state: DiffState): ILineChange[] {
+17 -1
View File
@@ -547,6 +547,22 @@ export interface CompletionList {
duration?: number;
}
/**
* Info provided on partial acceptance.
*/
export interface PartialAcceptInfo {
kind: PartialAcceptTriggerKind;
}
/**
* How a partial acceptance was triggered.
*/
export const enum PartialAcceptTriggerKind {
Word = 0,
Line = 1,
Suggest = 2,
}
/**
* How a suggest provider was triggered.
*/
@@ -718,7 +734,7 @@ export interface InlineCompletionsProvider<T extends InlineCompletions = InlineC
/**
* Will be called when an item is partially accepted.
*/
handlePartialAccept?(completions: T, item: T['items'][number], acceptedCharacters: number): void;
handlePartialAccept?(completions: T, item: T['items'][number], acceptedCharacters: number, info: PartialAcceptInfo): void;
/**
* Will be called when a completions list is no longer in use and can be garbage-collected.
@@ -740,6 +740,15 @@ export enum OverviewRulerLane {
Full = 7
}
/**
* How a partial acceptance was triggered.
*/
export enum PartialAcceptTriggerKind {
Word = 0,
Line = 1,
Suggest = 2
}
export enum PositionAffinity {
/**
* Prefers the left most position.
@@ -14,7 +14,7 @@ import { EditOperation } from 'vs/editor/common/core/editOperation';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { InlineCompletionContext, InlineCompletionTriggerKind } from 'vs/editor/common/languages';
import { InlineCompletionContext, InlineCompletionTriggerKind, PartialAcceptTriggerKind } from 'vs/editor/common/languages';
import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry';
import { EndOfLinePreference, ITextModel } from 'vs/editor/common/model';
import { IFeatureDebounceInformation } from 'vs/editor/common/services/languageFeatureDebounce';
@@ -379,7 +379,7 @@ export class InlineCompletionsModel extends Disposable {
}
}
return acceptUntilIndexExclusive;
});
}, PartialAcceptTriggerKind.Word);
}
public async acceptNextLine(editor: ICodeEditor): Promise<void> {
@@ -389,10 +389,10 @@ export class InlineCompletionsModel extends Disposable {
return m.index + 1;
}
return text.length;
});
}, PartialAcceptTriggerKind.Line);
}
private async _acceptNext(editor: ICodeEditor, getAcceptUntilIndex: (position: Position, text: string) => number): Promise<void> {
private async _acceptNext(editor: ICodeEditor, getAcceptUntilIndex: (position: Position, text: string) => number, kind: PartialAcceptTriggerKind): Promise<void> {
if (editor.getModel() !== this.textModel) {
throw new BugIndicatingError();
}
@@ -448,6 +448,9 @@ export class InlineCompletionsModel extends Disposable {
completion.source.inlineCompletions,
completion.sourceInlineCompletion,
text.length,
{
kind,
}
);
}
} finally {
@@ -465,6 +468,9 @@ export class InlineCompletionsModel extends Disposable {
inlineCompletion.source.inlineCompletions,
inlineCompletion.sourceInlineCompletion,
itemEdit.text.length,
{
kind: PartialAcceptTriggerKind.Suggest,
}
);
}
}
@@ -310,6 +310,11 @@ class WordHighlighter {
this._onPositionChanged(e);
}));
this.toUnhook.add(editor.onDidFocusEditorText((e) => {
if (this.occurrencesHighlight === 'off') {
// Early exit if nothing needs to be done
return;
}
if (!this.workerRequest) {
this._run();
}
@@ -809,6 +809,7 @@ export function createMonacoLanguagesAPI(): typeof monaco.languages {
InlineEditTriggerKind: standaloneEnums.InlineEditTriggerKind,
CodeActionTriggerType: standaloneEnums.CodeActionTriggerType,
NewSymbolNameTag: standaloneEnums.NewSymbolNameTag,
PartialAcceptTriggerKind: standaloneEnums.PartialAcceptTriggerKind,
// classes
FoldingRangeKind: languages.FoldingRangeKind,
+17 -1
View File
@@ -6956,6 +6956,22 @@ declare namespace monaco.languages {
dispose?(): void;
}
/**
* Info provided on partial acceptance.
*/
export interface PartialAcceptInfo {
kind: PartialAcceptTriggerKind;
}
/**
* How a partial acceptance was triggered.
*/
export enum PartialAcceptTriggerKind {
Word = 0,
Line = 1,
Suggest = 2
}
/**
* How a suggest provider was triggered.
*/
@@ -7102,7 +7118,7 @@ declare namespace monaco.languages {
/**
* Will be called when an item is partially accepted.
*/
handlePartialAccept?(completions: T, item: T['items'][number], acceptedCharacters: number): void;
handlePartialAccept?(completions: T, item: T['items'][number], acceptedCharacters: number, info: PartialAcceptInfo): void;
/**
* Will be called when a completions list is no longer in use and can be garbage-collected.
*/
+1 -1
View File
@@ -8,7 +8,7 @@ export interface ILocalizeInfo {
comment: string[];
}
interface ILocalizedString {
export interface ILocalizedString {
original: string;
value: string;
}
@@ -17,15 +17,15 @@ export const IAccessibilitySignalService = createDecorator<IAccessibilitySignalS
export interface IAccessibilitySignalService {
readonly _serviceBrand: undefined;
playSignal(cue: AccessibilitySignal, options?: IAccessbilitySignalOptions): Promise<void>;
playAccessibilitySignals(cues: (AccessibilitySignal | { cue: AccessibilitySignal; source: string })[]): Promise<void>;
isSoundEnabled(cue: AccessibilitySignal): boolean;
isAnnouncementEnabled(cue: AccessibilitySignal): boolean;
onSoundEnabledChanged(cue: AccessibilitySignal): Event<void>;
onAnnouncementEnabledChanged(cue: AccessibilitySignal): Event<void>;
playSignal(signal: AccessibilitySignal, options?: IAccessbilitySignalOptions): Promise<void>;
playAccessibilitySignals(signals: (AccessibilitySignal | { signal: AccessibilitySignal; source: string })[]): Promise<void>;
isSoundEnabled(signal: AccessibilitySignal): boolean;
isAnnouncementEnabled(signal: AccessibilitySignal): boolean;
onSoundEnabledChanged(signal: AccessibilitySignal): Event<void>;
onAnnouncementEnabledChanged(signal: AccessibilitySignal): Event<void>;
playSound(cue: Sound, allowManyInParallel?: boolean): Promise<void>;
playSignalLoop(cue: AccessibilitySignal, milliseconds: number): IDisposable;
playSound(signal: Sound, allowManyInParallel?: boolean): Promise<void>;
playSignalLoop(signal: AccessibilitySignal, milliseconds: number): IDisposable;
}
export interface IAccessbilitySignalOptions {
@@ -57,9 +57,9 @@ export class AccessibilitySignalService extends Disposable implements IAccessibi
}
public async playSignal(signal: AccessibilitySignal, options: IAccessbilitySignalOptions = {}): Promise<void> {
const alertMessage = signal.announcementMessage;
if (this.isAnnouncementEnabled(signal, options.userGesture) && alertMessage) {
this.accessibilityService.status(alertMessage);
const announcementMessage = signal.announcementMessage;
if (this.isAnnouncementEnabled(signal, options.userGesture) && announcementMessage) {
this.accessibilityService.status(announcementMessage);
}
if (this.isSoundEnabled(signal, options.userGesture)) {
@@ -68,26 +68,26 @@ export class AccessibilitySignalService extends Disposable implements IAccessibi
}
}
public async playAccessibilitySignals(cues: (AccessibilitySignal | { cue: AccessibilitySignal; source: string })[]): Promise<void> {
for (const cue of cues) {
this.sendSignalTelemetry('cue' in cue ? cue.cue : cue, 'source' in cue ? cue.source : undefined);
public async playAccessibilitySignals(signals: (AccessibilitySignal | { signal: AccessibilitySignal; source: string })[]): Promise<void> {
for (const signal of signals) {
this.sendSignalTelemetry('signal' in signal ? signal.signal : signal, 'source' in signal ? signal.source : undefined);
}
const cueArray = cues.map(c => 'cue' in c ? c.cue : c);
const alerts = cueArray.filter(cue => this.isAnnouncementEnabled(cue)).map(c => c.announcementMessage);
if (alerts.length) {
this.accessibilityService.status(alerts.join(', '));
const signalArray = signals.map(s => 'signal' in s ? s.signal : s);
const announcements = signalArray.filter(signal => this.isAnnouncementEnabled(signal)).map(s => s.announcementMessage);
if (announcements.length) {
this.accessibilityService.status(announcements.join(', '));
}
// Some sounds are reused. Don't play the same sound twice.
const sounds = new Set(cueArray.filter(cue => this.isSoundEnabled(cue)).map(cue => cue.sound.getSound()));
const sounds = new Set(signalArray.filter(signal => this.isSoundEnabled(signal)).map(signal => signal.sound.getSound()));
await Promise.all(Array.from(sounds).map(sound => this.playSound(sound, true)));
}
private sendSignalTelemetry(cue: AccessibilitySignal, source: string | undefined): void {
private sendSignalTelemetry(signal: AccessibilitySignal, source: string | undefined): void {
const isScreenReaderOptimized = this.accessibilityService.isScreenReaderOptimized();
const key = cue.name + (source ? `::${source}` : '') + (isScreenReaderOptimized ? '{screenReaderOptimized}' : '');
const key = signal.name + (source ? `::${source}` : '') + (isScreenReaderOptimized ? '{screenReaderOptimized}' : '');
// Only send once per user session
if (this.sentTelemetry.has(key) || this.getVolumeInPercent() === 0) {
return;
@@ -107,7 +107,7 @@ export class AccessibilitySignalService extends Disposable implements IAccessibi
comment: 'This data is collected to understand how signals are used and if more signals should be added.';
}>('signal.played', {
signal: cue.name,
signal: signal.name,
source: source ?? '',
isScreenReaderOptimized,
});
@@ -214,7 +214,7 @@ export class AccessibilitySignalService extends Disposable implements IAccessibi
() => event.signal.announcementMessage ? this.configurationService.getValue<'auto' | 'off' | 'userGesture' | 'always' | 'never'>(event.signal.settingsKey + '.announcement') : false
);
return derived(reader => {
/** @description alert enabled */
/** @description announcement enabled */
const setting = settingObservable.read(reader);
if (
!this.screenReaderAttached.read(reader)
@@ -240,8 +240,8 @@ export class AccessibilitySignalService extends Disposable implements IAccessibi
return Event.fromObservableLight(this.isSoundEnabledCache.get({ signal }));
}
public onAnnouncementEnabledChanged(cue: AccessibilitySignal): Event<void> {
return Event.fromObservableLight(this.isAnnouncementEnabledCache.get({ signal: cue }));
public onAnnouncementEnabledChanged(signal: AccessibilitySignal): Event<void> {
return Event.fromObservableLight(this.isAnnouncementEnabledCache.get({ signal }));
}
}
@@ -314,6 +314,7 @@ export class Sound {
public static readonly clear = Sound.register({ fileName: 'clear.mp3' });
public static readonly save = Sound.register({ fileName: 'save.mp3' });
public static readonly format = Sound.register({ fileName: 'format.mp3' });
public static readonly voiceRecordingStarted = Sound.register({ fileName: 'voiceRecordingStarted.mp3' });
private constructor(public readonly fileName: string) { }
}
@@ -582,6 +583,13 @@ export class AccessibilitySignal {
settingsKey: 'accessibility.signals.format'
});
public static readonly voiceRecordingStarted = AccessibilitySignal.register({
name: localize('accessibilitySignals.voiceRecordingStarted', 'Voice Recording Started'),
sound: Sound.voiceRecordingStarted,
legacySoundSettingsKey: 'audioCues.voiceRecordingStarted',
settingsKey: 'accessibility.signals.voiceRecordingStarted'
});
private constructor(
public readonly sound: SoundSource,
public readonly name: string,
@@ -16,7 +16,8 @@ import * as nls from 'vs/nls';
import {
ExtensionManagementError, IExtensionGalleryService, IExtensionIdentifier, IExtensionManagementParticipant, IGalleryExtension, ILocalExtension, InstallOperation,
IExtensionsControlManifest, StatisticType, isTargetPlatformCompatible, TargetPlatformToString, ExtensionManagementErrorCode,
InstallOptions, UninstallOptions, Metadata, InstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionResult, UninstallExtensionEvent, IExtensionManagementService, InstallExtensionInfo, EXTENSION_INSTALL_DEP_PACK_CONTEXT, ExtensionGalleryError
InstallOptions, UninstallOptions, Metadata, InstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionResult, UninstallExtensionEvent, IExtensionManagementService, InstallExtensionInfo, EXTENSION_INSTALL_DEP_PACK_CONTEXT, ExtensionGalleryError,
IProductVersion
} from 'vs/platform/extensionManagement/common/extensionManagement';
import { areSameExtensions, ExtensionKey, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ExtensionType, IExtensionManifest, isApplicationScopedExtension, TargetPlatform } from 'vs/platform/extensions/common/extensions';
@@ -29,7 +30,7 @@ import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/use
export type ExtensionVerificationStatus = boolean | string;
export type InstallableExtension = { readonly manifest: IExtensionManifest; extension: IGalleryExtension | URI; options: InstallOptions };
export type InstallExtensionTaskOptions = InstallOptions & { readonly profileLocation: URI };
export type InstallExtensionTaskOptions = InstallOptions & { readonly profileLocation: URI; readonly productVersion: IProductVersion };
export interface IInstallExtensionTask {
readonly identifier: IExtensionIdentifier;
readonly source: IGalleryExtension | URI;
@@ -124,7 +125,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
await Promise.allSettled(extensions.map(async ({ extension, options }) => {
try {
const compatible = await this.checkAndGetCompatibleVersion(extension, !!options?.installGivenVersion, !!options?.installPreReleaseVersion);
const compatible = await this.checkAndGetCompatibleVersion(extension, !!options?.installGivenVersion, !!options?.installPreReleaseVersion, options.productVersion ?? { version: this.productService.version, date: this.productService.date });
installableExtensions.push({ ...compatible, options });
} catch (error) {
results.push({ identifier: extension.identifier, operation: InstallOperation.Install, source: extension, error });
@@ -230,7 +231,8 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
...options,
installOnlyNewlyAddedFromExtensionPack: options.installOnlyNewlyAddedFromExtensionPack ?? !URI.isUri(extension) /* always true for gallery extensions */,
isApplicationScoped,
profileLocation: isApplicationScoped ? this.userDataProfilesService.defaultProfile.extensionsResource : options.profileLocation ?? this.getCurrentExtensionsManifestLocation()
profileLocation: isApplicationScoped ? this.userDataProfilesService.defaultProfile.extensionsResource : options.profileLocation ?? this.getCurrentExtensionsManifestLocation(),
productVersion: options.productVersion ?? { version: this.productService.version, date: this.productService.date }
};
const existingInstallExtensionTask = !URI.isUri(extension) ? this.installingExtensions.get(getInstallExtensionTaskKey(extension, installExtensionTaskOptions.profileLocation)) : undefined;
@@ -248,8 +250,8 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
this.logService.info('Installing the extension without checking dependencies and pack', task.identifier.id);
} else {
try {
const allDepsAndPackExtensionsToInstall = await this.getAllDepsAndPackExtensions(task.identifier, manifest, !!task.options.installOnlyNewlyAddedFromExtensionPack, !!task.options.installPreReleaseVersion, task.options.profileLocation);
const installed = await this.getInstalled(undefined, task.options.profileLocation);
const allDepsAndPackExtensionsToInstall = await this.getAllDepsAndPackExtensions(task.identifier, manifest, !!task.options.installOnlyNewlyAddedFromExtensionPack, !!task.options.installPreReleaseVersion, task.options.profileLocation, task.options.productVersion);
const installed = await this.getInstalled(undefined, task.options.profileLocation, task.options.productVersion);
const options: InstallExtensionTaskOptions = { ...task.options, donotIncludePackAndDependencies: true, context: { ...task.options.context, [EXTENSION_INSTALL_DEP_PACK_CONTEXT]: true } };
for (const { gallery, manifest } of distinct(allDepsAndPackExtensionsToInstall, ({ gallery }) => gallery.identifier.id)) {
if (installingExtensionsMap.has(`${gallery.identifier.id.toLowerCase()}-${options.profileLocation.toString()}`)) {
@@ -405,12 +407,12 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
return results;
}
private async getAllDepsAndPackExtensions(extensionIdentifier: IExtensionIdentifier, manifest: IExtensionManifest, getOnlyNewlyAddedFromExtensionPack: boolean, installPreRelease: boolean, profile: URI | undefined): Promise<{ gallery: IGalleryExtension; manifest: IExtensionManifest }[]> {
private async getAllDepsAndPackExtensions(extensionIdentifier: IExtensionIdentifier, manifest: IExtensionManifest, getOnlyNewlyAddedFromExtensionPack: boolean, installPreRelease: boolean, profile: URI | undefined, productVersion: IProductVersion): Promise<{ gallery: IGalleryExtension; manifest: IExtensionManifest }[]> {
if (!this.galleryService.isEnabled()) {
return [];
}
const installed = await this.getInstalled(undefined, profile);
const installed = await this.getInstalled(undefined, profile, productVersion);
const knownIdentifiers: IExtensionIdentifier[] = [];
const allDependenciesAndPacks: { gallery: IGalleryExtension; manifest: IExtensionManifest }[] = [];
@@ -442,7 +444,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
const isDependency = dependecies.some(id => areSameExtensions({ id }, galleryExtension.identifier));
let compatible;
try {
compatible = await this.checkAndGetCompatibleVersion(galleryExtension, false, installPreRelease);
compatible = await this.checkAndGetCompatibleVersion(galleryExtension, false, installPreRelease, productVersion);
} catch (error) {
if (!isDependency) {
this.logService.info('Skipping the packed extension as it cannot be installed', galleryExtension.identifier.id, getErrorMessage(error));
@@ -462,7 +464,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
return allDependenciesAndPacks;
}
private async checkAndGetCompatibleVersion(extension: IGalleryExtension, sameVersion: boolean, installPreRelease: boolean): Promise<{ extension: IGalleryExtension; manifest: IExtensionManifest }> {
private async checkAndGetCompatibleVersion(extension: IGalleryExtension, sameVersion: boolean, installPreRelease: boolean, productVersion: IProductVersion): Promise<{ extension: IGalleryExtension; manifest: IExtensionManifest }> {
let compatibleExtension: IGalleryExtension | null;
const extensionsControlManifest = await this.getExtensionsControlManifest();
@@ -473,7 +475,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
const deprecationInfo = extensionsControlManifest.deprecated[extension.identifier.id.toLowerCase()];
if (deprecationInfo?.extension?.autoMigrate) {
this.logService.info(`The '${extension.identifier.id}' extension is deprecated, fetching the compatible '${deprecationInfo.extension.id}' extension instead.`);
compatibleExtension = (await this.galleryService.getExtensions([{ id: deprecationInfo.extension.id, preRelease: deprecationInfo.extension.preRelease }], { targetPlatform: await this.getTargetPlatform(), compatible: true }, CancellationToken.None))[0];
compatibleExtension = (await this.galleryService.getExtensions([{ id: deprecationInfo.extension.id, preRelease: deprecationInfo.extension.preRelease }], { targetPlatform: await this.getTargetPlatform(), compatible: true, productVersion }, CancellationToken.None))[0];
if (!compatibleExtension) {
throw new ExtensionManagementError(nls.localize('notFoundDeprecatedReplacementExtension', "Can't install '{0}' extension since it was deprecated and the replacement extension '{1}' can't be found.", extension.identifier.id, deprecationInfo.extension.id), ExtensionManagementErrorCode.Deprecated);
}
@@ -485,7 +487,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
throw new ExtensionManagementError(nls.localize('incompatible platform', "The '{0}' extension is not available in {1} for {2}.", extension.identifier.id, this.productService.nameLong, TargetPlatformToString(targetPlatform)), ExtensionManagementErrorCode.IncompatibleTargetPlatform);
}
compatibleExtension = await this.getCompatibleVersion(extension, sameVersion, installPreRelease);
compatibleExtension = await this.getCompatibleVersion(extension, sameVersion, installPreRelease, productVersion);
if (!compatibleExtension) {
/** If no compatible release version is found, check if the extension has a release version or not and throw relevant error */
if (!installPreRelease && extension.properties.isPreReleaseVersion && (await this.galleryService.getExtensions([extension.identifier], CancellationToken.None))[0]) {
@@ -508,23 +510,23 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
return { extension: compatibleExtension, manifest };
}
protected async getCompatibleVersion(extension: IGalleryExtension, sameVersion: boolean, includePreRelease: boolean): Promise<IGalleryExtension | null> {
protected async getCompatibleVersion(extension: IGalleryExtension, sameVersion: boolean, includePreRelease: boolean, productVersion: IProductVersion): Promise<IGalleryExtension | null> {
const targetPlatform = await this.getTargetPlatform();
let compatibleExtension: IGalleryExtension | null = null;
if (!sameVersion && extension.hasPreReleaseVersion && extension.properties.isPreReleaseVersion !== includePreRelease) {
compatibleExtension = (await this.galleryService.getExtensions([{ ...extension.identifier, preRelease: includePreRelease }], { targetPlatform, compatible: true }, CancellationToken.None))[0] || null;
compatibleExtension = (await this.galleryService.getExtensions([{ ...extension.identifier, preRelease: includePreRelease }], { targetPlatform, compatible: true, productVersion }, CancellationToken.None))[0] || null;
}
if (!compatibleExtension && await this.galleryService.isExtensionCompatible(extension, includePreRelease, targetPlatform)) {
if (!compatibleExtension && await this.galleryService.isExtensionCompatible(extension, includePreRelease, targetPlatform, productVersion)) {
compatibleExtension = extension;
}
if (!compatibleExtension) {
if (sameVersion) {
compatibleExtension = (await this.galleryService.getExtensions([{ ...extension.identifier, version: extension.version }], { targetPlatform, compatible: true }, CancellationToken.None))[0] || null;
compatibleExtension = (await this.galleryService.getExtensions([{ ...extension.identifier, version: extension.version }], { targetPlatform, compatible: true, productVersion }, CancellationToken.None))[0] || null;
} else {
compatibleExtension = await this.galleryService.getCompatibleExtension(extension, includePreRelease, targetPlatform);
compatibleExtension = await this.galleryService.getCompatibleExtension(extension, includePreRelease, targetPlatform, productVersion);
}
}
@@ -718,7 +720,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
abstract install(vsix: URI, options?: InstallOptions): Promise<ILocalExtension>;
abstract installFromLocation(location: URI, profileLocation: URI): Promise<ILocalExtension>;
abstract installExtensionsFromProfile(extensions: IExtensionIdentifier[], fromProfileLocation: URI, toProfileLocation: URI): Promise<ILocalExtension[]>;
abstract getInstalled(type?: ExtensionType, profileLocation?: URI): Promise<ILocalExtension[]>;
abstract getInstalled(type?: ExtensionType, profileLocation?: URI, productVersion?: IProductVersion): Promise<ILocalExtension[]>;
abstract copyExtensions(fromProfileLocation: URI, toProfileLocation: URI): Promise<void>;
abstract download(extension: IGalleryExtension, operation: InstallOperation, donotVerifySignature: boolean): Promise<URI>;
abstract reinstallFromGallery(extension: ILocalExtension): Promise<ILocalExtension>;
@@ -15,7 +15,7 @@ import { URI } from 'vs/base/common/uri';
import { IHeaders, IRequestContext, IRequestOptions } from 'vs/base/parts/request/common/request';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { getTargetPlatform, IExtensionGalleryService, IExtensionIdentifier, IExtensionInfo, IGalleryExtension, IGalleryExtensionAsset, IGalleryExtensionAssets, IGalleryExtensionVersion, InstallOperation, IQueryOptions, IExtensionsControlManifest, isNotWebExtensionInWebTargetPlatform, isTargetPlatformCompatible, ITranslation, SortBy, SortOrder, StatisticType, toTargetPlatform, WEB_EXTENSION_TAG, IExtensionQueryOptions, IDeprecationInfo, ISearchPrefferedResults, ExtensionGalleryError, ExtensionGalleryErrorCode } from 'vs/platform/extensionManagement/common/extensionManagement';
import { getTargetPlatform, IExtensionGalleryService, IExtensionIdentifier, IExtensionInfo, IGalleryExtension, IGalleryExtensionAsset, IGalleryExtensionAssets, IGalleryExtensionVersion, InstallOperation, IQueryOptions, IExtensionsControlManifest, isNotWebExtensionInWebTargetPlatform, isTargetPlatformCompatible, ITranslation, SortBy, SortOrder, StatisticType, toTargetPlatform, WEB_EXTENSION_TAG, IExtensionQueryOptions, IDeprecationInfo, ISearchPrefferedResults, ExtensionGalleryError, ExtensionGalleryErrorCode, IProductVersion } from 'vs/platform/extensionManagement/common/extensionManagement';
import { adoptToGalleryExtensionId, areSameExtensions, getGalleryExtensionId, getGalleryExtensionTelemetryData } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { IExtensionManifest, TargetPlatform } from 'vs/platform/extensions/common/extensions';
import { isEngineValid } from 'vs/platform/extensions/common/extensionValidator';
@@ -295,6 +295,7 @@ type GalleryServiceAdditionalQueryEvent = {
};
interface IExtensionCriteria {
readonly productVersion: IProductVersion;
readonly targetPlatform: TargetPlatform;
readonly compatible: boolean;
readonly includePreRelease: boolean | (IExtensionIdentifier & { includePreRelease: boolean })[];
@@ -662,14 +663,14 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi
query = query.withSource(options.source);
}
const { extensions } = await this.queryGalleryExtensions(query, { targetPlatform: options.targetPlatform ?? CURRENT_TARGET_PLATFORM, includePreRelease: includePreReleases, versions, compatible: !!options.compatible }, token);
const { extensions } = await this.queryGalleryExtensions(query, { targetPlatform: options.targetPlatform ?? CURRENT_TARGET_PLATFORM, includePreRelease: includePreReleases, versions, compatible: !!options.compatible, productVersion: options.productVersion ?? { version: this.productService.version, date: this.productService.date } }, token);
if (options.source) {
extensions.forEach((e, index) => setTelemetry(e, index, options.source));
}
return extensions;
}
async getCompatibleExtension(extension: IGalleryExtension, includePreRelease: boolean, targetPlatform: TargetPlatform): Promise<IGalleryExtension | null> {
async getCompatibleExtension(extension: IGalleryExtension, includePreRelease: boolean, targetPlatform: TargetPlatform, productVersion: IProductVersion = { version: this.productService.version, date: this.productService.date }): Promise<IGalleryExtension | null> {
if (isNotWebExtensionInWebTargetPlatform(extension.allTargetPlatforms, targetPlatform)) {
return null;
}
@@ -680,11 +681,11 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi
.withFlags(Flags.IncludeVersions)
.withPage(1, 1)
.withFilter(FilterType.ExtensionId, extension.identifier.uuid);
const { extensions } = await this.queryGalleryExtensions(query, { targetPlatform, compatible: true, includePreRelease }, CancellationToken.None);
const { extensions } = await this.queryGalleryExtensions(query, { targetPlatform, compatible: true, includePreRelease, productVersion }, CancellationToken.None);
return extensions[0] || null;
}
async isExtensionCompatible(extension: IGalleryExtension, includePreRelease: boolean, targetPlatform: TargetPlatform): Promise<boolean> {
async isExtensionCompatible(extension: IGalleryExtension, includePreRelease: boolean, targetPlatform: TargetPlatform, productVersion: IProductVersion = { version: this.productService.version, date: this.productService.date }): Promise<boolean> {
if (!isTargetPlatformCompatible(extension.properties.targetPlatform, extension.allTargetPlatforms, targetPlatform)) {
return false;
}
@@ -702,10 +703,10 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi
}
engine = manifest.engines.vscode;
}
return isEngineValid(engine, this.productService.version, this.productService.date);
return isEngineValid(engine, productVersion.version, productVersion.date);
}
private async isValidVersion(rawGalleryExtensionVersion: IRawGalleryExtensionVersion, versionType: 'release' | 'prerelease' | 'any', compatible: boolean, allTargetPlatforms: TargetPlatform[], targetPlatform: TargetPlatform): Promise<boolean> {
private async isValidVersion(rawGalleryExtensionVersion: IRawGalleryExtensionVersion, versionType: 'release' | 'prerelease' | 'any', compatible: boolean, allTargetPlatforms: TargetPlatform[], targetPlatform: TargetPlatform, productVersion: IProductVersion = { version: this.productService.version, date: this.productService.date }): Promise<boolean> {
if (!isTargetPlatformCompatible(getTargetPlatformForExtensionVersion(rawGalleryExtensionVersion), allTargetPlatforms, targetPlatform)) {
return false;
}
@@ -717,7 +718,7 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi
if (compatible) {
try {
const engine = await this.getEngine(rawGalleryExtensionVersion);
if (!isEngineValid(engine, this.productService.version, this.productService.date)) {
if (!isEngineValid(engine, productVersion.version, productVersion.date)) {
return false;
}
} catch (error) {
@@ -784,7 +785,7 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi
}
const runQuery = async (query: Query, token: CancellationToken) => {
const { extensions, total } = await this.queryGalleryExtensions(query, { targetPlatform: CURRENT_TARGET_PLATFORM, compatible: false, includePreRelease: !!options.includePreRelease }, token);
const { extensions, total } = await this.queryGalleryExtensions(query, { targetPlatform: CURRENT_TARGET_PLATFORM, compatible: false, includePreRelease: !!options.includePreRelease, productVersion: options.productVersion ?? { version: this.productService.version, date: this.productService.date } }, token);
extensions.forEach((e, index) => setTelemetry(e, ((query.pageNumber - 1) * query.pageSize) + index, options.source));
return { extensions, total };
};
@@ -913,7 +914,7 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi
continue;
}
// Allow any version if includePreRelease flag is set otherwise only release versions are allowed
if (await this.isValidVersion(rawGalleryExtensionVersion, includePreRelease ? 'any' : 'release', criteria.compatible, allTargetPlatforms, criteria.targetPlatform)) {
if (await this.isValidVersion(rawGalleryExtensionVersion, includePreRelease ? 'any' : 'release', criteria.compatible, allTargetPlatforms, criteria.targetPlatform, criteria.productVersion)) {
return toExtension(rawGalleryExtension, rawGalleryExtensionVersion, allTargetPlatforms, queryContext);
}
if (version && rawGalleryExtensionVersion.version === version) {
@@ -20,6 +20,11 @@ export const EXTENSION_INSTALL_SKIP_WALKTHROUGH_CONTEXT = 'skipWalkthrough';
export const EXTENSION_INSTALL_SYNC_CONTEXT = 'extensionsSync';
export const EXTENSION_INSTALL_DEP_PACK_CONTEXT = 'dependecyOrPackExtensionInstall';
export interface IProductVersion {
readonly version: string;
readonly date?: string;
}
export function TargetPlatformToString(targetPlatform: TargetPlatform) {
switch (targetPlatform) {
case TargetPlatform.WIN32_X64: return 'Windows 64 bit';
@@ -281,6 +286,7 @@ export interface IQueryOptions {
sortOrder?: SortOrder;
source?: string;
includePreRelease?: boolean;
productVersion?: IProductVersion;
}
export const enum StatisticType {
@@ -330,6 +336,7 @@ export interface IExtensionInfo extends IExtensionIdentifier {
export interface IExtensionQueryOptions {
targetPlatform?: TargetPlatform;
productVersion?: IProductVersion;
compatible?: boolean;
queryAllVersions?: boolean;
source?: string;
@@ -347,8 +354,8 @@ export interface IExtensionGalleryService {
query(options: IQueryOptions, token: CancellationToken): Promise<IPager<IGalleryExtension>>;
getExtensions(extensionInfos: ReadonlyArray<IExtensionInfo>, token: CancellationToken): Promise<IGalleryExtension[]>;
getExtensions(extensionInfos: ReadonlyArray<IExtensionInfo>, options: IExtensionQueryOptions, token: CancellationToken): Promise<IGalleryExtension[]>;
isExtensionCompatible(extension: IGalleryExtension, includePreRelease: boolean, targetPlatform: TargetPlatform): Promise<boolean>;
getCompatibleExtension(extension: IGalleryExtension, includePreRelease: boolean, targetPlatform: TargetPlatform): Promise<IGalleryExtension | null>;
isExtensionCompatible(extension: IGalleryExtension, includePreRelease: boolean, targetPlatform: TargetPlatform, productVersion?: IProductVersion): Promise<boolean>;
getCompatibleExtension(extension: IGalleryExtension, includePreRelease: boolean, targetPlatform: TargetPlatform, productVersion?: IProductVersion): Promise<IGalleryExtension | null>;
getAllCompatibleVersions(extension: IGalleryExtension, includePreRelease: boolean, targetPlatform: TargetPlatform): Promise<IGalleryExtensionVersion[]>;
download(extension: IGalleryExtension, location: URI, operation: InstallOperation): Promise<void>;
downloadSignatureArchive(extension: IGalleryExtension, location: URI): Promise<void>;
@@ -454,6 +461,7 @@ export type InstallOptions = {
operation?: InstallOperation;
profileLocation?: URI;
installOnlyNewlyAddedFromExtensionPack?: boolean;
productVersion?: IProductVersion;
/**
* Context passed through to InstallExtensionResult
*/
@@ -490,7 +498,7 @@ export interface IExtensionManagementService {
uninstall(extension: ILocalExtension, options?: UninstallOptions): Promise<void>;
toggleAppliationScope(extension: ILocalExtension, fromProfileLocation: URI): Promise<ILocalExtension>;
reinstallFromGallery(extension: ILocalExtension): Promise<ILocalExtension>;
getInstalled(type?: ExtensionType, profileLocation?: URI): Promise<ILocalExtension[]>;
getInstalled(type?: ExtensionType, profileLocation?: URI, productVersion?: IProductVersion): Promise<ILocalExtension[]>;
getExtensionsControlManifest(): Promise<IExtensionsControlManifest>;
copyExtensions(fromProfileLocation: URI, toProfileLocation: URI): Promise<void>;
updateMetadata(local: ILocalExtension, metadata: Partial<Metadata>, profileLocation?: URI): Promise<ILocalExtension>;
@@ -9,7 +9,7 @@ import { cloneAndChange } from 'vs/base/common/objects';
import { URI, UriComponents } from 'vs/base/common/uri';
import { DefaultURITransformer, IURITransformer, transformAndReviveIncomingURIs } from 'vs/base/common/uriIpc';
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
import { IExtensionIdentifier, IExtensionTipsService, IGalleryExtension, ILocalExtension, IExtensionsControlManifest, isTargetPlatformCompatible, InstallOptions, UninstallOptions, Metadata, IExtensionManagementService, DidUninstallExtensionEvent, InstallExtensionEvent, InstallExtensionResult, UninstallExtensionEvent, InstallOperation, InstallExtensionInfo } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionIdentifier, IExtensionTipsService, IGalleryExtension, ILocalExtension, IExtensionsControlManifest, isTargetPlatformCompatible, InstallOptions, UninstallOptions, Metadata, IExtensionManagementService, DidUninstallExtensionEvent, InstallExtensionEvent, InstallExtensionResult, UninstallExtensionEvent, InstallOperation, InstallExtensionInfo, IProductVersion } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionType, IExtensionManifest, TargetPlatform } from 'vs/platform/extensions/common/extensions';
function transformIncomingURI(uri: UriComponents, transformer: IURITransformer | null): URI;
@@ -139,7 +139,7 @@ export class ExtensionManagementChannel implements IServerChannel {
return this.service.reinstallFromGallery(transformIncomingExtension(args[0], uriTransformer));
}
case 'getInstalled': {
const extensions = await this.service.getInstalled(args[0], transformIncomingURI(args[1], uriTransformer));
const extensions = await this.service.getInstalled(args[0], transformIncomingURI(args[1], uriTransformer), args[2]);
return extensions.map(e => transformOutgoingExtension(e, uriTransformer));
}
case 'toggleAppliationScope': {
@@ -271,8 +271,8 @@ export class ExtensionManagementChannelClient extends Disposable implements IExt
return Promise.resolve(this.channel.call<ILocalExtension>('reinstallFromGallery', [extension])).then(local => transformIncomingExtension(local, null));
}
getInstalled(type: ExtensionType | null = null, extensionsProfileResource?: URI): Promise<ILocalExtension[]> {
return Promise.resolve(this.channel.call<ILocalExtension[]>('getInstalled', [type, extensionsProfileResource]))
getInstalled(type: ExtensionType | null = null, extensionsProfileResource?: URI, productVersion?: IProductVersion): Promise<ILocalExtension[]> {
return Promise.resolve(this.channel.call<ILocalExtension[]>('getInstalled', [type, extensionsProfileResource, productVersion]))
.then(extensions => extensions.map(extension => transformIncomingExtension(extension, null)));
}
@@ -83,7 +83,7 @@ export interface IExtensionsProfileScannerService {
readonly onDidRemoveExtensions: Event<DidRemoveProfileExtensionsEvent>;
scanProfileExtensions(profileLocation: URI, options?: IProfileExtensionsScanOptions): Promise<IScannedProfileExtension[]>;
addExtensionsToProfile(extensions: [IExtension, Metadata | undefined][], profileLocation: URI): Promise<IScannedProfileExtension[]>;
addExtensionsToProfile(extensions: [IExtension, Metadata | undefined][], profileLocation: URI, keepExistingVersions?: boolean): Promise<IScannedProfileExtension[]>;
updateMetadata(extensions: [IExtension, Metadata | undefined][], profileLocation: URI): Promise<IScannedProfileExtension[]>;
removeExtensionFromProfile(extension: IExtension, profileLocation: URI): Promise<void>;
}
@@ -120,18 +120,22 @@ export abstract class AbstractExtensionsProfileScannerService extends Disposable
return this.withProfileExtensions(profileLocation, undefined, options);
}
async addExtensionsToProfile(extensions: [IExtension, Metadata | undefined][], profileLocation: URI): Promise<IScannedProfileExtension[]> {
async addExtensionsToProfile(extensions: [IExtension, Metadata | undefined][], profileLocation: URI, keepExistingVersions?: boolean): Promise<IScannedProfileExtension[]> {
const extensionsToRemove: IScannedProfileExtension[] = [];
const extensionsToAdd: IScannedProfileExtension[] = [];
try {
await this.withProfileExtensions(profileLocation, existingExtensions => {
const result: IScannedProfileExtension[] = [];
for (const existing of existingExtensions) {
if (extensions.some(([e]) => areSameExtensions(e.identifier, existing.identifier) && e.manifest.version !== existing.version)) {
// Remove the existing extension with different version
extensionsToRemove.push(existing);
} else {
result.push(existing);
if (keepExistingVersions) {
result.push(...existingExtensions);
} else {
for (const existing of existingExtensions) {
if (extensions.some(([e]) => areSameExtensions(e.identifier, existing.identifier) && e.manifest.version !== existing.version)) {
// Remove the existing extension with different version
extensionsToRemove.push(existing);
} else {
result.push(existing);
}
}
}
for (const [extension, metadata] of extensions) {
@@ -22,7 +22,7 @@ import { isEmptyObject } from 'vs/base/common/types';
import { URI } from 'vs/base/common/uri';
import { localize } from 'vs/nls';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { Metadata } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IProductVersion, Metadata } from 'vs/platform/extensionManagement/common/extensionManagement';
import { areSameExtensions, computeTargetPlatform, ExtensionKey, getExtensionId, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ExtensionType, ExtensionIdentifier, IExtensionManifest, TargetPlatform, IExtensionIdentifier, IRelaxedExtensionManifest, UNDEFINED_PUBLISHER, IExtensionDescription, BUILTIN_MANIFEST_CACHE_FILE, USER_MANIFEST_CACHE_FILE, ExtensionIdentifierMap } from 'vs/platform/extensions/common/extensions';
import { validateExtensionManifest } from 'vs/platform/extensions/common/extensionValidator';
@@ -108,6 +108,7 @@ export type ScanOptions = {
readonly checkControlFile?: boolean;
readonly language?: string;
readonly useCache?: boolean;
readonly productVersion?: IProductVersion;
};
export const IExtensionsScannerService = createDecorator<IExtensionsScannerService>('IExtensionsScannerService');
@@ -195,7 +196,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem
const location = scanOptions.profileLocation ?? this.userExtensionsLocation;
this.logService.trace('Started scanning user extensions', location);
const profileScanOptions: IProfileExtensionsScanOptions | undefined = this.uriIdentityService.extUri.isEqual(scanOptions.profileLocation, this.userDataProfilesService.defaultProfile.extensionsResource) ? { bailOutWhenFileNotFound: true } : undefined;
const extensionsScannerInput = await this.createExtensionScannerInput(location, !!scanOptions.profileLocation, ExtensionType.User, !scanOptions.includeUninstalled, scanOptions.language, true, profileScanOptions);
const extensionsScannerInput = await this.createExtensionScannerInput(location, !!scanOptions.profileLocation, ExtensionType.User, !scanOptions.includeUninstalled, scanOptions.language, true, profileScanOptions, scanOptions.productVersion ?? this.getProductVersion());
const extensionsScanner = scanOptions.useCache && !extensionsScannerInput.devMode && extensionsScannerInput.excludeObsolete ? this.userExtensionsCachedScanner : this.extensionsScanner;
let extensions: IRelaxedScannedExtension[];
try {
@@ -217,7 +218,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem
if (this.environmentService.isExtensionDevelopment && this.environmentService.extensionDevelopmentLocationURI) {
const extensions = (await Promise.all(this.environmentService.extensionDevelopmentLocationURI.filter(extLoc => extLoc.scheme === Schemas.file)
.map(async extensionDevelopmentLocationURI => {
const input = await this.createExtensionScannerInput(extensionDevelopmentLocationURI, false, ExtensionType.User, true, scanOptions.language, false /* do not validate */, undefined);
const input = await this.createExtensionScannerInput(extensionDevelopmentLocationURI, false, ExtensionType.User, true, scanOptions.language, false /* do not validate */, undefined, scanOptions.productVersion ?? this.getProductVersion());
const extensions = await this.extensionsScanner.scanOneOrMultipleExtensions(input);
return extensions.map(extension => {
// Override the extension type from the existing extensions
@@ -233,7 +234,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem
}
async scanExistingExtension(extensionLocation: URI, extensionType: ExtensionType, scanOptions: ScanOptions): Promise<IScannedExtension | null> {
const extensionsScannerInput = await this.createExtensionScannerInput(extensionLocation, false, extensionType, true, scanOptions.language, true, undefined);
const extensionsScannerInput = await this.createExtensionScannerInput(extensionLocation, false, extensionType, true, scanOptions.language, true, undefined, scanOptions.productVersion ?? this.getProductVersion());
const extension = await this.extensionsScanner.scanExtension(extensionsScannerInput);
if (!extension) {
return null;
@@ -245,7 +246,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem
}
async scanOneOrMultipleExtensions(extensionLocation: URI, extensionType: ExtensionType, scanOptions: ScanOptions): Promise<IScannedExtension[]> {
const extensionsScannerInput = await this.createExtensionScannerInput(extensionLocation, false, extensionType, true, scanOptions.language, true, undefined);
const extensionsScannerInput = await this.createExtensionScannerInput(extensionLocation, false, extensionType, true, scanOptions.language, true, undefined, scanOptions.productVersion ?? this.getProductVersion());
const extensions = await this.extensionsScanner.scanOneOrMultipleExtensions(extensionsScannerInput);
return this.applyScanOptions(extensions, extensionType, scanOptions, true);
}
@@ -392,7 +393,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem
private async scanDefaultSystemExtensions(useCache: boolean, language: string | undefined): Promise<IRelaxedScannedExtension[]> {
this.logService.trace('Started scanning system extensions');
const extensionsScannerInput = await this.createExtensionScannerInput(this.systemExtensionsLocation, false, ExtensionType.System, true, language, true, undefined);
const extensionsScannerInput = await this.createExtensionScannerInput(this.systemExtensionsLocation, false, ExtensionType.System, true, language, true, undefined, this.getProductVersion());
const extensionsScanner = useCache && !extensionsScannerInput.devMode ? this.systemExtensionsCachedScanner : this.extensionsScanner;
const result = await extensionsScanner.scanExtensions(extensionsScannerInput);
this.logService.trace('Scanned system extensions:', result.length);
@@ -422,7 +423,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem
break;
}
}
const result = await Promise.all(devSystemExtensionsLocations.map(async location => this.extensionsScanner.scanExtension((await this.createExtensionScannerInput(location, false, ExtensionType.System, true, language, true, undefined)))));
const result = await Promise.all(devSystemExtensionsLocations.map(async location => this.extensionsScanner.scanExtension((await this.createExtensionScannerInput(location, false, ExtensionType.System, true, language, true, undefined, this.getProductVersion())))));
this.logService.trace('Scanned dev system extensions:', result.length);
return coalesce(result);
}
@@ -436,7 +437,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem
}
}
private async createExtensionScannerInput(location: URI, profile: boolean, type: ExtensionType, excludeObsolete: boolean, language: string | undefined, validate: boolean, profileScanOptions: IProfileExtensionsScanOptions | undefined): Promise<ExtensionScannerInput> {
private async createExtensionScannerInput(location: URI, profile: boolean, type: ExtensionType, excludeObsolete: boolean, language: string | undefined, validate: boolean, profileScanOptions: IProfileExtensionsScanOptions | undefined, productVersion: IProductVersion): Promise<ExtensionScannerInput> {
const translations = await this.getTranslations(language ?? platform.language);
const mtime = await this.getMtime(location);
const applicationExtensionsLocation = profile && !this.uriIdentityService.extUri.isEqual(location, this.userDataProfilesService.defaultProfile.extensionsResource) ? this.userDataProfilesService.defaultProfile.extensionsResource : undefined;
@@ -451,8 +452,8 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem
type,
excludeObsolete,
validate,
this.productService.version,
this.productService.date,
productVersion.version,
productVersion.date,
this.productService.commit,
!this.environmentService.isBuilt,
language,
@@ -472,6 +473,13 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem
return undefined;
}
private getProductVersion(): IProductVersion {
return {
version: this.productService.version,
date: this.productService.date,
};
}
}
export class ExtensionScannerInput {
@@ -28,7 +28,8 @@ import { INativeEnvironmentService } from 'vs/platform/environment/common/enviro
import { AbstractExtensionManagementService, AbstractExtensionTask, ExtensionVerificationStatus, IInstallExtensionTask, InstallExtensionTaskOptions, IUninstallExtensionTask, joinErrors, toExtensionManagementError, UninstallExtensionTaskOptions } from 'vs/platform/extensionManagement/common/abstractExtensionManagementService';
import {
ExtensionManagementError, ExtensionManagementErrorCode, IExtensionGalleryService, IExtensionIdentifier, IExtensionManagementService, IGalleryExtension, ILocalExtension, InstallOperation,
Metadata, InstallOptions
Metadata, InstallOptions,
IProductVersion
} from 'vs/platform/extensionManagement/common/extensionManagement';
import { areSameExtensions, computeTargetPlatform, ExtensionKey, getGalleryExtensionId, groupByExtension } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { IExtensionsProfileScannerService, IScannedProfileExtension } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService';
@@ -128,8 +129,8 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi
}
}
getInstalled(type?: ExtensionType, profileLocation: URI = this.userDataProfilesService.defaultProfile.extensionsResource): Promise<ILocalExtension[]> {
return this.extensionsScanner.scanExtensions(type ?? null, profileLocation);
getInstalled(type?: ExtensionType, profileLocation: URI = this.userDataProfilesService.defaultProfile.extensionsResource, productVersion: IProductVersion = { version: this.productService.version, date: this.productService.date }): Promise<ILocalExtension[]> {
return this.extensionsScanner.scanExtensions(type ?? null, profileLocation, productVersion);
}
scanAllUserInstalledExtensions(): Promise<ILocalExtension[]> {
@@ -179,7 +180,7 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi
async installExtensionsFromProfile(extensions: IExtensionIdentifier[], fromProfileLocation: URI, toProfileLocation: URI): Promise<ILocalExtension[]> {
this.logService.trace('ExtensionManagementService#installExtensionsFromProfile', extensions, fromProfileLocation.toString(), toProfileLocation.toString());
const extensionsToInstall = (await this.extensionsScanner.scanExtensions(ExtensionType.User, fromProfileLocation)).filter(e => extensions.some(id => areSameExtensions(id, e.identifier)));
const extensionsToInstall = (await this.getInstalled(ExtensionType.User, fromProfileLocation)).filter(e => extensions.some(id => areSameExtensions(id, e.identifier)));
if (extensionsToInstall.length) {
const metadata = await Promise.all(extensionsToInstall.map(e => this.extensionsScanner.scanMetadata(e, fromProfileLocation)));
await this.addExtensionsToProfile(extensionsToInstall.map((e, index) => [e, metadata[index]]), toProfileLocation);
@@ -236,7 +237,7 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi
}
copyExtensions(fromProfileLocation: URI, toProfileLocation: URI): Promise<void> {
return this.extensionsScanner.copyExtensions(fromProfileLocation, toProfileLocation);
return this.extensionsScanner.copyExtensions(fromProfileLocation, toProfileLocation, { version: this.productService.version, date: this.productService.date });
}
markAsUninstalled(...extensions: IExtension[]): Promise<void> {
@@ -333,7 +334,7 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi
}
}
if (added) {
const extensions = await this.extensionsScanner.scanExtensions(ExtensionType.User, added.profileLocation);
const extensions = await this.getInstalled(ExtensionType.User, added.profileLocation);
const addedExtensions = extensions.filter(e => added.extensions.some(identifier => areSameExtensions(identifier, e.identifier)));
this._onDidInstallExtensions.fire(addedExtensions.map(local => {
this.logService.info('Extensions added from another source', local.identifier.id, added.profileLocation.toString());
@@ -449,8 +450,8 @@ export class ExtensionsScanner extends Disposable {
await this.removeUninstalledExtensions();
}
async scanExtensions(type: ExtensionType | null, profileLocation: URI): Promise<ILocalExtension[]> {
const userScanOptions: ScanOptions = { includeInvalid: true, profileLocation };
async scanExtensions(type: ExtensionType | null, profileLocation: URI, productVersion: IProductVersion): Promise<ILocalExtension[]> {
const userScanOptions: ScanOptions = { includeInvalid: true, profileLocation, productVersion };
let scannedExtensions: IScannedExtension[] = [];
if (type === null || type === ExtensionType.System) {
scannedExtensions.push(...await this.extensionsScannerService.scanAllExtensions({ includeInvalid: true }, userScanOptions, false));
@@ -613,8 +614,8 @@ export class ExtensionsScanner extends Disposable {
return this.scanLocalExtension(extension.location, extension.type, toProfileLocation);
}
async copyExtensions(fromProfileLocation: URI, toProfileLocation: URI): Promise<void> {
const fromExtensions = await this.scanExtensions(ExtensionType.User, fromProfileLocation);
async copyExtensions(fromProfileLocation: URI, toProfileLocation: URI, productVersion: IProductVersion): Promise<void> {
const fromExtensions = await this.scanExtensions(ExtensionType.User, fromProfileLocation, productVersion);
const extensions: [ILocalExtension, Metadata | undefined][] = await Promise.all(fromExtensions
.filter(e => !e.isApplicationScoped) /* remove application scoped extensions */
.map(async e => ([e, await this.scanMetadata(e, fromProfileLocation)])));
@@ -819,7 +820,7 @@ abstract class InstallExtensionTask extends AbstractExtensionTask<ILocalExtensio
if (this.uriIdentityService.extUri.isEqual(this.userDataProfilesService.defaultProfile.extensionsResource, this._profileLocation)) {
await this.extensionsScannerService.initializeDefaultProfileExtensions();
}
await this.extensionsProfileScannerService.addExtensionsToProfile([[local, metadata]], this._profileLocation);
await this.extensionsProfileScannerService.addExtensionsToProfile([[local, metadata]], this._profileLocation, !local.isValid);
return local;
}
@@ -879,7 +880,7 @@ export class InstallGalleryExtensionTask extends InstallExtensionTask {
protected async install(token: CancellationToken): Promise<[ILocalExtension, Metadata]> {
let installed;
try {
installed = await this.extensionsScanner.scanExtensions(null, this.options.profileLocation);
installed = await this.extensionsScanner.scanExtensions(null, this.options.profileLocation, this.options.productVersion);
} catch (error) {
throw new ExtensionManagementError(error, ExtensionManagementErrorCode.Scanning);
}
@@ -969,7 +970,7 @@ class InstallVSIXTask extends InstallExtensionTask {
protected async install(token: CancellationToken): Promise<[ILocalExtension, Metadata]> {
const extensionKey = new ExtensionKey(this.identifier, this.manifest.version);
const installedExtensions = await this.extensionsScanner.scanExtensions(ExtensionType.User, this.options.profileLocation);
const installedExtensions = await this.extensionsScanner.scanExtensions(ExtensionType.User, this.options.profileLocation, this.options.productVersion);
const existing = installedExtensions.find(i => areSameExtensions(this.identifier, i.identifier));
const metadata: Metadata = {
isApplicationScoped: this.options.isApplicationScoped || existing?.isApplicationScoped,
@@ -102,7 +102,7 @@ class TestInstallGalleryExtensionTask extends InstallGalleryExtensionTask {
engines: { vscode: '*' },
},
extension,
{ profileLocation: userDataProfilesService.defaultProfile.extensionsResource },
{ profileLocation: userDataProfilesService.defaultProfile.extensionsResource, productVersion: { version: '' } },
extensionDownloader,
new TestExtensionsScanner(),
uriIdentityService,
+1 -1
View File
@@ -236,7 +236,7 @@ export interface IHoverTarget extends IDisposable {
export class WorkbenchHoverDelegate extends Disposable implements IHoverDelegate {
private lastHoverHideTime = Number.MAX_VALUE;
private lastHoverHideTime = 0;
private timeLimit = 200;
private _delay: number;
@@ -6,7 +6,7 @@
import { timeout } from 'vs/base/common/async';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { Disposable, DisposableStore, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle';
import { IKeyMods, IQuickPickDidAcceptEvent, IQuickPickSeparator, IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { IKeyMods, IQuickPickDidAcceptEvent, IQuickPickSeparator, IQuickPick, IQuickPickItem, IQuickInputButton } from 'vs/platform/quickinput/common/quickInput';
import { IQuickAccessProvider, IQuickAccessProviderRunOptions } from 'vs/platform/quickinput/common/quickAccess';
import { isFunction } from 'vs/base/common/types';
@@ -59,6 +59,22 @@ export interface IPickerQuickAccessItem extends IQuickPickItem {
trigger?(buttonIndex: number, keyMods: IKeyMods): TriggerAction | Promise<TriggerAction>;
}
export interface IPickerQuickAccessSeparator extends IQuickPickSeparator {
/**
* A method that will be executed when a button of the pick item was
* clicked on.
*
* @param buttonIndex index of the button of the item that
* was clicked.
*
* @param the state of modifier keys when the button was triggered.
*
* @returns a value that indicates what should happen after the trigger
* which can be a `Promise` for long running operations.
*/
trigger?(buttonIndex: number, keyMods: IKeyMods): TriggerAction | Promise<TriggerAction>;
}
export interface IPickerQuickAccessProviderOptions<T extends IPickerQuickAccessItem> {
/**
@@ -320,47 +336,52 @@ export abstract class PickerQuickAccessProvider<T extends IPickerQuickAccessItem
}
}));
// Trigger the pick with button index if button triggered
disposables.add(picker.onDidTriggerItemButton(async ({ button, item }) => {
if (typeof item.trigger === 'function') {
const buttonIndex = item.buttons?.indexOf(button) ?? -1;
if (buttonIndex >= 0) {
const result = item.trigger(buttonIndex, picker.keyMods);
const action = (typeof result === 'number') ? result : await result;
const buttonTrigger = async (button: IQuickInputButton, item: T | IPickerQuickAccessSeparator) => {
if (typeof item.trigger !== 'function') {
return;
}
if (token.isCancellationRequested) {
return;
}
const buttonIndex = item.buttons?.indexOf(button) ?? -1;
if (buttonIndex >= 0) {
const result = item.trigger(buttonIndex, picker.keyMods);
const action = (typeof result === 'number') ? result : await result;
switch (action) {
case TriggerAction.NO_ACTION:
break;
case TriggerAction.CLOSE_PICKER:
picker.hide();
break;
case TriggerAction.REFRESH_PICKER:
updatePickerItems();
break;
case TriggerAction.REMOVE_ITEM: {
const index = picker.items.indexOf(item);
if (index !== -1) {
const items = picker.items.slice();
const removed = items.splice(index, 1);
const activeItems = picker.activeItems.filter(activeItem => activeItem !== removed[0]);
const keepScrollPositionBefore = picker.keepScrollPosition;
picker.keepScrollPosition = true;
picker.items = items;
if (activeItems) {
picker.activeItems = activeItems;
}
picker.keepScrollPosition = keepScrollPositionBefore;
if (token.isCancellationRequested) {
return;
}
switch (action) {
case TriggerAction.NO_ACTION:
break;
case TriggerAction.CLOSE_PICKER:
picker.hide();
break;
case TriggerAction.REFRESH_PICKER:
updatePickerItems();
break;
case TriggerAction.REMOVE_ITEM: {
const index = picker.items.indexOf(item);
if (index !== -1) {
const items = picker.items.slice();
const removed = items.splice(index, 1);
const activeItems = picker.activeItems.filter(activeItem => activeItem !== removed[0]);
const keepScrollPositionBefore = picker.keepScrollPosition;
picker.keepScrollPosition = true;
picker.items = items;
if (activeItems) {
picker.activeItems = activeItems;
}
break;
picker.keepScrollPosition = keepScrollPositionBefore;
}
break;
}
}
}
}));
};
// Trigger the pick with button index if button triggered
disposables.add(picker.onDidTriggerItemButton(({ button, item }) => buttonTrigger(button, item)));
disposables.add(picker.onDidTriggerSeparatorButton(({ button, separator }) => buttonTrigger(button, separator)));
return disposables;
}
@@ -162,6 +162,7 @@ class QuickInput extends Disposable implements IQuickInput {
private _lastSeverity: Severity | undefined;
private readonly onDidTriggerButtonEmitter = this._register(new Emitter<IQuickInputButton>());
private readonly onDidHideEmitter = this._register(new Emitter<IQuickInputHideEvent>());
private readonly onWillHideEmitter = this._register(new Emitter<IQuickInputHideEvent>());
private readonly onDisposeEmitter = this._register(new Emitter<void>());
protected readonly visibleDisposables = this._register(new DisposableStore());
@@ -352,6 +353,11 @@ class QuickInput extends Disposable implements IQuickInput {
readonly onDidHide = this.onDidHideEmitter.event;
willHide(reason = QuickInputHideReason.Other): void {
this.onWillHideEmitter.fire({ reason });
}
readonly onWillHide = this.onWillHideEmitter.event;
protected update() {
if (!this.visible) {
return;
@@ -614,6 +614,7 @@ export class QuickInputController extends Disposable {
if (!controller) {
return;
}
controller.willHide(reason);
const container = this.ui?.container;
const focusChanged = container && !dom.isAncestorOfActiveElement(container);
@@ -209,6 +209,11 @@ export interface IQuickInput extends IDisposable {
*/
readonly onDidHide: Event<IQuickInputHideEvent>;
/**
* An event that is fired when the quick input will be hidden.
*/
readonly onWillHide: Event<IQuickInputHideEvent>;
/**
* An event that is fired when the quick input is disposed.
*/
@@ -285,6 +290,12 @@ export interface IQuickInput extends IDisposable {
* @param reason The reason why the quick input was hidden.
*/
didHide(reason?: QuickInputHideReason): void;
/**
* Notifies that the quick input will be hidden.
* @param reason The reason why the quick input will be hidden.
*/
willHide(reason?: QuickInputHideReason): void;
}
export interface IQuickWidget extends IQuickInput {
@@ -6,7 +6,7 @@
import { IpcMainEvent, MessagePortMain } from 'electron';
import { validatedIpcMain } from 'vs/base/parts/ipc/electron-main/ipcMain';
import { Barrier, DeferredPromise } from 'vs/base/common/async';
import { Disposable } from 'vs/base/common/lifecycle';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService';
import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
import { ILogService } from 'vs/platform/log/common/log';
@@ -25,6 +25,7 @@ export class SharedProcess extends Disposable {
private readonly firstWindowConnectionBarrier = new Barrier();
private utilityProcess: UtilityProcess | undefined = undefined;
private utilityProcessLogListener: IDisposable | undefined = undefined;
constructor(
private readonly machineId: string,
@@ -104,13 +105,10 @@ export class SharedProcess extends Disposable {
// all services within have been created.
const whenReady = new DeferredPromise<void>();
if (this.utilityProcess) {
this.utilityProcess.once(SharedProcessLifecycle.initDone, () => whenReady.complete());
} else {
validatedIpcMain.once(SharedProcessLifecycle.initDone, () => whenReady.complete());
}
this.utilityProcess?.once(SharedProcessLifecycle.initDone, () => whenReady.complete());
await whenReady.p;
this.utilityProcessLogListener?.dispose();
this.logService.trace('[SharedProcess] Overall ready');
})();
}
@@ -131,11 +129,7 @@ export class SharedProcess extends Disposable {
// Wait for shared process indicating that IPC connections are accepted
const sharedProcessIpcReady = new DeferredPromise<void>();
if (this.utilityProcess) {
this.utilityProcess.once(SharedProcessLifecycle.ipcReady, () => sharedProcessIpcReady.complete());
} else {
validatedIpcMain.once(SharedProcessLifecycle.ipcReady, () => sharedProcessIpcReady.complete());
}
this.utilityProcess?.once(SharedProcessLifecycle.ipcReady, () => sharedProcessIpcReady.complete());
await sharedProcessIpcReady.p;
this.logService.trace('[SharedProcess] IPC ready');
@@ -148,6 +142,15 @@ export class SharedProcess extends Disposable {
private createUtilityProcess(): void {
this.utilityProcess = this._register(new UtilityProcess(this.logService, NullTelemetryService, this.lifecycleMainService));
// Install a log listener for very early shared process warnings and errors
this.utilityProcessLogListener = this.utilityProcess.onMessage((e: any) => {
if (typeof e.warning === 'string') {
this.logService.warn(e.warning);
} else if (typeof e.error === 'string') {
this.logService.error(e.error);
}
});
const inspectParams = parseSharedProcessDebugPort(this.environmentMainService.args, this.environmentMainService.isBuilt);
let execArgv: string[] | undefined = undefined;
if (inspectParams.port) {
@@ -122,6 +122,7 @@ export const enum TerminalSettingId {
StickyScrollEnabled = 'terminal.integrated.stickyScroll.enabled',
StickyScrollMaxLineCount = 'terminal.integrated.stickyScroll.maxLineCount',
MouseWheelZoom = 'terminal.integrated.mouseWheelZoom',
ExperimentalInlineChat = 'workbench.action.terminal.experimentalInlineChat',
// Debug settings that are hidden from user
@@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { OperatingSystem, OS } from 'vs/base/common/platform';
import type { IShellLaunchConfig } from 'vs/platform/terminal/common/terminal';
/**
* Aggressively escape non-windows paths to prepare for being sent to a shell. This will do some
@@ -59,3 +60,11 @@ export function sanitizeCwd(cwd: string): string {
}
return cwd;
}
/**
* Determines whether the given shell launch config should use the environment variable collection.
* @param slc The shell launch config to check.
*/
export function shouldUseEnvironmentVariableCollection(slc: IShellLaunchConfig): boolean {
return !slc.strictEnv && !slc.hideFromUser;
}
+3 -2
View File
@@ -9,6 +9,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
export interface IUpdate {
version: string;
productVersion: string;
timestamp?: number;
url?: string;
sha256hash?: string;
}
@@ -63,7 +64,7 @@ export type Disabled = { type: StateType.Disabled; reason: DisablementReason };
export type Idle = { type: StateType.Idle; updateType: UpdateType; error?: string };
export type CheckingForUpdates = { type: StateType.CheckingForUpdates; explicit: boolean };
export type AvailableForDownload = { type: StateType.AvailableForDownload; update: IUpdate };
export type Downloading = { type: StateType.Downloading; update: IUpdate };
export type Downloading = { type: StateType.Downloading };
export type Downloaded = { type: StateType.Downloaded; update: IUpdate };
export type Updating = { type: StateType.Updating; update: IUpdate };
export type Ready = { type: StateType.Ready; update: IUpdate };
@@ -76,7 +77,7 @@ export const State = {
Idle: (updateType: UpdateType, error?: string) => ({ type: StateType.Idle, updateType, error }) as Idle,
CheckingForUpdates: (explicit: boolean) => ({ type: StateType.CheckingForUpdates, explicit } as CheckingForUpdates),
AvailableForDownload: (update: IUpdate) => ({ type: StateType.AvailableForDownload, update } as AvailableForDownload),
Downloading: (update: IUpdate) => ({ type: StateType.Downloading, update } as Downloading),
Downloading: { type: StateType.Downloading } as Downloading,
Downloaded: (update: IUpdate) => ({ type: StateType.Downloaded, update } as Downloaded),
Updating: (update: IUpdate) => ({ type: StateType.Updating, update } as Updating),
Ready: (update: IUpdate) => ({ type: StateType.Ready, update } as Ready),
@@ -24,8 +24,8 @@ export class DarwinUpdateService extends AbstractUpdateService implements IRelau
@memoize private get onRawError(): Event<string> { return Event.fromNodeEventEmitter(electron.autoUpdater, 'error', (_, message) => message); }
@memoize private get onRawUpdateNotAvailable(): Event<void> { return Event.fromNodeEventEmitter<void>(electron.autoUpdater, 'update-not-available'); }
@memoize private get onRawUpdateAvailable(): Event<IUpdate> { return Event.fromNodeEventEmitter(electron.autoUpdater, 'update-available', (_, url, version) => ({ url, version, productVersion: version })); }
@memoize private get onRawUpdateDownloaded(): Event<IUpdate> { return Event.fromNodeEventEmitter(electron.autoUpdater, 'update-downloaded', (_, releaseNotes, version, date) => ({ releaseNotes, version, productVersion: version, date })); }
@memoize private get onRawUpdateAvailable(): Event<void> { return Event.fromNodeEventEmitter(electron.autoUpdater, 'update-available'); }
@memoize private get onRawUpdateDownloaded(): Event<IUpdate> { return Event.fromNodeEventEmitter(electron.autoUpdater, 'update-downloaded', (_, releaseNotes, version, timestamp) => ({ version, productVersion: version, timestamp })); }
constructor(
@ILifecycleMainService lifecycleMainService: ILifecycleMainService,
@@ -96,12 +96,12 @@ export class DarwinUpdateService extends AbstractUpdateService implements IRelau
electron.autoUpdater.checkForUpdates();
}
private onUpdateAvailable(update: IUpdate): void {
private onUpdateAvailable(): void {
if (this.state.type !== StateType.CheckingForUpdates) {
return;
}
this.setState(State.Downloading(update));
this.setState(State.Downloading);
}
private onUpdateDownloaded(update: IUpdate): void {
@@ -135,7 +135,7 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun
return Promise.resolve(null);
}
this.setState(State.Downloading(update));
this.setState(State.Downloading);
return this.cleanup(update.version).then(() => {
return this.getUpdatePackagePath(update.version).then(updatePackagePath => {
@@ -153,15 +153,13 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun
.then(() => updatePackagePath);
});
}).then(packagePath => {
const fastUpdatesEnabled = this.configurationService.getValue('update.enableWindowsBackgroundUpdates');
this.availableUpdate = { packagePath };
this.setState(State.Downloaded(update));
const fastUpdatesEnabled = this.configurationService.getValue('update.enableWindowsBackgroundUpdates');
if (fastUpdatesEnabled) {
if (this.productService.target === 'user') {
this.doApplyUpdate();
} else {
this.setState(State.Downloaded(update));
}
} else {
this.setState(State.Ready(update));
@@ -209,7 +207,7 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun
}
protected override async doApplyUpdate(): Promise<void> {
if (this.state.type !== StateType.Downloaded && this.state.type !== StateType.Downloading) {
if (this.state.type !== StateType.Downloaded) {
return Promise.resolve(undefined);
}
@@ -273,14 +271,13 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun
const fastUpdatesEnabled = this.configurationService.getValue('update.enableWindowsBackgroundUpdates');
const update: IUpdate = { version: 'unknown', productVersion: 'unknown' };
this.setState(State.Downloading(update));
this.setState(State.Downloading);
this.availableUpdate = { packagePath };
this.setState(State.Downloaded(update));
if (fastUpdatesEnabled) {
if (this.productService.target === 'user') {
this.doApplyUpdate();
} else {
this.setState(State.Downloaded(update));
}
} else {
this.setState(State.Ready(update));
@@ -212,7 +212,10 @@ export class UtilityProcess extends Disposable {
const started = this.doStart(configuration);
if (started && configuration.payload) {
this.postMessage(configuration.payload);
const posted = this.postMessage(configuration.payload);
if (posted) {
this.log('payload sent via postMessage()', Severity.Info);
}
}
return started;
@@ -363,12 +366,14 @@ export class UtilityProcess extends Disposable {
}));
}
postMessage(message: unknown, transfer?: Electron.MessagePortMain[]): void {
postMessage(message: unknown, transfer?: Electron.MessagePortMain[]): boolean {
if (!this.process) {
return; // already killed, crashed or never started
return false; // already killed, crashed or never started
}
this.process.postMessage(message, transfer);
return true;
}
connect(payload?: unknown): Electron.MessagePortMain {
@@ -975,6 +975,13 @@ export class CodeWindow extends BaseWindow implements ICodeWindow {
const proxyBypassRules = newNoProxy ? `${newNoProxy},<local>` : '<local>';
this.logService.trace(`Setting proxy to '${proxyRules}', bypassing '${proxyBypassRules}'`);
this._win.webContents.session.setProxy({ proxyRules, proxyBypassRules, pacScript: '' });
type appWithProxySupport = Electron.App & {
setProxy(config: Electron.Config): Promise<void>;
resolveProxy(url: string): Promise<string>;
};
if (typeof (app as appWithProxySupport).setProxy === 'function') {
(app as appWithProxySupport).setProxy({ proxyRules, proxyBypassRules, pacScript: '' });
}
}
}
}
+2 -1
View File
@@ -32,6 +32,7 @@ import { IExtensionManagementService } from 'vs/platform/extensionManagement/com
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ILogService } from 'vs/platform/log/common/log';
import { promiseWithResolvers } from 'vs/base/common/async';
import { shouldUseEnvironmentVariableCollection } from 'vs/platform/terminal/common/terminalEnvironment';
class CustomVariableResolver extends AbstractVariableResolverService {
constructor(
@@ -235,7 +236,7 @@ export class RemoteTerminalChannel extends Disposable implements IServerChannel<
);
// Apply extension environment variable collections to the environment
if (!shellLaunchConfig.strictEnv) {
if (shouldUseEnvironmentVariableCollection(shellLaunchConfig)) {
const entries: [string, IEnvironmentVariableCollection][] = [];
for (const [k, v, d] of args.envVariableCollections) {
entries.push([k, { map: deserializeEnvironmentVariableCollection(v), descriptionMap: deserializeEnvironmentDescriptionMap(d) }]);
@@ -6,18 +6,32 @@
import { Disposable, DisposableMap } from 'vs/base/common/lifecycle';
import * as nls from 'vs/nls';
import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers';
import { getAuthenticationProviderActivationEvent, addAccountUsage } from 'vs/workbench/services/authentication/browser/authenticationService';
import { IAuthenticationCreateSessionOptions, AuthenticationSession, AuthenticationSessionsChangeEvent, IAuthenticationProvider, IAuthenticationService } from 'vs/workbench/services/authentication/common/authentication';
import { IAuthenticationCreateSessionOptions, AuthenticationSession, AuthenticationSessionsChangeEvent, IAuthenticationProvider, IAuthenticationService, IAuthenticationExtensionsService } from 'vs/workbench/services/authentication/common/authentication';
import { ExtHostAuthenticationShape, ExtHostContext, MainContext, MainThreadAuthenticationShape } from '../common/extHost.protocol';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IDialogService, IPromptButton } from 'vs/platform/dialogs/common/dialogs';
import Severity from 'vs/base/common/severity';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { ActivationKind, IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import type { AuthenticationGetSessionOptions } from 'vscode';
import { Emitter, Event } from 'vs/base/common/event';
import { IAuthenticationAccessService } from 'vs/workbench/services/authentication/browser/authenticationAccessService';
import { IAuthenticationUsageService } from 'vs/workbench/services/authentication/browser/authenticationUsageService';
import { getAuthenticationProviderActivationEvent } from 'vs/workbench/services/authentication/browser/authenticationService';
import { URI, UriComponents } from 'vs/base/common/uri';
import { IOpenerService } from 'vs/platform/opener/common/opener';
interface AuthenticationForceNewSessionOptions {
detail?: string;
learnMore?: UriComponents;
sessionToRecreate?: AuthenticationSession;
}
interface AuthenticationGetSessionOptions {
clearSessionPreference?: boolean;
createIfNone?: boolean;
forceNewSession?: boolean | AuthenticationForceNewSessionOptions;
silent?: boolean;
}
export class MainThreadAuthenticationProvider extends Disposable implements IAuthenticationProvider {
@@ -58,11 +72,14 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
constructor(
extHostContext: IExtHostContext,
@IAuthenticationService private readonly authenticationService: IAuthenticationService,
@IAuthenticationExtensionsService private readonly authenticationExtensionsService: IAuthenticationExtensionsService,
@IAuthenticationAccessService private readonly authenticationAccessService: IAuthenticationAccessService,
@IAuthenticationUsageService private readonly authenticationUsageService: IAuthenticationUsageService,
@IDialogService private readonly dialogService: IDialogService,
@IStorageService private readonly storageService: IStorageService,
@INotificationService private readonly notificationService: INotificationService,
@IExtensionService private readonly extensionService: IExtensionService,
@ITelemetryService private readonly telemetryService: ITelemetryService
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IOpenerService private readonly openerService: IOpenerService
) {
super();
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostAuthentication);
@@ -100,23 +117,43 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
$removeSession(providerId: string, sessionId: string): Promise<void> {
return this.authenticationService.removeSession(providerId, sessionId);
}
private async loginPrompt(providerName: string, extensionName: string, recreatingSession: boolean, detail?: string): Promise<boolean> {
private async loginPrompt(providerName: string, extensionName: string, recreatingSession: boolean, options?: AuthenticationForceNewSessionOptions): Promise<boolean> {
const message = recreatingSession
? nls.localize('confirmRelogin', "The extension '{0}' wants you to sign in again using {1}.", extensionName, providerName)
: nls.localize('confirmLogin', "The extension '{0}' wants to sign in using {1}.", extensionName, providerName);
const { confirmed } = await this.dialogService.confirm({
const buttons: IPromptButton<boolean | undefined>[] = [
{
label: nls.localize({ key: 'allow', comment: ['&& denotes a mnemonic'] }, "&&Allow"),
run() {
return true;
},
}
];
if (options?.learnMore) {
buttons.push({
label: nls.localize('learnMore', "Learn more"),
run: async () => {
const result = this.loginPrompt(providerName, extensionName, recreatingSession, options);
await this.openerService.open(URI.revive(options.learnMore!), { allowCommands: true });
return await result;
}
});
}
const { result } = await this.dialogService.prompt({
type: Severity.Info,
message,
detail,
primaryButton: nls.localize({ key: 'allow', comment: ['&& denotes a mnemonic'] }, "&&Allow")
buttons,
detail: options?.detail,
cancelButton: true,
});
return confirmed;
return result ?? false;
}
private async doGetSession(providerId: string, scopes: string[], extensionId: string, extensionName: string, options: AuthenticationGetSessionOptions): Promise<AuthenticationSession | undefined> {
const sessions = await this.authenticationService.getSessions(providerId, scopes, true);
const supportsMultipleAccounts = this.authenticationService.supportsMultipleAccounts(providerId);
const provider = this.authenticationService.getProvider(providerId);
// Error cases
if (options.forceNewSession && options.createIfNone) {
@@ -131,22 +168,22 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
// Check if the sessions we have are valid
if (!options.forceNewSession && sessions.length) {
if (supportsMultipleAccounts) {
if (provider.supportsMultipleAccounts) {
if (options.clearSessionPreference) {
// Clearing the session preference is usually paired with createIfNone, so just remove the preference and
// defer to the rest of the logic in this function to choose the session.
this.authenticationService.removeSessionPreference(providerId, extensionId, scopes);
this.authenticationExtensionsService.removeSessionPreference(providerId, extensionId, scopes);
} else {
// If we have an existing session preference, use that. If not, we'll return any valid session at the end of this function.
const existingSessionPreference = this.authenticationService.getSessionPreference(providerId, extensionId, scopes);
const existingSessionPreference = this.authenticationExtensionsService.getSessionPreference(providerId, extensionId, scopes);
if (existingSessionPreference) {
const matchingSession = sessions.find(session => session.id === existingSessionPreference);
if (matchingSession && this.authenticationService.isAccessAllowed(providerId, matchingSession.account.label, extensionId)) {
if (matchingSession && this.authenticationAccessService.isAccessAllowed(providerId, matchingSession.account.label, extensionId)) {
return matchingSession;
}
}
}
} else if (this.authenticationService.isAccessAllowed(providerId, sessions[0].account.label, extensionId)) {
} else if (this.authenticationAccessService.isAccessAllowed(providerId, sessions[0].account.label, extensionId)) {
return sessions[0];
}
}
@@ -154,51 +191,44 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
// We may need to prompt because we don't have a valid session
// modal flows
if (options.createIfNone || options.forceNewSession) {
const providerName = this.authenticationService.getLabel(providerId);
const detail = (typeof options.forceNewSession === 'object') ? options.forceNewSession.detail : undefined;
let uiOptions: AuthenticationForceNewSessionOptions | undefined;
if (typeof options.forceNewSession === 'object') {
uiOptions = options.forceNewSession;
}
// We only want to show the "recreating session" prompt if we are using forceNewSession & there are sessions
// that we will be "forcing through".
const recreatingSession = !!(options.forceNewSession && sessions.length);
const isAllowed = await this.loginPrompt(providerName, extensionName, recreatingSession, detail);
const isAllowed = await this.loginPrompt(provider.label, extensionName, recreatingSession, uiOptions);
if (!isAllowed) {
throw new Error('User did not consent to login.');
}
let session;
if (sessions?.length && !options.forceNewSession) {
session = supportsMultipleAccounts
? await this.authenticationService.selectSession(providerId, extensionId, extensionName, scopes, sessions)
session = provider.supportsMultipleAccounts
? await this.authenticationExtensionsService.selectSession(providerId, extensionId, extensionName, scopes, sessions)
: sessions[0];
} else {
let sessionToRecreate: AuthenticationSession | undefined;
if (typeof options.forceNewSession === 'object' && options.forceNewSession.sessionToRecreate) {
sessionToRecreate = options.forceNewSession.sessionToRecreate as AuthenticationSession;
} else {
const sessionIdToRecreate = this.authenticationService.getSessionPreference(providerId, extensionId, scopes);
const sessionIdToRecreate = this.authenticationExtensionsService.getSessionPreference(providerId, extensionId, scopes);
sessionToRecreate = sessionIdToRecreate ? sessions.find(session => session.id === sessionIdToRecreate) : undefined;
}
session = await this.authenticationService.createSession(providerId, scopes, { activateImmediate: true, sessionToRecreate });
}
this.authenticationService.updateAllowedExtension(providerId, session.account.label, extensionId, extensionName, true);
this.authenticationService.updateSessionPreference(providerId, extensionId, session);
this.authenticationAccessService.updateAllowedExtensions(providerId, session.account.label, [{ id: extensionId, name: extensionName, allowed: true }]);
this.authenticationExtensionsService.updateSessionPreference(providerId, extensionId, session);
return session;
}
// For the silent flows, if we have a session, even though it may not be the user's preference, we'll return it anyway because it might be for a specific
// set of scopes.
const validSession = sessions.find(session => this.authenticationService.isAccessAllowed(providerId, session.account.label, extensionId));
const validSession = sessions.find(session => this.authenticationAccessService.isAccessAllowed(providerId, session.account.label, extensionId));
if (validSession) {
// Migration. If we have a valid session, but no preference, we'll set the preference to the valid session.
// TODO: Remove this after in a few releases.
if (!this.authenticationService.getSessionPreference(providerId, extensionId, scopes)) {
if (this.storageService.get(`${extensionName}-${providerId}`, StorageScope.APPLICATION)) {
this.storageService.remove(`${extensionName}-${providerId}`, StorageScope.APPLICATION);
}
this.authenticationService.updateAllowedExtension(providerId, validSession.account.label, extensionId, extensionName, true);
this.authenticationService.updateSessionPreference(providerId, extensionId, validSession);
}
return validSession;
}
@@ -207,8 +237,8 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
// If there is a potential session, but the extension doesn't have access to it, use the "grant access" flow,
// otherwise request a new one.
sessions.length
? this.authenticationService.requestSessionAccess(providerId, extensionId, extensionName, scopes, sessions)
: await this.authenticationService.requestNewSession(providerId, scopes, extensionId, extensionName);
? this.authenticationExtensionsService.requestSessionAccess(providerId, extensionId, extensionName, scopes, sessions)
: await this.authenticationExtensionsService.requestNewSession(providerId, scopes, extensionId, extensionName);
}
return undefined;
}
@@ -218,7 +248,7 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
if (session) {
this.sendProviderUsageTelemetry(extensionId, providerId);
addAccountUsage(this.storageService, providerId, session.account.label, extensionId, extensionName);
this.authenticationUsageService.addAccountUsage(providerId, session.account.label, extensionId, extensionName);
}
return session;
@@ -226,11 +256,11 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
async $getSessions(providerId: string, scopes: readonly string[], extensionId: string, extensionName: string): Promise<AuthenticationSession[]> {
const sessions = await this.authenticationService.getSessions(providerId, [...scopes], true);
const accessibleSessions = sessions.filter(s => this.authenticationService.isAccessAllowed(providerId, s.account.label, extensionId));
const accessibleSessions = sessions.filter(s => this.authenticationAccessService.isAccessAllowed(providerId, s.account.label, extensionId));
if (accessibleSessions.length) {
this.sendProviderUsageTelemetry(extensionId, providerId);
for (const session of accessibleSessions) {
addAccountUsage(this.storageService, providerId, session.account.label, extensionId, extensionName);
this.authenticationUsageService.addAccountUsage(providerId, session.account.label, extensionId, extensionName);
}
}
return accessibleSessions;
+1 -16
View File
@@ -9,7 +9,7 @@ import { URI, UriComponents } from 'vs/base/common/uri';
import { ExtHostChatShape, ExtHostContext, MainContext, MainThreadChatShape } from 'vs/workbench/api/common/extHost.protocol';
import { IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat';
import { IChatContributionService } from 'vs/workbench/contrib/chat/common/chatContributionService';
import { IChatDynamicRequest, IChatService } from 'vs/workbench/contrib/chat/common/chatService';
import { IChatService } from 'vs/workbench/contrib/chat/common/chatService';
import { IExtHostContext, extHostNamedCustomer } from 'vs/workbench/services/extensions/common/extHostCustomers';
@extHostNamedCustomer(MainContext.MainThreadChat)
@@ -55,18 +55,10 @@ export class MainThreadChat extends Disposable implements MainThreadChatShape {
return undefined;
}
const responderAvatarIconUri = session.responderAvatarIconUri &&
URI.revive(session.responderAvatarIconUri);
const emitter = new Emitter<any>();
this._stateEmitters.set(session.id, emitter);
return {
id: session.id,
requesterUsername: session.requesterUsername,
requesterAvatarIconUri: URI.revive(session.requesterAvatarIconUri),
responderUsername: session.responderUsername,
responderAvatarIconUri,
inputPlaceholder: session.inputPlaceholder,
dispose: () => {
emitter.dispose();
this._stateEmitters.delete(session.id);
@@ -83,13 +75,6 @@ export class MainThreadChat extends Disposable implements MainThreadChatShape {
this._stateEmitters.get(sessionId)?.fire(state);
}
async $sendRequestToProvider(providerId: string, message: IChatDynamicRequest): Promise<void> {
const widget = await this._chatWidgetService.revealViewForProvider(providerId);
if (widget && widget.viewModel) {
this._chatService.sendRequestToProvider(widget.viewModel.sessionId, message);
}
}
async $unregisterChatProvider(handle: number): Promise<void> {
this._providerRegistrations.deleteAndDispose(handle);
}
@@ -19,8 +19,8 @@ import { ExtHostChatAgentsShape2, ExtHostContext, IChatProgressDto, IExtensionCh
import { IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat';
import { ChatInputPart } from 'vs/workbench/contrib/chat/browser/chatInputPart';
import { AddDynamicVariableAction, IAddDynamicVariableContext } from 'vs/workbench/contrib/chat/browser/contrib/chatDynamicVariables';
import { IChatAgentCommand, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
import { IChatModel } from 'vs/workbench/contrib/chat/common/chatModel';
import { IChatAgentImplementation, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
import { IChatContributionService } from 'vs/workbench/contrib/chat/common/chatContributionService';
import { ChatRequestAgentPart } from 'vs/workbench/contrib/chat/common/chatParserTypes';
import { ChatRequestParser } from 'vs/workbench/contrib/chat/common/chatRequestParser';
import { IChatFollowup, IChatProgress, IChatService } from 'vs/workbench/contrib/chat/common/chatService';
@@ -29,7 +29,6 @@ import { IExtHostContext, extHostNamedCustomer } from 'vs/workbench/services/ext
type AgentData = {
dispose: () => void;
name: string;
hasSlashCommands?: boolean;
hasFollowups?: boolean;
};
@@ -49,6 +48,7 @@ export class MainThreadChatAgents2 extends Disposable implements MainThreadChatA
@ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService,
@IChatWidgetService private readonly _chatWidgetService: IChatWidgetService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@IChatContributionService private readonly _chatContributionService: IChatContributionService,
) {
super();
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostChatAgents2);
@@ -76,12 +76,13 @@ export class MainThreadChatAgents2 extends Disposable implements MainThreadChatA
this._agents.deleteAndDispose(handle);
}
$registerAgent(handle: number, extension: ExtensionIdentifier, name: string, metadata: IExtensionChatAgentMetadata): void {
const lastSlashCommands: WeakMap<IChatModel, IChatAgentCommand[]> = new WeakMap();
const d = this._chatAgentService.registerAgent({
id: name,
extensionId: extension,
metadata: revive(metadata),
$registerAgent(handle: number, extension: ExtensionIdentifier, name: string, metadata: IExtensionChatAgentMetadata, allowDynamic: boolean): void {
const staticAgentRegistration = this._chatContributionService.registeredParticipants.find(p => p.extensionId.value === extension.value && p.name === name);
if (!staticAgentRegistration && !allowDynamic) {
throw new Error(`chatParticipant must be declared in package.json: ${name}`);
}
const impl: IChatAgentImplementation = {
invoke: async (request, progress, history, token) => {
this._pendingProgress.set(request.requestId, progress);
try {
@@ -90,26 +91,12 @@ export class MainThreadChatAgents2 extends Disposable implements MainThreadChatA
this._pendingProgress.delete(request.requestId);
}
},
provideFollowups: async (request, result, token): Promise<IChatFollowup[]> => {
provideFollowups: async (request, result, history, token): Promise<IChatFollowup[]> => {
if (!this._agents.get(handle)?.hasFollowups) {
return [];
}
return this._proxy.$provideFollowups(request, handle, result, token);
},
getLastSlashCommands: (model: IChatModel) => {
return lastSlashCommands.get(model);
},
provideSlashCommands: async (model, history, token) => {
if (!this._agents.get(handle)?.hasSlashCommands) {
return []; // save an IPC call
}
const commands = await this._proxy.$provideSlashCommands(handle, { history }, token);
if (model) {
lastSlashCommands.set(model, commands);
}
return commands;
return this._proxy.$provideFollowups(request, handle, result, { history }, token);
},
provideWelcomeMessage: (token: CancellationToken) => {
return this._proxy.$provideWelcomeMessage(handle, token);
@@ -117,11 +104,25 @@ export class MainThreadChatAgents2 extends Disposable implements MainThreadChatA
provideSampleQuestions: (token: CancellationToken) => {
return this._proxy.$provideSampleQuestions(handle, token);
}
});
};
let disposable: IDisposable;
if (!staticAgentRegistration && allowDynamic) {
disposable = this._chatAgentService.registerDynamicAgent(
{
id: name,
extensionId: extension,
metadata: revive(metadata),
slashCommands: [],
},
impl);
} else {
disposable = this._chatAgentService.registerAgent(name, impl);
}
this._agents.set(handle, {
name,
dispose: d.dispose,
hasSlashCommands: metadata.hasSlashCommands,
dispose: disposable.dispose,
hasFollowups: metadata.hasFollowups
});
}
@@ -131,7 +132,6 @@ export class MainThreadChatAgents2 extends Disposable implements MainThreadChatA
if (!data) {
throw new Error(`No agent with handle ${handle} registered`);
}
data.hasSlashCommands = metadataUpdate.hasSlashCommands;
data.hasFollowups = metadataUpdate.hasFollowups;
this._chatAgentService.updateAgent(data.name, revive(metadataUpdate));
}
@@ -5,7 +5,7 @@
import { DisposableMap, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { URI as uri, UriComponents } from 'vs/base/common/uri';
import { IDebugService, IConfig, IDebugConfigurationProvider, IBreakpoint, IFunctionBreakpoint, IBreakpointData, IDebugAdapter, IDebugAdapterDescriptorFactory, IDebugSession, IDebugAdapterFactory, IDataBreakpoint, IDebugSessionOptions, IInstructionBreakpoint, DebugConfigurationProviderTriggerKind, IDebugVisualization } from 'vs/workbench/contrib/debug/common/debug';
import { IDebugService, IConfig, IDebugConfigurationProvider, IBreakpoint, IFunctionBreakpoint, IBreakpointData, IDebugAdapter, IDebugAdapterDescriptorFactory, IDebugSession, IDebugAdapterFactory, IDataBreakpoint, IDebugSessionOptions, IInstructionBreakpoint, DebugConfigurationProviderTriggerKind, IDebugVisualization, DataBreakpointSetType } from 'vs/workbench/contrib/debug/common/debug';
import {
ExtHostContext, ExtHostDebugServiceShape, MainThreadDebugServiceShape, DebugSessionUUID, MainContext,
IBreakpointsDeltaDto, ISourceMultiBreakpointDto, ISourceBreakpointDto, IFunctionBreakpointDto, IDebugSessionDto, IDataBreakpointDto, IStartDebuggingOptions, IDebugConfiguration, IThreadFocusDto, IStackFrameFocusDto
@@ -225,7 +225,14 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
} else if (dto.type === 'function') {
this.debugService.addFunctionBreakpoint(dto.functionName, dto.id, dto.mode);
} else if (dto.type === 'data') {
this.debugService.addDataBreakpoint(dto.label, dto.dataId, dto.canPersist, dto.accessTypes, dto.accessType, dto.mode);
this.debugService.addDataBreakpoint({
description: dto.label,
src: { type: DataBreakpointSetType.Variable, dataId: dto.dataId },
canPersist: dto.canPersist,
accessTypes: dto.accessTypes,
accessType: dto.accessType,
mode: dto.mode
});
}
}
return Promise.resolve();
@@ -436,19 +443,20 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
logMessage: fbp.logMessage,
functionName: fbp.name
};
} else if ('dataId' in bp) {
} else if ('src' in bp) {
const dbp = <IDataBreakpoint>bp;
return <IDataBreakpointDto>{
return {
type: 'data',
id: dbp.getId(),
dataId: dbp.dataId,
dataId: dbp.src.type === DataBreakpointSetType.Variable ? dbp.src.dataId : dbp.src.address,
enabled: dbp.enabled,
condition: dbp.condition,
hitCondition: dbp.hitCondition,
logMessage: dbp.logMessage,
accessType: dbp.accessType,
label: dbp.description,
canPersist: dbp.canPersist
};
} satisfies IDataBreakpointDto;
} else {
const sbp = <IBreakpoint>bp;
return <ISourceBreakpointDto>{
@@ -601,9 +601,9 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread
await this._proxy.$handleInlineCompletionDidShow(handle, completions.pid, item.idx, updatedInsertText);
}
},
handlePartialAccept: async (completions, item, acceptedCharacters): Promise<void> => {
handlePartialAccept: async (completions, item, acceptedCharacters, info: languages.PartialAcceptInfo): Promise<void> => {
if (supportsHandleEvents) {
await this._proxy.$handleInlineCompletionPartialAccept(handle, completions.pid, item.idx, acceptedCharacters);
await this._proxy.$handleInlineCompletionPartialAccept(handle, completions.pid, item.idx, acceptedCharacters, info);
}
},
freeInlineCompletions: (completions: IdentifiableInlineCompletions): void => {
@@ -14,6 +14,7 @@ import { IProgress, Progress } from 'vs/platform/progress/common/progress';
import { Registry } from 'vs/platform/registry/common/platform';
import { ExtHostLanguageModelsShape, ExtHostContext, MainContext, MainThreadLanguageModelsShape } from 'vs/workbench/api/common/extHost.protocol';
import { ILanguageModelChatMetadata, IChatResponseFragment, ILanguageModelsService, IChatMessage } from 'vs/workbench/contrib/chat/common/languageModels';
import { IAuthenticationAccessService } from 'vs/workbench/services/authentication/browser/authenticationAccessService';
import { AuthenticationSession, AuthenticationSessionsChangeEvent, IAuthenticationProvider, IAuthenticationProviderCreateSessionOptions, IAuthenticationService, INTERNAL_AUTH_PROVIDER_PREFIX } from 'vs/workbench/services/authentication/common/authentication';
import { Extensions, IExtensionFeaturesManagementService, IExtensionFeaturesRegistry } from 'vs/workbench/services/extensionManagement/common/extensionFeatures';
import { IExtHostContext, extHostNamedCustomer } from 'vs/workbench/services/extensions/common/extHostCustomers';
@@ -33,6 +34,7 @@ export class MainThreadLanguageModels implements MainThreadLanguageModelsShape {
@IExtensionFeaturesManagementService private readonly _extensionFeaturesManagementService: IExtensionFeaturesManagementService,
@ILogService private readonly _logService: ILogService,
@IAuthenticationService private readonly _authenticationService: IAuthenticationService,
@IAuthenticationAccessService private readonly _authenticationAccessService: IAuthenticationAccessService,
@IExtensionService private readonly _extensionService: IExtensionService
) {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostChatProvider);
@@ -132,28 +134,8 @@ export class MainThreadLanguageModels implements MainThreadLanguageModelsShape {
disposables.add(toDisposable(() => {
this._authenticationService.unregisterAuthenticationProvider(authProviderId);
}));
disposables.add(this._authenticationService.onDidChangeSessions(async (e) => {
if (e.providerId === authProviderId) {
if (e.event.removed?.length) {
const allowedExtensions = this._authenticationService.readAllowedExtensions(authProviderId, accountLabel);
const extensionsToUpdateAccess = [];
for (const allowed of allowedExtensions) {
const from = await this._extensionService.getExtension(allowed.id);
this._authenticationService.updateAllowedExtension(authProviderId, authProviderId, allowed.id, allowed.name, false);
if (from) {
extensionsToUpdateAccess.push({
from: from.identifier,
to: extension,
enabled: false
});
}
}
this._proxy.$updateModelAccesslist(extensionsToUpdateAccess);
}
}
}));
disposables.add(this._authenticationService.onDidChangeExtensionSessionAccess(async (e) => {
const allowedExtensions = this._authenticationService.readAllowedExtensions(authProviderId, accountLabel);
disposables.add(this._authenticationAccessService.onDidChangeExtensionSessionAccess(async (e) => {
const allowedExtensions = this._authenticationAccessService.readAllowedExtensions(authProviderId, accountLabel);
const accessList = [];
for (const allowedExtension of allowedExtensions) {
const from = await this._extensionService.getExtension(allowedExtension.id);
@@ -286,6 +286,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
const authentication: typeof vscode.authentication = {
getSession(providerId: string, scopes: readonly string[], options?: vscode.AuthenticationGetSessionOptions) {
if (typeof options?.forceNewSession === 'object' && options.forceNewSession.learnMore) {
checkProposedApiEnabled(extension, 'authLearnMore');
}
return extHostAuthentication.getSession(extension, providerId, scopes, options as any);
},
getSessions(providerId: string, scopes: readonly string[]) {
@@ -538,7 +541,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
const interalSelector = typeConverters.LanguageSelector.from(selector);
let notebook: vscode.NotebookDocument | undefined;
if (targetsNotebooks(interalSelector)) {
notebook = extHostNotebook.notebookDocuments.find(value => Boolean(value.getCell(document.uri)))?.apiNotebook;
notebook = extHostNotebook.notebookDocuments.find(value => value.apiNotebook.getCells().find(c => c.document === document))?.apiNotebook;
}
return score(interalSelector, document.uri, document.languageId, true, notebook?.uri, notebook?.notebookType);
},
@@ -1385,10 +1388,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
checkProposedApiEnabled(extension, 'interactive');
return extHostChat.registerChatProvider(extension, id, provider);
},
sendInteractiveRequestToProvider(providerId: string, message: vscode.InteractiveSessionDynamicRequest) {
checkProposedApiEnabled(extension, 'interactive');
return extHostChat.sendInteractiveRequestToProvider(providerId, message);
},
transferChatSession(session: vscode.InteractiveSession, toWorkspace: vscode.Uri) {
checkProposedApiEnabled(extension, 'interactive');
return extHostChat.transferChatSession(session, toWorkspace);
@@ -1678,6 +1677,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
ThreadFocus: extHostTypes.ThreadFocus,
RelatedInformationType: extHostTypes.RelatedInformationType,
SpeechToTextStatus: extHostTypes.SpeechToTextStatus,
PartialAcceptTriggerKind: extHostTypes.PartialAcceptTriggerKind,
KeywordRecognitionStatus: extHostTypes.KeywordRecognitionStatus,
ChatResponseMarkdownPart: extHostTypes.ChatResponseMarkdownPart,
ChatResponseFileTreePart: extHostTypes.ChatResponseFileTreePart,
@@ -50,12 +50,12 @@ import * as tasks from 'vs/workbench/api/common/shared/tasks';
import { SaveReason } from 'vs/workbench/common/editor';
import { IRevealOptions, ITreeItem, IViewBadge } from 'vs/workbench/common/views';
import { CallHierarchyItem } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
import { IChatAgentCommand, IChatAgentMetadata, IChatAgentRequest, IChatAgentResult } from 'vs/workbench/contrib/chat/common/chatAgents';
import { IChatAgentMetadata, IChatAgentRequest, IChatAgentResult } from 'vs/workbench/contrib/chat/common/chatAgents';
import { IChatProgressResponseContent } from 'vs/workbench/contrib/chat/common/chatModel';
import { IChatMessage, IChatResponseFragment, ILanguageModelChatMetadata } from 'vs/workbench/contrib/chat/common/languageModels';
import { IChatDynamicRequest, IChatProgress, IChatResponseErrorDetails, IChatUserActionEvent, InteractiveSessionVoteDirection, IChatFollowup } from 'vs/workbench/contrib/chat/common/chatService';
import { IChatFollowup, IChatProgress, IChatResponseErrorDetails, IChatUserActionEvent, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService';
import { IChatRequestVariableValue, IChatVariableData, IChatVariableResolverProgress } from 'vs/workbench/contrib/chat/common/chatVariables';
import { DebugConfigurationProviderTriggerKind, MainThreadDebugVisualization, IAdapterDescriptor, IConfig, IDebugSessionReplMode, IDebugVisualization, IDebugVisualizationContext, IDebugVisualizationTreeItem } from 'vs/workbench/contrib/debug/common/debug';
import { IChatMessage, IChatResponseFragment, ILanguageModelChatMetadata } from 'vs/workbench/contrib/chat/common/languageModels';
import { DebugConfigurationProviderTriggerKind, IAdapterDescriptor, IConfig, IDebugSessionReplMode, IDebugVisualization, IDebugVisualizationContext, IDebugVisualizationTreeItem, MainThreadDebugVisualization } from 'vs/workbench/contrib/debug/common/debug';
import { IInlineChatBulkEditResponse, IInlineChatEditResponse, IInlineChatFollowup, IInlineChatProgressItem, IInlineChatRequest, IInlineChatSession, InlineChatResponseFeedbackKind } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
import * as notebookCommon from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CellExecutionUpdateType } from 'vs/workbench/contrib/notebook/common/notebookExecutionService';
@@ -1126,6 +1126,8 @@ export interface VariablesResult {
name: string;
value: string;
type?: string;
language?: string;
expression?: string;
hasNamedChildren: boolean;
indexedChildrenCount: number;
extensionId: string;
@@ -1196,12 +1198,11 @@ export interface ExtHostLanguageModelsShape {
}
export interface IExtensionChatAgentMetadata extends Dto<IChatAgentMetadata> {
hasSlashCommands?: boolean;
hasFollowups?: boolean;
}
export interface MainThreadChatAgentsShape2 extends IDisposable {
$registerAgent(handle: number, extension: ExtensionIdentifier, name: string, metadata: IExtensionChatAgentMetadata): void;
$registerAgent(handle: number, extension: ExtensionIdentifier, name: string, metadata: IExtensionChatAgentMetadata, allowDynamic: boolean): void;
$registerAgentCompletionsProvider(handle: number, triggerCharacters: string[]): void;
$unregisterAgentCompletionsProvider(handle: number): void;
$updateAgent(handle: number, metadataUpdate: IExtensionChatAgentMetadata): void;
@@ -1228,8 +1229,7 @@ export type IChatAgentHistoryEntryDto = {
export interface ExtHostChatAgentsShape2 {
$invokeAgent(handle: number, request: IChatAgentRequest, context: { history: IChatAgentHistoryEntryDto[] }, token: CancellationToken): Promise<IChatAgentResult | undefined>;
$provideSlashCommands(handle: number, context: { history: IChatAgentHistoryEntryDto[] }, token: CancellationToken): Promise<IChatAgentCommand[]>;
$provideFollowups(request: IChatAgentRequest, handle: number, result: IChatAgentResult, token: CancellationToken): Promise<IChatFollowup[]>;
$provideFollowups(request: IChatAgentRequest, handle: number, result: IChatAgentResult, context: { history: IChatAgentHistoryEntryDto[] }, token: CancellationToken): Promise<IChatFollowup[]>;
$acceptFeedback(handle: number, result: IChatAgentResult, vote: InteractiveSessionVoteDirection, reportIssue?: boolean): void;
$acceptAction(handle: number, result: IChatAgentResult, action: IChatUserActionEvent): void;
$invokeCompletionProvider(handle: number, query: string, token: CancellationToken): Promise<IChatAgentCompletionItem[]>;
@@ -1277,11 +1277,6 @@ export interface MainThreadUrlsShape extends IDisposable {
export interface IChatDto {
id: number;
requesterUsername: string;
requesterAvatarIconUri?: UriComponents;
responderUsername: string;
responderAvatarIconUri?: UriComponents;
inputPlaceholder?: string;
}
export interface IChatRequestDto {
@@ -1315,7 +1310,6 @@ export type IChatProgressDto =
export interface MainThreadChatShape extends IDisposable {
$registerChatProvider(handle: number, id: string): Promise<void>;
$acceptChatState(sessionId: number, state: any): Promise<void>;
$sendRequestToProvider(providerId: string, message: IChatDynamicRequest): void;
$unregisterChatProvider(handle: number): Promise<void>;
$transferChatSession(sessionId: number, toWorkspace: UriComponents): void;
}
@@ -2129,7 +2123,7 @@ export interface ExtHostLanguageFeaturesShape {
$releaseCompletionItems(handle: number, id: number): void;
$provideInlineCompletions(handle: number, resource: UriComponents, position: IPosition, context: languages.InlineCompletionContext, token: CancellationToken): Promise<IdentifiableInlineCompletions | undefined>;
$handleInlineCompletionDidShow(handle: number, pid: number, idx: number, updatedInsertText: string): void;
$handleInlineCompletionPartialAccept(handle: number, pid: number, idx: number, acceptedCharacters: number): void;
$handleInlineCompletionPartialAccept(handle: number, pid: number, idx: number, acceptedCharacters: number, info: languages.PartialAcceptInfo): void;
$freeInlineCompletionsList(handle: number, pid: number): void;
$provideSignatureHelp(handle: number, resource: UriComponents, position: IPosition, context: languages.SignatureHelpContext, token: CancellationToken): Promise<ISignatureHelpDto | undefined>;
$releaseSignatureHelp(handle: number, id: number): void;
@@ -58,10 +58,6 @@ export class ExtHostChat implements ExtHostChatShape {
this._proxy.$transferChatSession(sessionId, newWorkspace);
}
sendInteractiveRequestToProvider(providerId: string, message: vscode.InteractiveSessionDynamicRequest): void {
this._proxy.$sendRequestToProvider(providerId, message);
}
async $prepareChat(handle: number, token: CancellationToken): Promise<IChatDto | undefined> {
const entry = this._chatProvider.get(handle);
if (!entry) {
@@ -78,11 +74,6 @@ export class ExtHostChat implements ExtHostChatShape {
return {
id,
requesterUsername: session.requester?.name,
requesterAvatarIconUri: session.requester?.icon,
responderUsername: session.responder?.name,
responderAvatarIconUri: session.responder?.icon,
inputPlaceholder: session.inputPlaceholder,
};
}
@@ -21,7 +21,7 @@ import { ExtHostChatAgentsShape2, IChatAgentCompletionItem, IChatAgentHistoryEnt
import { CommandsConverter, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters';
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
import { IChatAgentCommand, IChatAgentRequest, IChatAgentResult } from 'vs/workbench/contrib/chat/common/chatAgents';
import { IChatAgentRequest, IChatAgentResult } from 'vs/workbench/contrib/chat/common/chatAgents';
import { IChatFollowup, IChatProgress, IChatUserActionEvent, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService';
import { checkProposedApiEnabled, isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
import { Dto } from 'vs/workbench/services/extensions/common/proxyIdentifier';
@@ -171,7 +171,7 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 {
const agent = new ExtHostChatAgent(extension, name, this._proxy, handle, handler);
this._agents.set(handle, agent);
this._proxy.$registerAgent(handle, extension.identifier, name, {});
this._proxy.$registerAgent(handle, extension.identifier, name, {}, isProposedApiEnabled(extension, 'chatParticipantAdditions'));
return agent.apiAgent;
}
@@ -213,7 +213,7 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 {
} catch (e) {
this._logService.error(e, agent.extension);
return { errorDetails: { message: localize('errorResponse', "Error from provider: {0}", toErrorMessage(e)), responseIsIncomplete: true } };
return { errorDetails: { message: localize('errorResponse', "Error from participant: {0}", toErrorMessage(e)), responseIsIncomplete: true } };
} finally {
stream.close();
@@ -245,38 +245,23 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 {
this._sessionDisposables.deleteAndDispose(sessionId);
}
async $provideSlashCommands(handle: number, context: { history: IChatAgentHistoryEntryDto[] }, token: CancellationToken): Promise<IChatAgentCommand[]> {
const agent = this._agents.get(handle);
if (!agent) {
// this is OK, the agent might have disposed while the request was in flight
return [];
}
const convertedHistory = await this.prepareHistoryTurns(agent.id, context);
try {
return await agent.provideSlashCommands({ history: convertedHistory }, token);
} catch (err) {
const msg = toErrorMessage(err);
this._logService.error(`[${agent.extension.identifier.value}] [@${agent.id}] Error while providing slash commands: ${msg}`);
return [];
}
}
async $provideFollowups(request: IChatAgentRequest, handle: number, result: IChatAgentResult, token: CancellationToken): Promise<IChatFollowup[]> {
async $provideFollowups(request: IChatAgentRequest, handle: number, result: IChatAgentResult, context: { history: IChatAgentHistoryEntryDto[] }, token: CancellationToken): Promise<IChatFollowup[]> {
const agent = this._agents.get(handle);
if (!agent) {
return Promise.resolve([]);
}
const convertedHistory = await this.prepareHistoryTurns(agent.id, context);
const ehResult = typeConvert.ChatAgentResult.to(result);
return (await agent.provideFollowups(ehResult, token))
return (await agent.provideFollowups(ehResult, { history: convertedHistory }, token))
.filter(f => {
// The followup must refer to a participant that exists from the same extension
const isValid = !f.participant || Iterable.some(
this._agents.values(),
a => a.id === f.participant && ExtensionIdentifier.equals(a.extension.identifier, agent.extension.identifier));
if (!isValid) {
this._logService.warn(`[@${agent.id}] ChatFollowup refers to an invalid participant: ${f.participant}`);
this._logService.warn(`[@${agent.id}] ChatFollowup refers to an unknown participant: ${f.participant}`);
}
return isValid;
})
@@ -352,7 +337,6 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 {
class ExtHostChatAgent {
private _commandProvider: vscode.ChatCommandProvider | undefined;
private _followupProvider: vscode.ChatFollowupProvider | undefined;
private _description: string | undefined;
private _fullName: string | undefined;
@@ -369,6 +353,7 @@ class ExtHostChatAgent {
private _agentVariableProvider?: { provider: vscode.ChatParticipantCompletionItemProvider; triggerCharacters: string[] };
private _welcomeMessageProvider?: vscode.ChatWelcomeMessageProvider | undefined;
private _isSticky: boolean | undefined;
private _requester: vscode.ChatRequesterInformation | undefined;
constructor(
public readonly extension: IExtensionDescription,
@@ -394,35 +379,12 @@ class ExtHostChatAgent {
return await this._agentVariableProvider.provider.provideCompletionItems(query, token) ?? [];
}
async provideSlashCommands(context: vscode.ChatContext, token: CancellationToken): Promise<IChatAgentCommand[]> {
if (!this._commandProvider) {
return [];
}
const result = await this._commandProvider.provideCommands(context, token);
if (!result) {
return [];
}
return result
.map(c => {
if ('isSticky2' in c) {
checkProposedApiEnabled(this.extension, 'chatParticipantAdditions');
}
return {
name: c.name,
description: c.description ?? '',
followupPlaceholder: c.isSticky2?.placeholder,
isSticky: c.isSticky2?.isSticky ?? c.isSticky,
sampleRequest: c.sampleRequest
} satisfies IChatAgentCommand;
});
}
async provideFollowups(result: vscode.ChatResult, token: CancellationToken): Promise<vscode.ChatFollowup[]> {
async provideFollowups(result: vscode.ChatResult, context: vscode.ChatContext, token: CancellationToken): Promise<vscode.ChatFollowup[]> {
if (!this._followupProvider) {
return [];
}
const followups = await this._followupProvider.provideFollowups(result, token);
const followups = await this._followupProvider.provideFollowups(result, context, token);
if (!followups) {
return [];
}
@@ -475,7 +437,7 @@ class ExtHostChatAgent {
updateScheduled = true;
queueMicrotask(() => {
this._proxy.$updateAgent(this._handle, {
description: this._description ?? '',
description: this._description,
fullName: this._fullName,
icon: !this._iconPath ? undefined :
this._iconPath instanceof URI ? this._iconPath :
@@ -485,9 +447,7 @@ class ExtHostChatAgent {
'dark' in this._iconPath ? this._iconPath.dark :
undefined,
themeIcon: this._iconPath instanceof extHostTypes.ThemeIcon ? this._iconPath : undefined,
hasSlashCommands: this._commandProvider !== undefined,
hasFollowups: this._followupProvider !== undefined,
isDefault: this._isDefault,
isSecondary: this._isSecondary,
helpTextPrefix: (!this._helpTextPrefix || typeof this._helpTextPrefix === 'string') ? this._helpTextPrefix : typeConvert.MarkdownString.from(this._helpTextPrefix),
helpTextVariablesPrefix: (!this._helpTextVariablesPrefix || typeof this._helpTextVariablesPrefix === 'string') ? this._helpTextVariablesPrefix : typeConvert.MarkdownString.from(this._helpTextVariablesPrefix),
@@ -495,6 +455,7 @@ class ExtHostChatAgent {
sampleRequest: this._sampleRequest,
supportIssueReporting: this._supportIssueReporting,
isSticky: this._isSticky,
requester: this._requester
});
updateScheduled = false;
});
@@ -535,13 +496,6 @@ class ExtHostChatAgent {
assertType(typeof v === 'function', 'Invalid request handler');
that._requestHandler = v;
},
get commandProvider() {
return that._commandProvider;
},
set commandProvider(v) {
that._commandProvider = v;
updateMetadataSoon();
},
get followupProvider() {
return that._followupProvider;
},
@@ -564,10 +518,6 @@ class ExtHostChatAgent {
},
set helpTextPrefix(v) {
checkProposedApiEnabled(that.extension, 'defaultChatParticipant');
if (!that._isDefault) {
throw new Error('helpTextPrefix is only available on the default chat agent');
}
that._helpTextPrefix = v;
updateMetadataSoon();
},
@@ -577,10 +527,6 @@ class ExtHostChatAgent {
},
set helpTextVariablesPrefix(v) {
checkProposedApiEnabled(that.extension, 'defaultChatParticipant');
if (!that._isDefault) {
throw new Error('helpTextVariablesPrefix is only available on the default chat agent');
}
that._helpTextVariablesPrefix = v;
updateMetadataSoon();
},
@@ -590,10 +536,6 @@ class ExtHostChatAgent {
},
set helpTextPostfix(v) {
checkProposedApiEnabled(that.extension, 'defaultChatParticipant');
if (!that._isDefault) {
throw new Error('helpTextPostfix is only available on the default chat agent');
}
that._helpTextPostfix = v;
updateMetadataSoon();
},
@@ -662,9 +604,15 @@ class ExtHostChatAgent {
that._isSticky = v;
updateMetadataSoon();
},
set requester(v) {
that._requester = v;
updateMetadataSoon();
},
get requester() {
return that._requester;
},
dispose() {
disposed = true;
that._commandProvider = undefined;
that._followupProvider = undefined;
that._onDidReceiveFeedback.dispose();
that._proxy.$unregisterAgent(that._handle);
@@ -1230,7 +1230,7 @@ class InlineCompletionAdapterBase {
handleDidShowCompletionItem(pid: number, idx: number, updatedInsertText: string): void { }
handlePartialAccept(pid: number, idx: number, acceptedCharacters: number): void { }
handlePartialAccept(pid: number, idx: number, acceptedCharacters: number, info: languages.PartialAcceptInfo): void { }
}
class InlineCompletionAdapter extends InlineCompletionAdapterBase {
@@ -1345,11 +1345,12 @@ class InlineCompletionAdapter extends InlineCompletionAdapterBase {
}
}
override handlePartialAccept(pid: number, idx: number, acceptedCharacters: number): void {
override handlePartialAccept(pid: number, idx: number, acceptedCharacters: number, info: languages.PartialAcceptInfo): void {
const completionItem = this._references.get(pid)?.items[idx];
if (completionItem) {
if (this._provider.handleDidPartiallyAcceptCompletionItem && this._isAdditionsProposedApiEnabled) {
this._provider.handleDidPartiallyAcceptCompletionItem(completionItem, acceptedCharacters);
this._provider.handleDidPartiallyAcceptCompletionItem(completionItem, typeConvert.PartialAcceptInfo.to(info));
}
}
}
@@ -2489,9 +2490,9 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
}, undefined, undefined);
}
$handleInlineCompletionPartialAccept(handle: number, pid: number, idx: number, acceptedCharacters: number): void {
$handleInlineCompletionPartialAccept(handle: number, pid: number, idx: number, acceptedCharacters: number, info: languages.PartialAcceptInfo): void {
this._withAdapter(handle, InlineCompletionAdapterBase, async adapter => {
adapter.handlePartialAccept(pid, idx, acceptedCharacters);
adapter.handlePartialAccept(pid, idx, acceptedCharacters, info);
}, undefined, undefined);
}
@@ -442,17 +442,7 @@ export class ExtHostNotebookDocument {
return this._cells[index];
}
getCell(cellHandle: number | URI): ExtHostCell | undefined {
if (URI.isUri(cellHandle)) {
const data = notebookCommon.CellUri.parse(cellHandle);
if (!data) {
return undefined;
}
if (data.notebook.toString() !== this.uri.toString()) {
return undefined;
}
cellHandle = data.handle;
}
getCell(cellHandle: number): ExtHostCell | undefined {
return this._cells.find(cell => cell.handle === cellHandle);
}
@@ -475,6 +475,8 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape {
name: result.variable.name,
value: result.variable.value,
type: result.variable.type,
language: result.variable.language,
expression: result.variable.expression,
hasNamedChildren: result.hasNamedChildren,
indexedChildrenCount: result.indexedChildrenCount,
extensionId: obj.extensionId.value,
@@ -2672,6 +2672,29 @@ export namespace TerminalQuickFix {
}
}
export namespace PartialAcceptInfo {
export function to(info: languages.PartialAcceptInfo): types.PartialAcceptInfo {
return {
kind: PartialAcceptTriggerKind.to(info.kind),
};
}
}
export namespace PartialAcceptTriggerKind {
export function to(kind: languages.PartialAcceptTriggerKind): types.PartialAcceptTriggerKind {
switch (kind) {
case languages.PartialAcceptTriggerKind.Word:
return types.PartialAcceptTriggerKind.Word;
case languages.PartialAcceptTriggerKind.Line:
return types.PartialAcceptTriggerKind.Line;
case languages.PartialAcceptTriggerKind.Suggest:
return types.PartialAcceptTriggerKind.Suggest;
default:
return types.PartialAcceptTriggerKind.Unknown;
}
}
}
export namespace DebugTreeItem {
export function from(item: vscode.DebugTreeItem, id: number): IDebugVisualizationTreeItem {
return {
@@ -1795,6 +1795,17 @@ export class InlineSuggestionList implements vscode.InlineCompletionList {
}
}
export interface PartialAcceptInfo {
kind: PartialAcceptTriggerKind;
}
export enum PartialAcceptTriggerKind {
Unknown = 0,
Word = 1,
Line = 2,
Suggest = 3,
}
export enum ViewColumn {
Active = -1,
Beside = -2,
@@ -91,7 +91,7 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase {
const terminalName = args.title || nls.localize('debug.terminal.title', "Debug Process");
const termKey = createKeyForShell(shell, shellArgs, args);
let terminal = await this._integratedTerminalInstances.checkout(termKey, terminalName);
let terminal = await this._integratedTerminalInstances.checkout(termKey, terminalName, true);
let cwdForPrepareCommand: string | undefined;
let giveShellTimeToInitialize = false;
@@ -127,6 +127,10 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase {
// give a new terminal some time to initialize the shell
await new Promise(resolve => setTimeout(resolve, 1000));
} else {
if (terminal.state.isInteractedWith) {
terminal.sendText('\u0003'); // Ctrl+C for #106743. Not part of the same command for #107969
}
if (configProvider.getConfiguration('debug.terminal').get<boolean>('clearBeforeReusing')) {
// clear terminal before reusing it
if (shell.indexOf('powershell') >= 0 || shell.indexOf('pwsh') >= 0 || shell.indexOf('cmd.exe') >= 0) {
@@ -195,7 +199,7 @@ class DebugTerminalCollection {
private _terminalInstances = new Map<vscode.Terminal, { lastUsedAt: number; config: string }>();
public async checkout(config: string, name: string) {
public async checkout(config: string, name: string, cleanupOthersByName = false) {
const entries = [...this._terminalInstances.entries()];
const promises = entries.map(([terminal, termInfo]) => createCancelablePromise(async ct => {
@@ -215,6 +219,9 @@ class DebugTerminalCollection {
}
if (termInfo.config !== config) {
if (cleanupOthersByName) {
terminal.dispose();
}
return null;
}
@@ -19,7 +19,7 @@ import { ExtHostContext, MainContext } from 'vs/workbench/api/common/extHost.pro
import { ExtHostAuthentication } from 'vs/workbench/api/common/extHostAuthentication';
import { IActivityService } from 'vs/workbench/services/activity/common/activity';
import { AuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService';
import { IAuthenticationService } from 'vs/workbench/services/authentication/common/authentication';
import { IAuthenticationExtensionsService, IAuthenticationService } from 'vs/workbench/services/authentication/common/authentication';
import { IExtensionService, nullExtensionDescription as extensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { TestRPCProtocol } from 'vs/workbench/api/test/common/testRPCProtocol';
@@ -28,6 +28,9 @@ import { TestActivityService, TestExtensionService, TestProductService, TestStor
import type { AuthenticationProvider, AuthenticationSession } from 'vscode';
import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService';
import { IProductService } from 'vs/platform/product/common/productService';
import { AuthenticationAccessService, IAuthenticationAccessService } from 'vs/workbench/services/authentication/browser/authenticationAccessService';
import { AuthenticationUsageService, IAuthenticationUsageService } from 'vs/workbench/services/authentication/browser/authenticationUsageService';
import { AuthenticationExtensionsService } from 'vs/workbench/services/authentication/browser/authenticationExtensionsService';
class AuthQuickPick {
private listener: ((e: IQuickPickDidAcceptEvent) => any) | undefined;
@@ -113,9 +116,12 @@ suite('ExtHostAuthentication', () => {
instantiationService.stub(ITelemetryService, NullTelemetryService);
instantiationService.stub(IBrowserWorkbenchEnvironmentService, TestEnvironmentService);
instantiationService.stub(IProductService, TestProductService);
instantiationService.stub(IAuthenticationAccessService, instantiationService.createInstance(AuthenticationAccessService));
instantiationService.stub(IAuthenticationUsageService, instantiationService.createInstance(AuthenticationUsageService));
const rpcProtocol = new TestRPCProtocol();
instantiationService.stub(IAuthenticationService, instantiationService.createInstance(AuthenticationService));
instantiationService.stub(IAuthenticationExtensionsService, instantiationService.createInstance(AuthenticationExtensionsService));
rpcProtocol.set(MainContext.MainThreadAuthentication, instantiationService.createInstance(MainThreadAuthentication, rpcProtocol));
extHostAuthentication = new ExtHostAuthentication(rpcProtocol);
rpcProtocol.set(ExtHostContext.ExtHostAuthentication, extHostAuthentication);
+5 -21
View File
@@ -10,7 +10,7 @@ import { IComposite, ICompositeControl } from 'vs/workbench/common/composite';
import { Event, Emitter } from 'vs/base/common/event';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IConstructorSignature, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { trackFocus, Dimension, IDomPosition, focusWindow } from 'vs/base/browser/dom';
import { trackFocus, Dimension, IDomPosition } from 'vs/base/browser/dom';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { Disposable } from 'vs/base/common/lifecycle';
import { assertIsDefined } from 'vs/base/common/types';
@@ -36,7 +36,7 @@ export abstract class Composite extends Component implements IComposite {
private readonly _onTitleAreaUpdate = this._register(new Emitter<void>());
readonly onTitleAreaUpdate = this._onTitleAreaUpdate.event;
private _onDidFocus: Emitter<void> | undefined;
protected _onDidFocus: Emitter<void> | undefined;
get onDidFocus(): Event<void> {
if (!this._onDidFocus) {
this._onDidFocus = this.registerFocusTrackEvents().onDidFocus;
@@ -45,10 +45,6 @@ export abstract class Composite extends Component implements IComposite {
return this._onDidFocus.event;
}
protected fireOnDidFocus(): void {
this._onDidFocus?.fire();
}
private _onDidBlur: Emitter<void> | undefined;
get onDidBlur(): Event<void> {
if (!this._onDidBlur) {
@@ -86,22 +82,16 @@ export abstract class Composite extends Component implements IComposite {
protected actionRunner: IActionRunner | undefined;
private _telemetryService: ITelemetryService;
protected get telemetryService(): ITelemetryService { return this._telemetryService; }
private visible: boolean;
private visible = false;
private parent: HTMLElement | undefined;
constructor(
id: string,
telemetryService: ITelemetryService,
protected readonly telemetryService: ITelemetryService,
themeService: IThemeService,
storageService: IStorageService
) {
super(id, themeService, storageService);
this._telemetryService = telemetryService;
this.visible = false;
}
getTitle(): string | undefined {
@@ -149,13 +139,7 @@ export abstract class Composite extends Component implements IComposite {
* Called when this composite should receive keyboard focus.
*/
focus(): void {
const container = this.getContainer();
if (container) {
// Make sure to focus the window of the container
// because it is possible that the composite is
// opened in a auxiliary window that is not focused.
focusWindow(container);
}
// Subclasses can implement
}
/**
+5 -5
View File
@@ -59,23 +59,23 @@ export class EditorPaneDescriptor implements IEditorPaneDescriptor {
static readonly onWillInstantiateEditorPane = EditorPaneDescriptor._onWillInstantiateEditorPane.event;
static create<Services extends BrandedService[]>(
ctor: { new(...services: Services): EditorPane },
ctor: { new(group: IEditorGroup, ...services: Services): EditorPane },
typeId: string,
name: string
): EditorPaneDescriptor {
return new EditorPaneDescriptor(ctor as IConstructorSignature<EditorPane>, typeId, name);
return new EditorPaneDescriptor(ctor as IConstructorSignature<EditorPane, [IEditorGroup]>, typeId, name);
}
private constructor(
private readonly ctor: IConstructorSignature<EditorPane>,
private readonly ctor: IConstructorSignature<EditorPane, [IEditorGroup]>,
readonly typeId: string,
readonly name: string
) { }
instantiate(instantiationService: IInstantiationService): EditorPane {
instantiate(instantiationService: IInstantiationService, group: IEditorGroup): EditorPane {
EditorPaneDescriptor._onWillInstantiateEditorPane.fire({ typeId: this.typeId });
const pane = instantiationService.createInstance(this.ctor);
const pane = instantiationService.createInstance(this.ctor, group);
EditorPaneDescriptor.instantiatedEditorPanes.add(this.typeId);
return pane;
+8 -8
View File
@@ -5,7 +5,7 @@
import { Disposable, DisposableMap, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { Event, Emitter } from 'vs/base/common/event';
import { EventType, addDisposableListener, getClientArea, position, size, IDimension, isAncestorUsingFlowTo, computeScreenAwareSize, getActiveDocument, getWindows, getActiveWindow, focusWindow, isActiveDocument, getWindow, getWindowId, getActiveElement } from 'vs/base/browser/dom';
import { EventType, addDisposableListener, getClientArea, position, size, IDimension, isAncestorUsingFlowTo, computeScreenAwareSize, getActiveDocument, getWindows, getActiveWindow, isActiveDocument, getWindow, getWindowId, getActiveElement } from 'vs/base/browser/dom';
import { onDidChangeFullscreen, isFullscreen, isWCOEnabled } from 'vs/base/browser/browser';
import { IWorkingCopyBackupService } from 'vs/workbench/services/workingCopy/common/workingCopyBackup';
import { isWindows, isLinux, isMacintosh, isWeb, isIOS } from 'vs/base/common/platform';
@@ -671,7 +671,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
// Only restore last viewlet if window was reloaded or we are in development mode
let viewContainerToRestore: string | undefined;
if (!this.environmentService.isBuilt || lifecycleService.startupKind === StartupKind.ReloadedWindow || isWeb) {
if (!this.environmentService.isBuilt || lifecycleService.startupKind === StartupKind.ReloadedWindow) {
viewContainerToRestore = this.storageService.get(SidebarPart.activeViewletSettingsKey, StorageScope.WORKSPACE, this.viewDescriptorService.getDefaultViewContainer(ViewContainerLocation.Sidebar)?.id);
} else {
viewContainerToRestore = this.viewDescriptorService.getDefaultViewContainer(ViewContainerLocation.Sidebar)?.id;
@@ -1089,8 +1089,11 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
});
}
registerPart(part: Part): void {
this.parts.set(part.getId(), part);
registerPart(part: Part): IDisposable {
const id = part.getId();
this.parts.set(id, part);
return toDisposable(() => this.parts.delete(id));
}
protected getPart(key: Parts): Part {
@@ -1124,9 +1127,6 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
focusPart(part: SINGLE_WINDOW_PARTS): void;
focusPart(part: Parts, targetWindow: Window = mainWindow): void {
const container = this.getContainer(targetWindow, part) ?? this.mainContainer;
if (container) {
focusWindow(container);
}
switch (part) {
case Parts.EDITOR_PART:
@@ -2611,7 +2611,7 @@ class LayoutStateModel extends Disposable {
LayoutStateKeys.GRID_SIZE.defaultValue = { height: workbenchDimensions.height, width: workbenchDimensions.width };
LayoutStateKeys.SIDEBAR_SIZE.defaultValue = Math.min(300, workbenchDimensions.width / 4);
LayoutStateKeys.AUXILIARYBAR_SIZE.defaultValue = Math.min(300, workbenchDimensions.width / 4);
LayoutStateKeys.PANEL_SIZE.defaultValue = (this.stateCache.get(LayoutStateKeys.PANEL_POSITION.name) ?? LayoutStateKeys.PANEL_POSITION.defaultValue) === 'bottom' ? workbenchDimensions.height / 3 : workbenchDimensions.width / 4;
LayoutStateKeys.PANEL_SIZE.defaultValue = (this.stateCache.get(LayoutStateKeys.PANEL_POSITION.name) ?? LayoutStateKeys.PANEL_POSITION.defaultValue) === Position.BOTTOM ? workbenchDimensions.height / 3 : workbenchDimensions.width / 4;
LayoutStateKeys.SIDEBAR_HIDDEN.defaultValue = this.contextService.getWorkbenchState() === WorkbenchState.EMPTY;
// Apply all defaults
+5 -6
View File
@@ -50,7 +50,7 @@ export abstract class Part extends Component implements ISerializableView {
) {
super(id, themeService, storageService);
layoutService.registerPart(this);
this._register(layoutService.registerPart(this));
}
protected override onThemeChange(theme: IColorTheme): void {
@@ -61,10 +61,6 @@ export abstract class Part extends Component implements ISerializableView {
}
}
override updateStyles(): void {
super.updateStyles();
}
/**
* Note: Clients should not call this method, the workbench calls this
* method. Calling it otherwise may result in unexpected behavior.
@@ -197,7 +193,10 @@ export abstract class MultiWindowParts<T extends IMultiWindowPart> extends Compo
registerPart(part: T): IDisposable {
this._parts.add(part);
return this._register(toDisposable(() => this.unregisterPart(part)));
return this._register(toDisposable(() => {
this.unregisterPart(part);
part = undefined!; // helps to avoid a memory leak with closures where part is captured
}));
}
protected unregisterPart(part: T): void {
@@ -86,7 +86,7 @@ export class BannerPart extends Part implements IBannerService {
}));
// Track focus
const scopedContextKeyService = this.contextKeyService.createScoped(this.element);
const scopedContextKeyService = this._register(this.contextKeyService.createScoped(this.element));
BannerFocused.bindTo(scopedContextKeyService).set(true);
return this.element;
@@ -13,7 +13,7 @@ import { BaseBinaryResourceEditor } from 'vs/workbench/browser/parts/editor/bina
import { IStorageService } from 'vs/platform/storage/common/storage';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfiguration';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
/**
@@ -24,6 +24,7 @@ export class BinaryResourceDiffEditor extends SideBySideEditor {
static override readonly ID = BINARY_DIFF_EDITOR_ID;
constructor(
group: IEditorGroup,
@ITelemetryService telemetryService: ITelemetryService,
@IInstantiationService instantiationService: IInstantiationService,
@IThemeService themeService: IThemeService,
@@ -33,7 +34,7 @@ export class BinaryResourceDiffEditor extends SideBySideEditor {
@IEditorService editorService: IEditorService,
@IEditorGroupsService editorGroupService: IEditorGroupsService
) {
super(telemetryService, instantiationService, themeService, storageService, configurationService, textResourceConfigurationService, editorService, editorGroupService);
super(group, telemetryService, instantiationService, themeService, storageService, configurationService, textResourceConfigurationService, editorService, editorGroupService);
}
getMetadata(): string | undefined {
@@ -13,6 +13,7 @@ import { IStorageService } from 'vs/platform/storage/common/storage';
import { ByteSize } from 'vs/platform/files/common/files';
import { IEditorOptions } from 'vs/platform/editor/common/editor';
import { EditorPlaceholder, IEditorPlaceholderContents } from 'vs/workbench/browser/parts/editor/editorPlaceholder';
import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
export interface IOpenCallbacks {
openInternal: (input: EditorInput, options: IEditorOptions | undefined) => Promise<void>;
@@ -33,12 +34,13 @@ export abstract class BaseBinaryResourceEditor extends EditorPlaceholder {
constructor(
id: string,
group: IEditorGroup,
private readonly callbacks: IOpenCallbacks,
telemetryService: ITelemetryService,
themeService: IThemeService,
@IStorageService storageService: IStorageService
) {
super(id, telemetryService, themeService, storageService);
super(id, group, telemetryService, themeService, storageService);
}
override getTitle(): string {
@@ -80,7 +80,7 @@ export class EditorAutoSave extends Disposable implements IWorkbenchContribution
if (workingCopyResult?.condition === condition) {
if (
workingCopyResult.workingCopy.isDirty() &&
this.filesConfigurationService.getAutoSaveMode(workingCopyResult.workingCopy.resource).mode !== AutoSaveMode.OFF
this.filesConfigurationService.getAutoSaveMode(workingCopyResult.workingCopy.resource, workingCopyResult.reason).mode !== AutoSaveMode.OFF
) {
this.discardAutoSave(workingCopyResult.workingCopy);
@@ -96,7 +96,7 @@ export class EditorAutoSave extends Disposable implements IWorkbenchContribution
editorResult?.condition === condition &&
!editorResult.editor.editor.isDisposed() &&
editorResult.editor.editor.isDirty() &&
this.filesConfigurationService.getAutoSaveMode(editorResult.editor.editor).mode !== AutoSaveMode.OFF
this.filesConfigurationService.getAutoSaveMode(editorResult.editor.editor, editorResult.reason).mode !== AutoSaveMode.OFF
) {
this.waitingOnConditionAutoSaveEditors.delete(resource);
@@ -151,7 +151,7 @@ export class EditorAutoSave extends Disposable implements IWorkbenchContribution
return; // no auto save for non-dirty, readonly or untitled editors
}
const autoSaveMode = this.filesConfigurationService.getAutoSaveMode(editorIdentifier.editor);
const autoSaveMode = this.filesConfigurationService.getAutoSaveMode(editorIdentifier.editor, reason);
if (autoSaveMode.mode !== AutoSaveMode.OFF) {
// Determine if we need to save all. In case of a window focus change we also save if
// auto save mode is configured to be ON_FOCUS_CHANGE (editor focus change)
@@ -198,7 +198,7 @@ export class EditorAutoSave extends Disposable implements IWorkbenchContribution
continue; // we never auto save untitled working copies
}
const autoSaveMode = this.filesConfigurationService.getAutoSaveMode(workingCopy.resource);
const autoSaveMode = this.filesConfigurationService.getAutoSaveMode(workingCopy.resource, reason);
if (autoSaveMode.mode !== AutoSaveMode.OFF) {
workingCopy.save({ reason });
} else if (autoSaveMode.reason === AutoSaveDisabledReason.ERRORS || autoSaveMode.reason === AutoSaveDisabledReason.DISABLED) {
@@ -257,12 +257,13 @@ export class EditorAutoSave extends Disposable implements IWorkbenchContribution
// Save if dirty and unless prevented by other conditions such as error markers
if (workingCopy.isDirty()) {
const autoSaveMode = this.filesConfigurationService.getAutoSaveMode(workingCopy.resource);
const reason = SaveReason.AUTO;
const autoSaveMode = this.filesConfigurationService.getAutoSaveMode(workingCopy.resource, reason);
if (autoSaveMode.mode !== AutoSaveMode.OFF) {
this.logService.trace(`[editor auto save] running auto save`, workingCopy.resource.toString(), workingCopy.typeId);
workingCopy.save({ reason: SaveReason.AUTO });
workingCopy.save({ reason });
} else if (autoSaveMode.reason === AutoSaveDisabledReason.ERRORS || autoSaveMode.reason === AutoSaveDisabledReason.DISABLED) {
this.waitingOnConditionAutoSaveWorkingCopies.set(workingCopy.resource, { workingCopy, reason: SaveReason.AUTO, condition: autoSaveMode.reason });
this.waitingOnConditionAutoSaveWorkingCopies.set(workingCopy.resource, { workingCopy, reason, condition: autoSaveMode.reason });
}
}
}, autoSaveAfterDelay);
@@ -11,7 +11,7 @@ import { EditorInput } from 'vs/workbench/common/editor/editorInput';
import { SideBySideEditorInput } from 'vs/workbench/common/editor/sideBySideEditorInput';
import { Emitter, Relay } from 'vs/base/common/event';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Dimension, trackFocus, addDisposableListener, EventType, EventHelper, findParentWithClass, isAncestor, IDomNodePagePosition, isMouseEvent, isActiveElement, focusWindow, getWindow, getActiveElement } from 'vs/base/browser/dom';
import { Dimension, trackFocus, addDisposableListener, EventType, EventHelper, findParentWithClass, isAncestor, IDomNodePagePosition, isMouseEvent, isActiveElement, getWindow, getActiveElement } from 'vs/base/browser/dom';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar';
@@ -976,9 +976,6 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
focus(): void {
// Ensure window focus
focusWindow(this.element);
// Pass focus to editor panes
if (this.activeEditorPane) {
this.activeEditorPane.focus();
@@ -24,6 +24,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IEditorOptions } from 'vs/platform/editor/common/editor';
import { ITextResourceConfigurationChangeEvent, ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfiguration';
import { IBoundarySashes } from 'vs/base/browser/ui/sash/sash';
import { getWindowById } from 'vs/base/browser/dom';
/**
* The base class of editors in the workbench. Editors register themselves for specific editor inputs.
@@ -70,8 +71,7 @@ export abstract class EditorPane extends Composite implements IEditorPane {
protected _options: IEditorOptions | undefined;
get options(): IEditorOptions | undefined { return this._options; }
private _group: IEditorGroup | undefined;
get group(): IEditorGroup | undefined { return this._group; }
get window() { return getWindowById(this.group.windowId, true).window; }
/**
* Should be overridden by editors that have their own ScopedContextKeyService
@@ -80,6 +80,7 @@ export abstract class EditorPane extends Composite implements IEditorPane {
constructor(
id: string,
readonly group: IEditorGroup,
telemetryService: ITelemetryService,
themeService: IThemeService,
storageService: IStorageService
@@ -145,22 +146,20 @@ export abstract class EditorPane extends Composite implements IEditorPane {
this._options = options;
}
override setVisible(visible: boolean, group?: IEditorGroup): void {
override setVisible(visible: boolean): void {
super.setVisible(visible);
// Propagate to Editor
this.setEditorVisible(visible, group);
this.setEditorVisible(visible);
}
/**
* Indicates that the editor control got visible or hidden in a specific group. A
* editor instance will only ever be visible in one editor group.
* Indicates that the editor control got visible or hidden.
*
* @param visible the state of visibility of this editor
* @param group the editor group this editor is in.
*/
protected setEditorVisible(visible: boolean, group: IEditorGroup | undefined): void {
this._group = group;
protected setEditorVisible(visible: boolean): void {
// Subclasses can implement
}
setBoundarySashes(_sashes: IBoundarySashes) {
@@ -10,7 +10,7 @@ import Severity from 'vs/base/common/severity';
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
import { EditorExtensions, EditorInputCapabilities, IEditorOpenContext, IVisibleEditorPane, createEditorOpenError, isEditorOpenError } from 'vs/workbench/common/editor';
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
import { Dimension, show, hide, IDomNodePagePosition, isAncestor, getWindow, getActiveElement } from 'vs/base/browser/dom';
import { Dimension, show, hide, IDomNodePagePosition, isAncestor, getActiveElement, getWindowById } from 'vs/base/browser/dom';
import { Registry } from 'vs/platform/registry/common/platform';
import { IEditorPaneRegistry, IEditorPaneDescriptor } from 'vs/workbench/browser/editor';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
@@ -131,7 +131,7 @@ export class EditorPanes extends Disposable {
try {
// Assert the `EditorInputCapabilities.AuxWindowUnsupported` condition
if (getWindow(this.editorGroupParent) !== mainWindow && editor.hasCapability(EditorInputCapabilities.AuxWindowUnsupported)) {
if (getWindowById(this.groupView.windowId, true).window !== mainWindow && editor.hasCapability(EditorInputCapabilities.AuxWindowUnsupported)) {
return await this.doShowError(createEditorOpenError(localize('editorUnsupportedInAuxWindow', "This type of editor cannot be opened in other windows yet."), [
toAction({
id: 'workbench.editor.action.closeEditor', label: localize('openFolder', "Close Editor"), run: async () => {
@@ -277,7 +277,7 @@ export class EditorPanes extends Disposable {
if (focus && this.shouldRestoreFocus(activeElement)) {
pane.focus();
} else if (!internalOptions?.preserveWindowOrder) {
this.hostService.moveTop(getWindow(this.editorGroupParent));
this.hostService.moveTop(getWindowById(this.groupView.windowId, true).window);
}
}
@@ -353,7 +353,7 @@ export class EditorPanes extends Disposable {
show(container);
// Indicate to editor that it is now visible
editorPane.setVisible(true, this.groupView);
editorPane.setVisible(true);
// Layout
if (this.pagePosition) {
@@ -393,7 +393,7 @@ export class EditorPanes extends Disposable {
}
// Otherwise instantiate new
const editorPane = this._register(descriptor.instantiate(this.instantiationService));
const editorPane = this._register(descriptor.instantiate(this.instantiationService, this.groupView));
this.editorPanes.push(editorPane);
return editorPane;
@@ -472,7 +472,7 @@ export class EditorPanes extends Disposable {
// the DOM to give a chance to persist certain state that
// might depend on still being the active DOM element.
this.safeRun(() => this._activeEditorPane?.clearInput());
this.safeRun(() => this._activeEditorPane?.setVisible(false, this.groupView));
this.safeRun(() => this._activeEditorPane?.setVisible(false));
// Remove editor pane from parent
const editorPaneContainer = this._activeEditorPane.getContainer();
@@ -492,7 +492,7 @@ export class EditorPanes extends Disposable {
}
setVisible(visible: boolean): void {
this.safeRun(() => this._activeEditorPane?.setVisible(visible, this.groupView));
this.safeRun(() => this._activeEditorPane?.setVisible(visible));
}
layout(pagePosition: IDomNodePagePosition): void {
@@ -29,6 +29,7 @@ import { SimpleIconLabel } from 'vs/base/browser/ui/iconLabel/simpleIconLabel';
import { FileChangeType, FileOperationError, FileOperationResult, IFileService } from 'vs/platform/files/common/files';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
export interface IEditorPlaceholderContents {
icon: string;
@@ -55,11 +56,12 @@ export abstract class EditorPlaceholder extends EditorPane {
constructor(
id: string,
group: IEditorGroup,
@ITelemetryService telemetryService: ITelemetryService,
@IThemeService themeService: IThemeService,
@IStorageService storageService: IStorageService
) {
super(id, telemetryService, themeService, storageService);
super(id, group, telemetryService, themeService, storageService);
}
protected createEditor(parent: HTMLElement): void {
@@ -186,13 +188,14 @@ export class WorkspaceTrustRequiredPlaceholderEditor extends EditorPlaceholder {
static readonly DESCRIPTOR = EditorPaneDescriptor.create(WorkspaceTrustRequiredPlaceholderEditor, WorkspaceTrustRequiredPlaceholderEditor.ID, WorkspaceTrustRequiredPlaceholderEditor.LABEL);
constructor(
group: IEditorGroup,
@ITelemetryService telemetryService: ITelemetryService,
@IThemeService themeService: IThemeService,
@ICommandService private readonly commandService: ICommandService,
@IWorkspaceContextService private readonly workspaceService: IWorkspaceContextService,
@IStorageService storageService: IStorageService
) {
super(WorkspaceTrustRequiredPlaceholderEditor.ID, telemetryService, themeService, storageService);
super(WorkspaceTrustRequiredPlaceholderEditor.ID, group, telemetryService, themeService, storageService);
}
override getTitle(): string {
@@ -223,18 +226,18 @@ export class ErrorPlaceholderEditor extends EditorPlaceholder {
static readonly DESCRIPTOR = EditorPaneDescriptor.create(ErrorPlaceholderEditor, ErrorPlaceholderEditor.ID, ErrorPlaceholderEditor.LABEL);
constructor(
group: IEditorGroup,
@ITelemetryService telemetryService: ITelemetryService,
@IThemeService themeService: IThemeService,
@IStorageService storageService: IStorageService,
@IFileService private readonly fileService: IFileService,
@IDialogService private readonly dialogService: IDialogService
) {
super(ErrorPlaceholderEditor.ID, telemetryService, themeService, storageService);
super(ErrorPlaceholderEditor.ID, group, telemetryService, themeService, storageService);
}
protected async getContents(input: EditorInput, options: IErrorEditorPlaceholderOptions, disposables: DisposableStore): Promise<IEditorPlaceholderContents> {
const resource = input.resource;
const group = this.group;
const error = options.error;
const isFileNotFound = (<FileOperationError | undefined>error)?.fileOperationResult === FileOperationResult.FILE_NOT_FOUND;
@@ -274,20 +277,20 @@ export class ErrorPlaceholderEditor extends EditorPlaceholder {
}
};
});
} else if (group) {
} else {
actions = [
{
label: localize('retry', "Try Again"),
run: () => group.openEditor(input, { ...options, source: EditorOpenSource.USER /* explicit user gesture */ })
run: () => this.group.openEditor(input, { ...options, source: EditorOpenSource.USER /* explicit user gesture */ })
}
];
}
// Auto-reload when file is added
if (group && isFileNotFound && resource && this.fileService.hasProvider(resource)) {
if (isFileNotFound && resource && this.fileService.hasProvider(resource)) {
disposables.add(this.fileService.onDidFilesChange(e => {
if (e.contains(resource, FileChangeType.ADDED, FileChangeType.UPDATED)) {
group.openEditor(input, options);
this.group.openEditor(input, options);
}
}));
}
@@ -366,7 +366,7 @@ class EditorStatus extends Disposable {
}
private registerCommands(): void {
CommandsRegistry.registerCommand({ id: `changeEditorIndentation${this.targetWindowId}`, handler: () => this.showIndentationPicker() });
this._register(CommandsRegistry.registerCommand({ id: `changeEditorIndentation${this.targetWindowId}`, handler: () => this.showIndentationPicker() }));
}
private async showIndentationPicker(): Promise<unknown> {
@@ -31,6 +31,7 @@ export abstract class AbstractEditorWithViewState<T extends object> extends Edit
constructor(
id: string,
group: IEditorGroup,
viewStateStorageKey: string,
@ITelemetryService telemetryService: ITelemetryService,
@IInstantiationService protected readonly instantiationService: IInstantiationService,
@@ -40,17 +41,17 @@ export abstract class AbstractEditorWithViewState<T extends object> extends Edit
@IEditorService protected readonly editorService: IEditorService,
@IEditorGroupsService protected readonly editorGroupService: IEditorGroupsService
) {
super(id, telemetryService, themeService, storageService);
super(id, group, telemetryService, themeService, storageService);
this.viewState = this.getEditorMemento<T>(editorGroupService, textResourceConfigurationService, viewStateStorageKey, 100);
}
protected override setEditorVisible(visible: boolean, group: IEditorGroup | undefined): void {
protected override setEditorVisible(visible: boolean): void {
// Listen to close events to trigger `onWillCloseEditorInGroup`
this.groupListener.value = group?.onWillCloseEditor(e => this.onWillCloseEditor(e));
this.groupListener.value = this.group.onWillCloseEditor(e => this.onWillCloseEditor(e));
super.setEditorVisible(visible, group);
super.setEditorVisible(visible);
}
private onWillCloseEditor(e: IEditorCloseEvent): void {
@@ -110,7 +111,7 @@ export abstract class AbstractEditorWithViewState<T extends object> extends Edit
// - the user configured to not restore view state unless the editor is still opened in the group
if (
(input.isDisposed() && !this.tracksDisposedEditorViewState()) ||
(!this.shouldRestoreEditorViewState(input) && (!this.group || !this.group.contains(input)))
(!this.shouldRestoreEditorViewState(input) && !this.group.contains(input))
) {
this.clearEditorViewState(resource, this.group);
}
@@ -147,10 +148,6 @@ export abstract class AbstractEditorWithViewState<T extends object> extends Edit
}
private saveEditorViewState(resource: URI): void {
if (!this.group) {
return;
}
const editorViewState = this.computeEditorViewState(resource);
if (!editorViewState) {
return;
@@ -160,7 +157,7 @@ export abstract class AbstractEditorWithViewState<T extends object> extends Edit
}
protected loadEditorViewState(input: EditorInput | undefined, context?: IEditorOpenContext): T | undefined {
if (!input || !this.group) {
if (!input) {
return undefined; // we need valid input
}
@@ -36,19 +36,23 @@ export class MultiRowEditorControl extends Disposable implements IEditorTabsCont
this.stickyEditorTabsControl = this._register(this.instantiationService.createInstance(MultiEditorTabsControl, this.parent, editorPartsView, this.groupsView, this.groupView, stickyModel));
this.unstickyEditorTabsControl = this._register(this.instantiationService.createInstance(MultiEditorTabsControl, this.parent, editorPartsView, this.groupsView, this.groupView, unstickyModel));
this.handlePinnedTabsSeparateRowToolbars();
this.handlePinnedTabsLayoutChange();
}
private handlePinnedTabsSeparateRowToolbars(): void {
private handlePinnedTabsLayoutChange(): void {
if (this.groupView.count === 0) {
// Do nothing as no tab bar is visible
return;
}
const hadTwoTabBars = this.parent.classList.contains('two-tab-bars');
const hasTwoTabBars = this.groupView.count !== this.groupView.stickyCount && this.groupView.stickyCount > 0;
// Ensure action toolbar is only visible once
if (this.groupView.count === this.groupView.stickyCount) {
this.parent.classList.toggle('two-tab-bars', false);
} else {
this.parent.classList.toggle('two-tab-bars', true);
this.parent.classList.toggle('two-tab-bars', hasTwoTabBars);
if (hadTwoTabBars !== hasTwoTabBars) {
this.groupView.relayout();
}
}
@@ -85,7 +89,7 @@ export class MultiRowEditorControl extends Disposable implements IEditorTabsCont
}
private handleOpenedEditors(): void {
this.handlePinnedTabsSeparateRowToolbars();
this.handlePinnedTabsLayoutChange();
}
beforeCloseEditor(editor: EditorInput): void {
@@ -111,7 +115,7 @@ export class MultiRowEditorControl extends Disposable implements IEditorTabsCont
}
private handleClosedEditors(): void {
this.handlePinnedTabsSeparateRowToolbars();
this.handlePinnedTabsLayoutChange();
}
moveEditor(editor: EditorInput, fromIndex: number, targetIndex: number, stickyStateChange: boolean): void {
@@ -125,7 +129,7 @@ export class MultiRowEditorControl extends Disposable implements IEditorTabsCont
this.unstickyEditorTabsControl.openEditor(editor);
}
this.handlePinnedTabsSeparateRowToolbars();
this.handlePinnedTabsLayoutChange();
} else {
if (this.model.isSticky(editor)) {
@@ -144,14 +148,14 @@ export class MultiRowEditorControl extends Disposable implements IEditorTabsCont
this.unstickyEditorTabsControl.closeEditor(editor);
this.stickyEditorTabsControl.openEditor(editor);
this.handlePinnedTabsSeparateRowToolbars();
this.handlePinnedTabsLayoutChange();
}
unstickEditor(editor: EditorInput): void {
this.stickyEditorTabsControl.closeEditor(editor);
this.unstickyEditorTabsControl.openEditor(editor);
this.handlePinnedTabsSeparateRowToolbars();
this.handlePinnedTabsLayoutChange();
}
setActive(isActive: boolean): void {
@@ -122,6 +122,7 @@ export class SideBySideEditor extends AbstractEditorWithViewState<ISideBySideEdi
private lastFocusedSide: Side.PRIMARY | Side.SECONDARY | undefined = undefined;
constructor(
group: IEditorGroup,
@ITelemetryService telemetryService: ITelemetryService,
@IInstantiationService instantiationService: IInstantiationService,
@IThemeService themeService: IThemeService,
@@ -131,7 +132,7 @@ export class SideBySideEditor extends AbstractEditorWithViewState<ISideBySideEdi
@IEditorService editorService: IEditorService,
@IEditorGroupsService editorGroupService: IEditorGroupsService
) {
super(SideBySideEditor.ID, SideBySideEditor.VIEW_STATE_PREFERENCE_KEY, telemetryService, instantiationService, storageService, textResourceConfigurationService, themeService, editorService, editorGroupService);
super(SideBySideEditor.ID, group, SideBySideEditor.VIEW_STATE_PREFERENCE_KEY, telemetryService, instantiationService, storageService, textResourceConfigurationService, themeService, editorService, editorGroupService);
this.registerListeners();
}
@@ -350,9 +351,9 @@ export class SideBySideEditor extends AbstractEditorWithViewState<ISideBySideEdi
}
// Create editor pane and make visible
const editorPane = editorPaneDescriptor.instantiate(this.instantiationService);
const editorPane = editorPaneDescriptor.instantiate(this.instantiationService, this.group);
editorPane.create(container);
editorPane.setVisible(this.isVisible(), this.group);
editorPane.setVisible(this.isVisible());
// Track selections if supported
if (isEditorPaneWithSelection(editorPane)) {
@@ -396,13 +397,13 @@ export class SideBySideEditor extends AbstractEditorWithViewState<ISideBySideEdi
this.getLastFocusedEditorPane()?.setOptions(options);
}
protected override setEditorVisible(visible: boolean, group: IEditorGroup | undefined): void {
protected override setEditorVisible(visible: boolean): void {
// Forward to both sides
this.primaryEditorPane?.setVisible(visible, group);
this.secondaryEditorPane?.setVisible(visible, group);
this.primaryEditorPane?.setVisible(visible);
this.secondaryEditorPane?.setVisible(visible);
super.setEditorVisible(visible, group);
super.setEditorVisible(visible);
}
override clearInput(): void {
@@ -17,7 +17,6 @@ import { IEditorViewState, ScrollType } from 'vs/editor/common/editorCommon';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { AbstractTextEditor } from 'vs/workbench/browser/parts/editor/textEditor';
import { Dimension } from 'vs/base/browser/dom';
import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
/**
* A text editor using the code editor widget.
@@ -98,8 +97,8 @@ export abstract class AbstractTextCodeEditor<T extends IEditorViewState> extends
return this.editorControl?.hasTextFocus() || super.hasFocus();
}
protected override setEditorVisible(visible: boolean, group: IEditorGroup | undefined): void {
super.setEditorVisible(visible, group);
protected override setEditorVisible(visible: boolean): void {
super.setEditorVisible(visible);
if (visible) {
this.editorControl?.onVisible();
@@ -58,6 +58,7 @@ export class TextDiffEditor extends AbstractTextEditor<IDiffEditorViewState> imp
}
constructor(
group: IEditorGroup,
@ITelemetryService telemetryService: ITelemetryService,
@IInstantiationService instantiationService: IInstantiationService,
@IStorageService storageService: IStorageService,
@@ -68,7 +69,7 @@ export class TextDiffEditor extends AbstractTextEditor<IDiffEditorViewState> imp
@IFileService fileService: IFileService,
@IPreferencesService private readonly preferencesService: IPreferencesService
) {
super(TextDiffEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, editorService, editorGroupService, fileService);
super(TextDiffEditor.ID, group, telemetryService, instantiationService, storageService, configurationService, themeService, editorService, editorGroupService, fileService);
}
override getTitle(): string {
@@ -171,7 +172,7 @@ export class TextDiffEditor extends AbstractTextEditor<IDiffEditorViewState> imp
}
// Handle case where a file is too large to open without confirmation
if ((<FileOperationError>error).fileOperationResult === FileOperationResult.FILE_TOO_LARGE && this.group) {
if ((<FileOperationError>error).fileOperationResult === FileOperationResult.FILE_TOO_LARGE) {
let message: string;
if (error instanceof TooLargeFileOperationError) {
message = localize('fileTooLargeForHeapErrorWithSize', "At least one file is not displayed in the text compare editor because it is very large ({0}).", ByteSize.formatSize(error.size));
@@ -222,7 +223,7 @@ export class TextDiffEditor extends AbstractTextEditor<IDiffEditorViewState> imp
}
// Replace this editor with the binary one
(this.group ?? this.editorGroupService.activeGroup).replaceEditors([{
this.group.replaceEditors([{
editor: input,
replacement: binaryDiffInput,
options: {
@@ -232,8 +233,8 @@ export class TextDiffEditor extends AbstractTextEditor<IDiffEditorViewState> imp
// and do not control the initial intent that resulted
// in us now opening as binary.
activation: EditorActivation.PRESERVE,
pinned: this.group?.isPinned(input),
sticky: this.group?.isSticky(input)
pinned: this.group.isPinned(input),
sticky: this.group.isSticky(input)
}
}]);
}
@@ -365,8 +366,8 @@ export class TextDiffEditor extends AbstractTextEditor<IDiffEditorViewState> imp
return this.diffEditorControl?.hasTextFocus() || super.hasFocus();
}
protected override setEditorVisible(visible: boolean, group: IEditorGroup | undefined): void {
super.setEditorVisible(visible, group);
protected override setEditorVisible(visible: boolean): void {
super.setEditorVisible(visible);
if (visible) {
this.diffEditorControl?.onVisible();

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